vector-mirror 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/package.json +65 -0
- package/src/adapters/emitter/prose.js +689 -0
- package/src/adapters/emitter/structured.js +649 -0
- package/src/adapters/renderer/playwright.js +7345 -0
- package/src/core/arbitrate.js +266 -0
- package/src/core/constraints/_schema.js +89 -0
- package/src/core/constraints/aligned.js +42 -0
- package/src/core/constraints/centered-in.js +29 -0
- package/src/core/constraints/color.js +63 -0
- package/src/core/constraints/distance.js +233 -0
- package/src/core/constraints/fill.js +22 -0
- package/src/core/constraints/inside.js +52 -0
- package/src/core/constraints/loader.js +65 -0
- package/src/core/constraints/no-overlap.js +50 -0
- package/src/core/constraints/positional.js +46 -0
- package/src/core/constraints/registry.js +98 -0
- package/src/core/constraints/same-size.js +35 -0
- package/src/core/diff.js +118 -0
- package/src/core/element_vocabulary.js +241 -0
- package/src/core/grid.js +240 -0
- package/src/core/honesty.js +214 -0
- package/src/core/sanitizer/auto_ids.js +104 -0
- package/src/core/tolerance.js +22 -0
- package/src/core/use_graph.js +541 -0
- package/src/interface/claims.js +439 -0
- package/src/interface/schema.js +626 -0
- package/src/interface/server.js +57 -0
- package/src/interface/tools.js +437 -0
- package/src/lib/bbox.js +17 -0
- package/src/lib/breaker.js +240 -0
- package/src/lib/geom.js +144 -0
- package/src/lib/palette.js +236 -0
- package/src/lib/transforms.js +111 -0
- package/src/pipeline.js +1983 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* honesty.js - Honesty-Gate (core-Invariante, Schritt 1: Fundament)
|
|
3
|
+
* Vector Mirror v2.0
|
|
4
|
+
*
|
|
5
|
+
* Die deterministische (REGEL-9, LLM-frei) und hexagonal-REINE (REGEL-4:
|
|
6
|
+
* KEINE Imports aus adapters/ oder interface/ — diese Datei hat bewusst
|
|
7
|
+
* NULL Imports) Klassifikations-/Assertions-Bibliothek, die SPAETER vor
|
|
8
|
+
* jeder Emission laeuft. Schritt 1 liefert nur die Bibliothek + Unit-Tests;
|
|
9
|
+
* es gibt JETZT noch keinen Caller (Verdrahtung = Schritt 3/4).
|
|
10
|
+
*
|
|
11
|
+
* Vier reine Funktionen. Pessimismus-Propagation (Spotter-Anti-Luege):
|
|
12
|
+
* im Zweifel wird Sicherheit verweigert, nicht behauptet.
|
|
13
|
+
*
|
|
14
|
+
* Quell-Verankerung (byte-genauer Port der heutigen Semantik):
|
|
15
|
+
* - allowDeltas == adapters/emitter/structured.js:359
|
|
16
|
+
* - assertEmissionGated == interface/schema.js:316-345 (superRefine, Live-Form)
|
|
17
|
+
* - countTruncation ersetzt spaeter das harte structured.js:426 suppressed:0
|
|
18
|
+
*
|
|
19
|
+
* Determinismus: keine Date, kein Math.random, kein I/O, kein LLM.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* allowDeltas — das SSOT-Gate. Byte-genauer Port der heutigen Semantik aus
|
|
24
|
+
* structured.js:359 (`const allowDeltas = elReliability === 'reliable';`).
|
|
25
|
+
*
|
|
26
|
+
* NUR `reliability → bool`. Die "default-reliable-bei-fehlender-id"-Logik
|
|
27
|
+
* (Lookup-Concern) bleibt beim spaeteren Caller — sie ist hier bewusst NICHT
|
|
28
|
+
* enthalten. 'approximate' / 'not_measurable' / alles andere → false
|
|
29
|
+
* (Pessimismus). Strikt case-sensitiv, exakt wie der Tripel-Gleich-Vergleich.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} reliability - z.B. 'reliable' | 'approximate' | 'not_measurable'
|
|
32
|
+
* @returns {boolean} true gdw. reliability strikt === 'reliable'
|
|
33
|
+
*/
|
|
34
|
+
export function allowDeltas(reliability) {
|
|
35
|
+
return reliability === 'reliable';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* bboxTrustedForVerdict — §H10 R11-07: darf eine bbox ein GRÜNES Verdikt
|
|
40
|
+
* tragen? Wohnt in der SSOT-Datei der Reliability-Semantik (REGEL-4, W1b-Pin),
|
|
41
|
+
* ist aber bewusst ein ANDERES Prädikat als allowDeltas (PRAEDIKAT-DISZIPLIN,
|
|
42
|
+
* s.o.): allowDeltas verweigert Pixel-Korrekturen schon bei 'approximate'
|
|
43
|
+
* (eine VORSCHREIBUNG braucht die exakte Zahl); das VERDIKT bleibt bei
|
|
44
|
+
* 'approximate' tragfähig (die Zahl ist annähernd wahr — Anti-Über-Gaten).
|
|
45
|
+
* NUR 'not_measurable' (3D-Transform / Non-SMIL-Motion: die Zahl selbst ist
|
|
46
|
+
* erklärt nicht reproduzierbar) trägt weder Korrektur noch grünes Verdikt.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} reliability - z.B. 'reliable' | 'approximate' | 'not_measurable'
|
|
49
|
+
* @returns {boolean} false gdw. reliability strikt === 'not_measurable'
|
|
50
|
+
*/
|
|
51
|
+
export function bboxTrustedForVerdict(reliability) {
|
|
52
|
+
return reliability !== 'not_measurable';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* gateCorrections — die EINE Pessimismus-Entscheidung pro failing-issue, am
|
|
57
|
+
* Emissions-Rand, EINMAL. Reiner (REGEL-4, importfrei) Port der heute in
|
|
58
|
+
* structured.js:358-365 verstreuten Inline-Logik in EINE Wahrheits-Quelle.
|
|
59
|
+
*
|
|
60
|
+
* Annotiert jedes issue mit `_gated = allowDeltas(reliabilityOf(issue.id) ?? '?')`.
|
|
61
|
+
* `reliabilityOf` ist eine reine Caller-Closure (id → reliability|undefined); der
|
|
62
|
+
* '?'-Default bei fehlendem Treffer macht `allowDeltas` zum Default-deny
|
|
63
|
+
* (Spotter-Anti-Luege: unbekannte id → keine Pixel-Korrektur).
|
|
64
|
+
*
|
|
65
|
+
* Bei `_gated === false` liefert die Funktion ein um dx/dy/dw/dh BEREINIGTES
|
|
66
|
+
* Derivat — UND eine um die Korrektur-VORSCHREIBUNG bereinigte `detail`-Prosa
|
|
67
|
+
* (sanitizeCorrectionDetail). Der Spotter-Anti-Luege-Schutz deckt damit BEIDE
|
|
68
|
+
* Kanaele, durch die ein Delta entkommen kann: das strukturierte Objekt-Feld
|
|
69
|
+
* (dx/dy/dw/dh) UND den detail-STRING (`. Korrektur: dx=..`, `Δw=..`,
|
|
70
|
+
* `Kürzester Fluchtweg: ..`), den die Constraint-Produzenten in denselben
|
|
71
|
+
* String einbetten. EIN Gate, vollstaendig. Bei `_gated === true` bleibt das
|
|
72
|
+
* issue (bis auf das additive `_gated`-Vertragsfeld) BYTE-IDENTISCH — die
|
|
73
|
+
* Sanitisierung laeuft NUR im deny-Zweig.
|
|
74
|
+
*
|
|
75
|
+
* PRAEDIKAT-DISZIPLIN (REGEL-9, bewusst NICHT zusammenfuehren): dieses Live-Gate
|
|
76
|
+
* nutzt `allowDeltas` (=== 'reliable', 4 Keys dx/dy/dw/dh, deny-unknown).
|
|
77
|
+
* `assertEmissionGated` (test-only Backstop, schema.js-Spiegel) nutzt ein
|
|
78
|
+
* ANDERES Praedikat (dx/dy-only, allow-unknown). Die beiden DECKEN SICH NICHT
|
|
79
|
+
* und duerfen es nicht — ein Merge wuerde entweder das Live-Gate aufweichen
|
|
80
|
+
* (dw/dh-Leak) oder den Backstop ueber-streng machen (unknown-id-Fehlalarm).
|
|
81
|
+
*
|
|
82
|
+
* @param {Array<{id?: string}>} failing - die failing-Liste (arbitrated.failing).
|
|
83
|
+
* @param {(id: string) => (string|undefined)} reliabilityOf - reine Lookup-Closure.
|
|
84
|
+
* @returns {Array} annotierte (und bei deny bereinigte) issue-Derivate.
|
|
85
|
+
*/
|
|
86
|
+
export function gateCorrections(failing, reliabilityOf) {
|
|
87
|
+
if (!Array.isArray(failing)) return [];
|
|
88
|
+
return failing.map((issue) => {
|
|
89
|
+
const _gated = allowDeltas(reliabilityOf(issue.id) ?? '?');
|
|
90
|
+
if (_gated) return { ...issue, _gated };
|
|
91
|
+
const { dx, dy, dw, dh, ...rest } = issue;
|
|
92
|
+
if (typeof rest.detail === 'string') {
|
|
93
|
+
rest.detail = sanitizeCorrectionDetail(rest.detail);
|
|
94
|
+
}
|
|
95
|
+
return { ...rest, _gated };
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* sanitizeCorrectionDetail — entfernt die VORGESCHRIEBENE Pixel-Korrektur aus
|
|
101
|
+
* dem detail-String eines failing-issue, behaelt aber die WAS/WIEVIEL-MESSUNG
|
|
102
|
+
* (VISION Prinzip 3: das Auge misst die Luecke, verschreibt sie nicht). Nur im
|
|
103
|
+
* deny-Zweig von gateCorrections aufgerufen (reliable bleibt byte-identisch).
|
|
104
|
+
*
|
|
105
|
+
* Exhaustiver Port der 3 Produzenten-Formen (grep-belegt, src/core/constraints):
|
|
106
|
+
* - `<WAS>. Korrektur: <deltas>` (centered-in, LEFT-OF, ABOVE,
|
|
107
|
+
* ALIGNED-LEFT/TOP, INSIDE)
|
|
108
|
+
* - `<WAS>. Kürzester Fluchtweg: <delta>` (NO-OVERLAP)
|
|
109
|
+
* - `Grösse weicht ab (Δw=..px, Δh=..px)` (SAME-SIZE)
|
|
110
|
+
* In allen Faellen wird die delta-tragende Vorschreibungs-Klausel abgeschnitten,
|
|
111
|
+
* die reine Beschreibung des Bruchs bleibt. distance.js (`Zu nah dran (..px statt
|
|
112
|
+
* ..px, Defizit ~..px)`) traegt KEINE Vorschreibung → bleibt unangetastet
|
|
113
|
+
* (ehrliche Distanz-Messung, kein Leak).
|
|
114
|
+
*
|
|
115
|
+
* @param {string} detail
|
|
116
|
+
* @returns {string}
|
|
117
|
+
*/
|
|
118
|
+
function sanitizeCorrectionDetail(detail) {
|
|
119
|
+
// Form 1+2: alles ab der Vorschreibungs-Klausel (Korrektur:/Fluchtweg:) inkl.
|
|
120
|
+
// des vorausgehenden Satz-Trenners abschneiden.
|
|
121
|
+
let out = detail.replace(/\.\s*(Korrektur|Kürzester Fluchtweg):.*$/u, '.');
|
|
122
|
+
// Form 3: den ` (Δw=..px, Δh=..px)`-Paren entfernen (SAME-SIZE).
|
|
123
|
+
out = out.replace(/\s*\(Δ[wh]=[^)]*\)/u, '');
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* classifyCanvas — Canvas-Validitaet aus zwei orthogonalen Renderer-Signalen.
|
|
129
|
+
*
|
|
130
|
+
* Pessimismus-Praezedenz (am-staerksten-degradiert gewinnt). Reihenfolge:
|
|
131
|
+
* 1. nicht-leerer sanitizeLoss → 'lossy' (DOMPurify hat
|
|
132
|
+
* Semantik entfernt; dominiert alles)
|
|
133
|
+
* 2. viewBoxValidity === 'degenerate' → 'degenerate' (NaN/zero/negativ)
|
|
134
|
+
* 3. viewBoxValidity === 'default_replaced' → 'default_replaced' (300x150 legitim)
|
|
135
|
+
* 4. sonst → 'valid'
|
|
136
|
+
*
|
|
137
|
+
* Die Eingabe-Felder erzeugt der Renderer (Schritt 2); hier nur die reine
|
|
138
|
+
* Klassifikation synthetischer Eingaben.
|
|
139
|
+
*
|
|
140
|
+
* @param {{viewBoxValidity?: string, sanitizeLoss?: Array}} resolved
|
|
141
|
+
* @returns {'valid'|'default_replaced'|'degenerate'|'lossy'}
|
|
142
|
+
*/
|
|
143
|
+
export function classifyCanvas({ viewBoxValidity, sanitizeLoss } = {}) {
|
|
144
|
+
if (Array.isArray(sanitizeLoss) && sanitizeLoss.length > 0) return 'lossy';
|
|
145
|
+
if (viewBoxValidity === 'degenerate') return 'degenerate';
|
|
146
|
+
if (viewBoxValidity === 'default_replaced') return 'default_replaced';
|
|
147
|
+
return 'valid';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* assertEmissionGated — Live-Form der superRefine-Invariante aus
|
|
152
|
+
* schema.js:316-345. Reine Pruefung: liefert die Liste der Verstoesse (entscheidet
|
|
153
|
+
* NICHT ueber die Fail-closed-Reaktion — das tut der Caller in Schritt 3).
|
|
154
|
+
*
|
|
155
|
+
* Ein Verstoss liegt vor, wenn eine correction ein dx oder dy traegt, deren
|
|
156
|
+
* Ziel-Element bbox_reliability ∈ {'not_measurable','approximate'} hat.
|
|
157
|
+
* Map-Lookup-Muster wie schema.js:319-321; '#'-Praefix-Strip wie schema.js:326;
|
|
158
|
+
* Lookup-Fehlschlag (reliability === undefined) ist KEIN Verstoss (schema.js:332).
|
|
159
|
+
*
|
|
160
|
+
* @param {{scene?: {elements?: Array}, corrections?: Array}} structured
|
|
161
|
+
* @returns {Array<{index: number, element: string, reliability: string, message: string}>}
|
|
162
|
+
*/
|
|
163
|
+
export function assertEmissionGated(structured) {
|
|
164
|
+
const violations = [];
|
|
165
|
+
if (!structured?.scene) return violations;
|
|
166
|
+
const elements = structured.scene.elements;
|
|
167
|
+
if (!Array.isArray(elements)) return violations;
|
|
168
|
+
const corrections = structured.corrections;
|
|
169
|
+
if (!Array.isArray(corrections)) return violations;
|
|
170
|
+
|
|
171
|
+
const reliabilityById = new Map();
|
|
172
|
+
for (const el of elements) {
|
|
173
|
+
reliabilityById.set(el.id, el.bbox_reliability);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < corrections.length; i++) {
|
|
177
|
+
const c = corrections[i];
|
|
178
|
+
if (!c || typeof c.element !== 'string') continue;
|
|
179
|
+
const id = c.element.replace(/^#/, '');
|
|
180
|
+
const reliability = reliabilityById.get(id);
|
|
181
|
+
// Lookup-Fehlschlag: separater REGEL-3-Bruch (β-003), hier kein Verstoss.
|
|
182
|
+
if (reliability === undefined) continue;
|
|
183
|
+
if (reliability === 'not_measurable' || reliability === 'approximate') {
|
|
184
|
+
const hasDx = c.dx !== undefined;
|
|
185
|
+
const hasDy = c.dy !== undefined;
|
|
186
|
+
if (hasDx || hasDy) {
|
|
187
|
+
violations.push({
|
|
188
|
+
index: i,
|
|
189
|
+
element: c.element,
|
|
190
|
+
reliability,
|
|
191
|
+
message:
|
|
192
|
+
`REGEL-3: corrections[${i}].element='${c.element}' verweist auf scene.elements mit ` +
|
|
193
|
+
`bbox_reliability='${reliability}' — dx/dy duerfen nicht emittiert werden (Spotter-Anti-Luege).`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return violations;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* countTruncation — ehrlicher Trunkierungs-Zaehler. Ersetzt spaeter das harte
|
|
203
|
+
* structured.js:426 `suppressed: 0`.
|
|
204
|
+
*
|
|
205
|
+
* @param {Array} items - die vollstaendige (ungekuerzte) Liste
|
|
206
|
+
* @param {number} cap - die Obergrenze sichtbarer Eintraege
|
|
207
|
+
* @returns {{total: number, returned: number, suppressed: number}}
|
|
208
|
+
*/
|
|
209
|
+
export function countTruncation(items, cap) {
|
|
210
|
+
const total = items.length;
|
|
211
|
+
const returned = Math.min(cap, total);
|
|
212
|
+
const suppressed = Math.max(0, total - cap);
|
|
213
|
+
return { total, returned, suppressed };
|
|
214
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auto_ids.js — §1.3 Auto-ID Content-Addressed Hash-Namespace
|
|
3
|
+
* Vector Mirror v2.0 (Sprint-β2 §1.3 LAYER-TRENNUNG, Patch2)
|
|
4
|
+
*
|
|
5
|
+
* Core module: NO imports from adapters/ or interface/ (REGEL-4 Hexagonal).
|
|
6
|
+
* NO external deps — node:crypto is built-in (REGEL-7 Eigenstaendigkeit).
|
|
7
|
+
*
|
|
8
|
+
* Layer-Trennung (Patch2, Mini-Review-Konvergenz Opus+Codex):
|
|
9
|
+
* Dieses Modul ist PURE FORMAT-Layer. Es kennt das Auto-ID-Format
|
|
10
|
+
* `_<8hex>_<tag><n>` und erzeugt Strings nach diesem Format — aber es
|
|
11
|
+
* kennt KEINE Tag-Allowlist und entscheidet NICHT, welche Tags gültig
|
|
12
|
+
* sind. Domain-Vertrag (Spotter-Set + Format-Regex) lebt in
|
|
13
|
+
* `../element_vocabulary.js`. Aufrufer entscheidet vorab, welche Tags
|
|
14
|
+
* überhaupt eine Auto-ID erhalten (Renderer-Adapter ruft isSpotterTag
|
|
15
|
+
* aus element_vocabulary.js).
|
|
16
|
+
*
|
|
17
|
+
* Konsequenz: formatAutoId/extractAutoIds sind NICHT "total" über
|
|
18
|
+
* beliebige Tags (F-PATCH-CODEX-002) — sie sind als pure Format-Helper
|
|
19
|
+
* konzipiert. Das ist BEABSICHTIGT: die Allowlist gehört in den
|
|
20
|
+
* Domain-Layer, nicht in den Format-Layer. Würde extractAutoIds selbst
|
|
21
|
+
* filtern, wäre `element_vocabulary.js` doppelt importiert (Layer-
|
|
22
|
+
* Vermischung). Die Verantwortung "nur Spotter-Tags reinreichen"
|
|
23
|
+
* liegt beim Caller (siehe playwright.js nextAutoId-Gate).
|
|
24
|
+
*
|
|
25
|
+
* Adressiert D-004 (ADR-025 §3 Auto-ID-Versprechen, 0 Treffer im Code) und
|
|
26
|
+
* FIX_PLAN §1.3 (Beschluss B-4-modified, 8 hex statt 6 hex).
|
|
27
|
+
*
|
|
28
|
+
* Format: _<8hex>_<tag><n>
|
|
29
|
+
* <8hex> = sha256(sanitizedSvg) als hex, truncated auf 8 chars (32 bit)
|
|
30
|
+
* <tag> = lowercase Element-Tag (rect, circle, path, ...)
|
|
31
|
+
* <n> = monoton steigender, tag-lokaler Counter (1, 2, 3, ...)
|
|
32
|
+
*
|
|
33
|
+
* Birthday-Bound (FIX_PLAN §1.3 Tabelle):
|
|
34
|
+
* 8 hex (32 bit) → P(Kollision) bei N=1000 ≈ 0.012 %, 50%-Punkt N ≈ 77163.
|
|
35
|
+
*
|
|
36
|
+
* Hash-Berechnung MUSS in Node-Land VOR page.setContent passieren (H-18 §1.3).
|
|
37
|
+
* Der Browser-Kontext hat kein node:crypto — daher ist diese Funktion PURE
|
|
38
|
+
* und arbeitet auf dem bereits-sanitized SVG-String (sanitizedSvg) plus
|
|
39
|
+
* einer in-Browser-extrahierten DOM-Element-Liste (tag-Liste reicht).
|
|
40
|
+
*/
|
|
41
|
+
import { createHash } from 'node:crypto';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Berechnet den 8-hex SHA-256-Praefix fuer den uebergebenen sanitizedSvg.
|
|
45
|
+
* Pure: gleiche Eingabe → gleiche Ausgabe (Determinismus-Garantie).
|
|
46
|
+
*
|
|
47
|
+
* @param {string} sanitizedSvg - DOMPurified SVG-String (Node-Land).
|
|
48
|
+
* @returns {string} 8-stelliger lowercase hex-Praefix.
|
|
49
|
+
*/
|
|
50
|
+
export function computeSvgHashPrefix(sanitizedSvg) {
|
|
51
|
+
if (typeof sanitizedSvg !== 'string') {
|
|
52
|
+
throw new TypeError('computeSvgHashPrefix erwartet einen String');
|
|
53
|
+
}
|
|
54
|
+
return createHash('sha256').update(sanitizedSvg).digest('hex').slice(0, 8);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generiert die Auto-ID fuer ein einzelnes Element.
|
|
59
|
+
* Format: _<8hex>_<tag><n>
|
|
60
|
+
*
|
|
61
|
+
* @param {string} hashPrefix - 8-hex Praefix (siehe computeSvgHashPrefix).
|
|
62
|
+
* @param {string} tag - lowercase Element-Tag (z.B. 'rect').
|
|
63
|
+
* @param {number} n - tag-lokaler Counter (1-basiert).
|
|
64
|
+
* @returns {string} Auto-ID.
|
|
65
|
+
*/
|
|
66
|
+
export function formatAutoId(hashPrefix, tag, n) {
|
|
67
|
+
return `_${hashPrefix}_${tag}${n}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Pure Generator-Funktion fuer Auto-IDs ueber eine Tag-Sequenz.
|
|
72
|
+
*
|
|
73
|
+
* Eingabe ist die geordnete Tag-Sequenz der DOM-Elemente OHNE explizite
|
|
74
|
+
* SVG-id (Reihenfolge wie sie im SVG-DOM erscheinen). Der Counter ist
|
|
75
|
+
* tag-lokal und monoton steigend, sodass _<hash>_rect1, _<hash>_rect2,
|
|
76
|
+
* _<hash>_circle1, _<hash>_rect3 entstehen — analog der bisherigen
|
|
77
|
+
* positional-stable DOM-Path-Logik, aber content-addressed via Hash-Praefix.
|
|
78
|
+
*
|
|
79
|
+
* Aufrufer-Vertrag (playwright.js):
|
|
80
|
+
* - sanitizedSvg ist im Node-Land VOR page.setContent verfuegbar.
|
|
81
|
+
* - tagSequence wird in der Browser-Iteration aufgebaut (gleiche Reihenfolge
|
|
82
|
+
* wie elements.push()), nur fuer Elemente ohne explizite id.
|
|
83
|
+
* - Rueckgabe: Array von Auto-IDs in derselben Reihenfolge wie tagSequence.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} sanitizedSvg - DOMPurified SVG-String.
|
|
86
|
+
* @param {ReadonlyArray<string>} tagSequence - Geordnete Tag-Liste der
|
|
87
|
+
* Elemente ohne explizite id (z.B. ['rect', 'rect', 'circle']).
|
|
88
|
+
* @returns {string[]} Auto-IDs in derselben Reihenfolge.
|
|
89
|
+
*/
|
|
90
|
+
export function extractAutoIds(sanitizedSvg, tagSequence) {
|
|
91
|
+
if (!Array.isArray(tagSequence)) {
|
|
92
|
+
throw new TypeError('extractAutoIds erwartet ein Array als tagSequence');
|
|
93
|
+
}
|
|
94
|
+
const hashPrefix = computeSvgHashPrefix(sanitizedSvg);
|
|
95
|
+
const counters = new Map();
|
|
96
|
+
const out = new Array(tagSequence.length);
|
|
97
|
+
for (let i = 0; i < tagSequence.length; i++) {
|
|
98
|
+
const tag = tagSequence[i];
|
|
99
|
+
const next = (counters.get(tag) || 0) + 1;
|
|
100
|
+
counters.set(tag, next);
|
|
101
|
+
out[i] = formatAutoId(hashPrefix, tag, next);
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tolerance.js - 5% Tolerance Rule
|
|
3
|
+
* Extracted from mirror.js:32-33
|
|
4
|
+
* Vector Mirror v2.0
|
|
5
|
+
*
|
|
6
|
+
* Tolerance formula: tolerance = min(cellW * 0.5, max(2, dimension * 0.05))
|
|
7
|
+
* See BAUPLAN S.13 (Eichkoerper EK-3: 5%-Toleranz-Regel)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns tolerance for X-axis based on dimension and grid.
|
|
12
|
+
*/
|
|
13
|
+
export function getTolX(dimension, grid) {
|
|
14
|
+
return Math.min(grid.cellW * 0.5, Math.max(2, dimension * 0.05));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns tolerance for Y-axis based on dimension and grid.
|
|
19
|
+
*/
|
|
20
|
+
export function getTolY(dimension, grid) {
|
|
21
|
+
return Math.min(grid.cellH * 0.5, Math.max(2, dimension * 0.05));
|
|
22
|
+
}
|