Upload index.html with huggingface_hub
Browse files- index.html +275 -17
index.html
CHANGED
|
@@ -1,19 +1,277 @@
|
|
| 1 |
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 6 |
+
<title>EU Liability Window Lint (PLD 2024/2853) — free online check</title>
|
| 7 |
+
<style>
|
| 8 |
+
:root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
|
| 9 |
+
.wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
|
| 10 |
+
textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
|
| 11 |
+
.bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
|
| 12 |
+
input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
|
| 13 |
+
table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
|
| 14 |
+
.sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
|
| 15 |
+
.note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
|
| 16 |
+
</style>
|
| 17 |
+
<script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
|
| 18 |
+
<!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/pld-liability-window-lint"></head>
|
| 19 |
+
<body>
|
| 20 |
+
<div class="wrap">
|
| 21 |
+
<h1>EU Liability Window Lint (PLD 2024/2853)</h1>
|
| 22 |
+
<p class="lede">Checks CHANGELOG.md and release notes for the four facts the EU Product Liability Directive makes load-bearing on 9 December 2026: release dates, support window, CVE-named security fixes, void AS-IS disclaimers. Runs entirely in your browser — nothing is uploaded.</p>
|
| 23 |
+
<textarea id="src" spellcheck="false"># Changelog
|
| 24 |
+
|
| 25 |
+
All notable changes to Ledgerbridge are recorded here. The project is actively maintained,
|
| 26 |
+
and the software is provided AS IS, without warranty of any kind.
|
| 27 |
+
|
| 28 |
+
## [Latest]
|
| 29 |
+
|
| 30 |
+
- Various fixes and improvements
|
| 31 |
+
- Tightened the importer
|
| 32 |
+
|
| 33 |
+
## 2.4.0
|
| 34 |
+
|
| 35 |
+
- Fixed a security issue in the CSV import path
|
| 36 |
+
- Bug fixes
|
| 37 |
+
|
| 38 |
+
## 2.3.1 - 2024-11-02
|
| 39 |
+
|
| 40 |
+
- Patched an XSS in the report viewer
|
| 41 |
+
- 2.3.0 was yanked
|
| 42 |
+
- Release 3.0.0 is dated 2027-01-10 in the signed manifest
|
| 43 |
+
|
| 44 |
+
## 2.2.0 - 2024-06-18
|
| 45 |
+
|
| 46 |
+
- Liability for this library follows Directive 85/374/EEC, and integrators accept no liability.
|
| 47 |
+
- The new Product Liability Directive applies from 2027-06-01.</textarea>
|
| 48 |
+
<div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div>
|
| 49 |
+
<div id="out" aria-live="polite"></div>
|
| 50 |
+
<div id="paid"><strong>Free here:</strong> Lints the open CHANGELOG.md or release-notes file and names every entry that will not carry a liability date, a support window or a CVE reference on 9 December 2026.. <strong>In the editor:</strong> Lints every changelog and release-notes file in the workspace at once and writes out a dated Markdown + CSV evidence record you keep and file. — that part asks for a licence key.</div>
|
| 51 |
+
<p class="note">Same engine as the VS Code extension, byte for byte.</p>
|
| 52 |
+
</div>
|
| 53 |
+
<script>var PLD_RULES = [
|
| 54 |
+
{"id": "undated_release", "sev": "high", "title": "Release entry carries no YYYY-MM-DD date",
|
| 55 |
+
"why": "Art. 17(2) of Directive (EU) 2024/2853 runs the 10-year longstop from the day that version was placed on the market. An undated heading cannot prove that day."},
|
| 56 |
+
{"id": "void_disclaimer", "sev": "high", "title": "AS-IS / no-liability wording that Art. 15 voids",
|
| 57 |
+
"why": "Art. 15 says liability towards an injured person may not be limited or excluded by a contractual provision. From 9 December 2026 the AS-IS clause stops doing the job it was written for."},
|
| 58 |
+
{"id": "security_fix_without_cve", "sev": "high", "title": "Security fix with no CVE or GHSA identifier",
|
| 59 |
+
"why": "Art. 11(2)(c) keeps a defect in your control when it turns on software updates you did or did not supply. Without an identifier you cannot show which defect a release removed."},
|
| 60 |
+
{"id": "old_directive_citation", "sev": "high", "title": "Citation of the repealed 1985 Directive",
|
| 61 |
+
"why": "Directive 85/374/EEC is repealed by Directive (EU) 2024/2853 with effect from 9 December 2026. A file that still cites it is describing a regime that has ended."},
|
| 62 |
+
{"id": "deadline_drift", "sev": "high", "title": "Product-liability date that is not 2026-12-09",
|
| 63 |
+
"why": "Member States apply Directive (EU) 2024/2853 from 9 December 2026. Any other date on the same line is the single most copied mistake in generated text."},
|
| 64 |
+
{"id": "no_support_window", "sev": "high", "title": "No dated support window anywhere in the file",
|
| 65 |
+
"why": "Annex II(8) of the Cyber Resilience Act (EU) 2024/2847 requires the end date of the support period in the information given to users, and Art. 11(2)(c) PLD reads that window as your evidence."},
|
| 66 |
+
{"id": "no_security_contact", "sev": "med", "title": "No vulnerability-reporting contact",
|
| 67 |
+
"why": "Annex II(2) CRA requires a single point of contact for reporting. Since 11 September 2026 an actively exploited vulnerability must reach ENISA within 24 hours."},
|
| 68 |
+
{"id": "unversioned_heading", "sev": "med", "title": "Release heading names no version",
|
| 69 |
+
"why": "Art. 4(1) treats each software version placed on the market as a product. 'Latest' and 'Current' identify no product."},
|
| 70 |
+
{"id": "stale_support_claim", "sev": "med", "title": "Active-support claim older than the last dated release",
|
| 71 |
+
"why": "A support claim that the release history does not back is the gap Art. 11(2)(c) opens: the update was in your control and it did not arrive."},
|
| 72 |
+
{"id": "yanked_without_reason", "sev": "med", "title": "Version withdrawn with no recorded reason",
|
| 73 |
+
"why": "Art. 8 lets a court order you to disclose the record. A withdrawal with no reason on it leaves the defect unexplained in your own file."},
|
| 74 |
+
{"id": "future_date", "sev": "low", "title": "Release dated after today",
|
| 75 |
+
"why": "A product cannot be placed on the market in the future. The date decides the 10-year window under Art. 17(2), so a typo moves it."},
|
| 76 |
+
{"id": "vague_fix_language", "sev": "low", "title": "'Various fixes' with nothing named",
|
| 77 |
+
"why": "Art. 10(2) lets a court presume defectiveness when ordered evidence is not produced. 'Various fixes' is not evidence of what was fixed."}
|
| 78 |
+
]
|
| 79 |
+
;</script>
|
| 80 |
+
<script>/* EU Liability Window Lint — engine.
|
| 81 |
+
Reads a CHANGELOG / release-notes file as text and reports the entries that
|
| 82 |
+
will not carry a liability date, a support window or a named defect on
|
| 83 |
+
9 December 2026, the day Directive (EU) 2024/2853 starts to apply. */
|
| 84 |
+
|
| 85 |
+
var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.PLD_RULES;
|
| 86 |
+
|
| 87 |
+
var PLD_APPLIES = '2026-12-09';
|
| 88 |
+
|
| 89 |
+
var RE_ISO = /\b(20\d{2})-(\d{2})-(\d{2})\b/;
|
| 90 |
+
var RE_ISO_G = /\b20\d{2}-\d{2}-\d{2}\b/g;
|
| 91 |
+
var RE_HEADING = /^\s{0,3}#{1,4}\s+\S/;
|
| 92 |
+
var RE_VERSION = /\bv?\d+\.\d+(?:\.\d+)?(?:[-+][0-9A-Za-z.\-]+)?\b/;
|
| 93 |
+
var RE_ID = /\b(?:CVE-\d{4}-\d{4,}|GHSA-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4})\b/i;
|
| 94 |
+
var RE_DISCLAIM = /\bas[\s\-]is\b|\bno warrant|\bwithout warrant|\bnot liable\b|\bno liability\b|\bat your own risk\b|\bdisclaims?\b[^.]{0,40}\b(?:liability|warrant)|\blimitation of liability\b|\bin no event shall\b/i;
|
| 95 |
+
var RE_SEC = /\b(?:security fix|security patch|security issue|vulnerabilit(?:y|ies)|exploit(?:ed|able)?|CVSS|RCE|XSS|CSRF|SSRF|SQL injection|buffer overflow|privilege escalation|remote code execution|denial of service|path traversal|auth bypass)\b/i;
|
| 96 |
+
var RE_SEC_BULLET = /^\s*(?:[-*+]|\d+\.)\s+.*\bsecurity\b/i;
|
| 97 |
+
var RE_OLD_DIR = /\b85\/374\/EEC\b|\b1999\/34\/EC\b|\bproduct liability directive\b[^.]{0,20}\b19\d{2}\b|\b1985\b[^.]{0,20}\bproduct liability\b/i;
|
| 98 |
+
var RE_PLD = /\b2024\/2853\b|\bproduct liability directive\b|\bnew pld\b|\bPLD\b/i;
|
| 99 |
+
var RE_SUPPORT = /\bsupported? until\b|\bsupport ends?\b|\bend[\s\-]of[\s\-]support\b|\bsecurity updates? until\b|\bsupport period\b|\bsupported through\b|\bEOL\b/i;
|
| 100 |
+
var RE_CONTACT = /security@|SECURITY\.md|\breport a vulnerabilit|\bvulnerability disclosure\b|\bsecurity policy\b|\bsecurity advisor(?:y|ies)\b|\bcoordinated disclosure\b/i;
|
| 101 |
+
var RE_ACTIVE = /\bactively maintained\b|\bactively supported\b|\bunder active development\b|\bfully supported\b|\bcurrently supported\b/i;
|
| 102 |
+
var RE_YANK = /\byank(?:ed)?\b|\bwithdrawn\b|\brevoked\b|\bretracted\b|\bunpublished\b/i;
|
| 103 |
+
var RE_REASON = /\bbecause\b|\breason\b|\bdue to\b|\bsee \b|\breplaced by\b|\bsuperseded by\b/i;
|
| 104 |
+
var RE_VAGUE = /\b(?:various|misc|miscellaneous|minor|assorted|several|small|general)\s+(?:bug\s?)?(?:fixes|fixups|improvements|changes|updates|tweaks|cleanups)\b|^\s*(?:[-*+]|\d+\.)\s*(?:bug\s?fixes|fixes|improvements|updates|cleanup)\s*\.?\s*$/i;
|
| 105 |
+
var RE_NOVERSION_HEAD = /^\s{0,3}#{1,4}\s*\[?\s*(?:latest|current|newest|recent|new release|this release|release|next)\s*\]?\s*[:\-]?\s*$/i;
|
| 106 |
+
|
| 107 |
+
function daysBetween(a, b) {
|
| 108 |
+
return Math.round((Date.parse(b + 'T00:00:00Z') - Date.parse(a + 'T00:00:00Z')) / 86400000);
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
function ruleById(id) {
|
| 112 |
+
for (var i = 0; i < RULES.length; i++) { if (RULES[i].id === id) return RULES[i]; }
|
| 113 |
+
return {id: id, sev: 'med', title: id, why: ''};
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
function check(text, opts) {
|
| 117 |
+
opts = opts || {};
|
| 118 |
+
var today = opts.today || '2026-09-16';
|
| 119 |
+
var lines = String(text == null ? '' : text).split(/\r?\n/);
|
| 120 |
+
var findings = [];
|
| 121 |
+
var seenSupport = false, seenContact = false;
|
| 122 |
+
var newestRelease = null, activeClaimLine = 0, activeClaimText = '';
|
| 123 |
+
|
| 124 |
+
function push(id, line, msg) {
|
| 125 |
+
var r = ruleById(id);
|
| 126 |
+
findings.push({check: id, sev: r.sev, msg: msg, line: line});
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
for (var i = 0; i < lines.length; i++) {
|
| 130 |
+
var raw = lines[i], line = i + 1, t = raw.trim();
|
| 131 |
+
if (!t) continue;
|
| 132 |
+
var isHeading = RE_HEADING.test(raw);
|
| 133 |
+
var iso = raw.match(RE_ISO);
|
| 134 |
+
var hasVersion = RE_VERSION.test(raw.replace(RE_ISO_G, ''));
|
| 135 |
+
|
| 136 |
+
if (RE_SUPPORT.test(raw) && iso) seenSupport = true;
|
| 137 |
+
if (RE_CONTACT.test(raw)) seenContact = true;
|
| 138 |
+
|
| 139 |
+
/* 1. release heading with a version but no ISO date */
|
| 140 |
+
if (isHeading && hasVersion && !iso && !/\bunreleased\b/i.test(raw)) {
|
| 141 |
+
push('undated_release', line, 'Release "' + t.replace(/^#+\s*/, '').slice(0, 48) +
|
| 142 |
+
'" has no YYYY-MM-DD date. The 10-year liability window in Art. 17(2) of Directive (EU) 2024/2853 runs from the day this version was placed on the market, and this line does not record it.');
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
/* 8. heading that names no version at all */
|
| 146 |
+
if (isHeading && RE_NOVERSION_HEAD.test(raw)) {
|
| 147 |
+
push('unversioned_heading', line, 'Heading "' + t.replace(/^#+\s*/, '') +
|
| 148 |
+
'" names no version. Art. 4(1) makes each version you place on the market its own product, so this entry belongs to nothing you can identify.');
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
/* 2. disclaimer wording that Art. 15 voids */
|
| 152 |
+
if (RE_DISCLAIM.test(raw)) {
|
| 153 |
+
push('void_disclaimer', line, 'Liability-exclusion wording here does not bind an injured person: Art. 15 of Directive (EU) 2024/2853 makes contractual limits void, and from ' +
|
| 154 |
+
PLD_APPLIES + ' that is the law in every Member State.');
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
/* 3. security work with no CVE/GHSA */
|
| 158 |
+
if (!isHeading && (RE_SEC.test(raw) || RE_SEC_BULLET.test(raw)) && !RE_ID.test(raw)) {
|
| 159 |
+
push('security_fix_without_cve', line, 'Security change with no CVE or GHSA identifier. Art. 11(2)(c) keeps a defect in your control when it turns on updates you supplied, and this line names no defect to point at.');
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
/* 4. repealed directive */
|
| 163 |
+
if (RE_OLD_DIR.test(raw)) {
|
| 164 |
+
push('old_directive_citation', line, 'This cites Directive 85/374/EEC, which Directive (EU) 2024/2853 repeals with effect from ' +
|
| 165 |
+
PLD_APPLIES + '. Software was not a product under the old text; it is one under Art. 4(1) of the new one.');
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
/* 5. wrong applicability date next to the PLD */
|
| 169 |
+
if (RE_PLD.test(raw)) {
|
| 170 |
+
var stripped = raw.replace(/2024\/2853/g, ' ').replace(/2024\/2847/g, ' ').replace(/85\/374\/EEC/gi, ' ');
|
| 171 |
+
var years = stripped.match(/\b20\d{2}\b/g) || [];
|
| 172 |
+
var isos = stripped.match(RE_ISO_G) || [];
|
| 173 |
+
var bad = null;
|
| 174 |
+
if (years.length && years.indexOf('2026') === -1) bad = years[0];
|
| 175 |
+
for (var k = 0; k < isos.length; k++) { if (isos[k].slice(0, 4) === '2026' && isos[k] !== PLD_APPLIES) bad = isos[k]; }
|
| 176 |
+
if (bad) {
|
| 177 |
+
push('deadline_drift', line, 'This puts the product-liability change at ' + bad +
|
| 178 |
+
'. Member States apply Directive (EU) 2024/2853 from ' + PLD_APPLIES + '; a reader who trusts this line plans around the wrong quarter.');
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
/* 10. withdrawal with no reason */
|
| 183 |
+
if (RE_YANK.test(raw) && !RE_ID.test(raw) && !RE_REASON.test(raw)) {
|
| 184 |
+
push('yanked_without_reason', line, 'A version is withdrawn here with no reason and no identifier. Under Art. 8 a court may order this record to be disclosed, and Art. 10(2) lets it presume the product was defective when the record does not answer.');
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
/* 12. vague fix language */
|
| 188 |
+
if (!isHeading && RE_VAGUE.test(raw)) {
|
| 189 |
+
push('vague_fix_language', line, '"' + t.replace(/^[-*+\s]+/, '').slice(0, 40) +
|
| 190 |
+
'" names nothing that was fixed. Art. 10(2) allows a presumption of defectiveness when ordered evidence is not produced, and this entry produces none.');
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
/* 11. future-dated release + newest release tracking */
|
| 194 |
+
if (iso) {
|
| 195 |
+
var d = iso[0];
|
| 196 |
+
var isReleaseLine = isHeading || /\brelease[ds]?\b|\bpublish(?:ed)?\b|\bshipped\b/i.test(raw);
|
| 197 |
+
if (isHeading && (!newestRelease || d > newestRelease)) newestRelease = d;
|
| 198 |
+
if (isReleaseLine && d > today) {
|
| 199 |
+
push('future_date', line, 'Dated ' + d + ', which is after today (' + today +
|
| 200 |
+
'). The placing-on-the-market date starts the 10-year window in Art. 17(2), so a typo here moves the window.');
|
| 201 |
+
}
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
if (!activeClaimLine && RE_ACTIVE.test(raw)) { activeClaimLine = line; activeClaimText = t.slice(0, 40); }
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
/* 9. active-support claim the release history does not back */
|
| 208 |
+
if (activeClaimLine && newestRelease) {
|
| 209 |
+
var gap = daysBetween(newestRelease, today);
|
| 210 |
+
if (gap > 365) {
|
| 211 |
+
push('stale_support_claim', activeClaimLine, 'This file claims active support, but the newest dated release is ' +
|
| 212 |
+
newestRelease + ' — ' + gap + ' days ago. Art. 11(2)(c) treats the update you did not ship as being within your control.');
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
/* 6. no dated support window */
|
| 217 |
+
if (!seenSupport) {
|
| 218 |
+
push('no_support_window', 1, 'No dated support window in this file. Annex II(8) of the Cyber Resilience Act (EU) 2024/2847 requires the end date of the support period in the information supplied to users, and that date is also the evidence Art. 11(2)(c) PLD asks for.');
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/* 7. no reporting contact */
|
| 222 |
+
if (!seenContact) {
|
| 223 |
+
push('no_security_contact', 1, 'No vulnerability-reporting contact in this file. Annex II(2) CRA requires a single point of contact, and since 11 September 2026 an actively exploited vulnerability must reach ENISA within 24 hours.');
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
findings.sort(function (a, b) { return a.line - b.line; });
|
| 227 |
+
return {findings: findings};
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
var API = {engine: {check: check}, RULES: RULES, RULE_COUNT: RULES.length};
|
| 231 |
+
if (typeof module !== 'undefined') { module.exports = API; }
|
| 232 |
+
if (typeof window !== 'undefined') { window.PLDENGINE = API; }
|
| 233 |
+
</script>
|
| 234 |
+
<script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
|
| 235 |
+
'use strict';
|
| 236 |
+
function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); }
|
| 237 |
+
function summaryLine(res) {
|
| 238 |
+
const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
|
| 239 |
+
return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
|
| 240 |
+
}
|
| 241 |
+
function toText(res, label) {
|
| 242 |
+
const f = (res && res.findings) || [];
|
| 243 |
+
const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
|
| 244 |
+
f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); });
|
| 245 |
+
return lines.join('\n');
|
| 246 |
+
}
|
| 247 |
+
function toHtml(res) {
|
| 248 |
+
const f = (res && res.findings) || [];
|
| 249 |
+
if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
|
| 250 |
+
const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
|
| 251 |
+
return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
|
| 252 |
+
}
|
| 253 |
+
var PLDREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
|
| 254 |
+
if (typeof module !== 'undefined' && module.exports) module.exports = PLDREPORT;
|
| 255 |
+
else window.PLDREPORT = PLDREPORT;
|
| 256 |
+
</script>
|
| 257 |
+
<script>
|
| 258 |
+
(function(){
|
| 259 |
+
var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
|
| 260 |
+
today.value=new Date().toISOString().slice(0,10);
|
| 261 |
+
function run(){ try { var res=window.PLDENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.PLDREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
|
| 262 |
+
go.onclick=run; if (src.value.trim()) run();
|
| 263 |
+
})();
|
| 264 |
+
</script>
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://open-vsx.org/extension/readystack/pld-liability-window-lint" rel="noopener" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><a href="https://www.npmjs.com/package/@readystack/pld-liability-window-lint" rel="noopener" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://hub.docker.com/r/getreadystack/pld-liability-window-lint" rel="noopener" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">npx @readystack/pld-liability-window-lint <file>
|
| 268 |
+
docker run --rm -v "$PWD:/w" getreadystack/pld-liability-window-lint /w</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://buy.polar.sh/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
|
| 269 |
+
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind EU Liability Window Lint every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"pld-liability-window-lint",lang:"en",source:"rule_alert"})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script></section>
|
| 270 |
+
|
| 271 |
+
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">12 rules read your CHANGELOG.md the way Directive (EU) 2024/2853 will read it from 2026-12-09: undated releases lose the start of the 10-year window in Art. 17(2), and the AS-IS clause is vo</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">One hour of EU product-compliance counsel bills around $300, and one repository's release history takes longer than an hour to read.</p><a href="https://buy.polar.sh/polar_cl_SLbJNzFJbhPz5hbdDSJNmtMORqZFq3eprmVBa2NojFU" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
|
| 272 |
+
<section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does EU Liability Window Lint actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads a CHANGELOG.md or release-notes file and applies 12 rules drawn from Directive (EU) 2024/2853 and the Cyber Resilience Act (EU) 2024/2847. On the 25-line sample changelog shipped with it, it returns 15 findings: undated releases, an AS-IS clause that Art. 15 voids, security fixes with no CVE, and a citation of the repealed Directive 85/374/EEC.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is this for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Maintainers, release engineers and vendors who place software on the EU market and keep a changelog. From 9 December 2026 software is a product under Art. 4(1) of Directive (EU) 2024/2853, so the release history stops being housekeeping and becomes a record a court can order disclosed under Art. 8.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why will a free markdown linter not do this?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Markdown linters and spell-checkers read style. None of them know that Art. 15 voids an AS-IS clause against an injured person, that Directive 85/374/EEC is repealed on 2026-12-09, or that Annex II(8) of the Cyber Resilience Act wants a support-end date. A chatbot will cheerfully hand you the wrong applicability year.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what does the paid version add?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free: the open file, all 12 rules, every finding with its line number and the article behind it. The clean sample returns 0 findings and the dirty one returns 15. Paid, $29 once: every changelog in the workspace in one pass, plus a dated Markdown and CSV evidence record you keep and file.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost if a person did it?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">One hour of EU product-compliance counsel bills around $300, and reading one repository's release history takes longer than an hour. This lint costs $29 once, runs in the editor or in the browser, and re-runs on every release. Seven-day full refund, one licence key per person or team seat.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does EU Liability Window Lint actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads a CHANGELOG.md or release-notes file and applies 12 rules drawn from Directive (EU) 2024/2853 and the Cyber Resilience Act (EU) 2024/2847. On the 25-line sample changelog shipped with it, it returns 15 findings: undated releases, an AS-IS clause that Art. 15 voids, security fixes with no CVE, and a citation of the repealed Directive 85/374/EEC."}}, {"@type": "Question", "name": "Who is this for?", "acceptedAnswer": {"@type": "Answer", "text": "Maintainers, release engineers and vendors who place software on the EU market and keep a changelog. From 9 December 2026 software is a product under Art. 4(1) of Directive (EU) 2024/2853, so the release history stops being housekeeping and becomes a record a court can order disclosed under Art. 8."}}, {"@type": "Question", "name": "Why will a free markdown linter not do this?", "acceptedAnswer": {"@type": "Answer", "text": "Markdown linters and spell-checkers read style. None of them know that Art. 15 voids an AS-IS clause against an injured person, that Directive 85/374/EEC is repealed on 2026-12-09, or that Annex II(8) of the Cyber Resilience Act wants a support-end date. A chatbot will cheerfully hand you the wrong applicability year."}}, {"@type": "Question", "name": "What is free and what does the paid version add?", "acceptedAnswer": {"@type": "Answer", "text": "Free: the open file, all 12 rules, every finding with its line number and the article behind it. The clean sample returns 0 findings and the dirty one returns 15. Paid, $29 once: every changelog in the workspace in one pass, plus a dated Markdown and CSV evidence record you keep and file."}}, {"@type": "Question", "name": "What would this cost if a person did it?", "acceptedAnswer": {"@type": "Answer", "text": "One hour of EU product-compliance counsel bills around $300, and reading one repository's release history takes longer than an hour. This lint costs $29 once, runs in the editor or in the browser, and re-runs on every release. Seven-day full refund, one licence key per person or team seat."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:0,slug:"pld-liability-window-lint",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence, 7-day refund.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/dora-ict-contract-clause-lint"><span class="yj-rel-t">DORA Art. 30 ICT Contract Clause Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/en301549-lint"><span class="yj-rel-t">EN 301 549 Lint — European Accessibility Act rules, with the clause number, in</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/dotnet-eol-support-gate"><span class="yj-rel-t">.NET EOL & CRA Support Window Gate</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/robots-txt-ai-crawler-audit"><span class="yj-rel-t">AI Crawler Rules - robots.txt Audit for AI Search</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/bulk-sender-lint"><span class="yj-rel-t">Bulk Sender Lint - the Gmail, Yahoo and Outlook.com rules, checked in your mai</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/devicetree-config-language-pack"><span class="yj-rel-t">DeviceTree Config Audit: 23 dtc-Style Rules, 50 Snippets</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section><!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div>
|
| 273 |
+
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||'')})}).catch(function(){});}catch(e){}})();</script>
|
| 274 |
+
|
| 275 |
+
<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use'})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>
|
| 276 |
+
</body>
|
| 277 |
</html>
|