Files
erikandClaude Sonnet 5 6ba93b2a92 Plan Checker, Wochenbericht-Fokus-Modus und Wandtafel-Dashboard-Feinschliff
Plan Checker (neues Studenten-Werkzeug, Seitenleiste + Admin → Plan-Checker-
Merkmale): lädt einen Bauplan als PDF hoch und lässt ChatGPT oder Claude ihn
gegen eine gepflegte Merkmalsliste prüfen. PDF-Rendering (pdf.js +
@napi-rs/canvas) berücksichtigt jetzt Ebenen (Optional Content Groups) und
WASM-Decoder korrekt, sendet eine Übersicht plus mehrere hochaufgelöste
Kachel-Ausschnitte statt eines einzelnen, zu groben Bilds, und beide Anbieter
bekommen dasselbe Bildmaterial. Jedes Prüfergebnis kann auf eines der
gesendeten Bilder als Beleg verweisen (bildIndex) — als Screenshot im
Ergebnis anklickbar, öffnet vergrößert in einem Modal statt eines von Chrome
blockierten target=_blank-Links auf eine data:-URL. KI-Modellname ist jetzt
pro Konto überschreibbar (Einstellungen → Mein Konto) für Drittanbieter mit
abweichendem Modell-Katalog.

Wochenbericht: dichte Kartenansicht bekommt einen Fokus-Modus — nur der
Kommentar liegt offen, Leistungsphasen und Beteiligte stecken in
aufklappbaren Abschnitten statt dauerhaft nebeneinander zu stehen.

Projektbereich: Terminplan-Auslastung rechnet jetzt exakt wie der
Auslastungs-Tab/die Wandtafel (statt einer unabhängigen, tageweisen
Schätzung); die LPH-Fortschrittsliste im Dashboard zeigt nur noch die
beauftragten Leistungsphasen.

Wandtafel: Dashboard-Kopf mit Kennzahlen-Reihe und Panel-Reihe neu
strukturiert, in den Blautönen des Firmenlogos; dieselbe Palette wurde
anschließend auf den gesamten App-weiten Dark Mode übernommen.

Dateibereinigung: nicht mehr benötigte Test-Uploads/Backups entfernt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-05 14:01:41 +02:00

245 lines
6.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict'
const assert = require('node:assert')
const { utf8DecodeBytes } = require('../../encoding')
/**
* @param {(char: string) => boolean} condition
* @param {string} input
* @param {{ position: number }} position
* @returns {string}
*
* @see https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
*/
function collectASequenceOfCodePoints (condition, input, position) {
// 1. Let result be the empty string.
let result = ''
// 2. While position doesn’t point past the end of input and the
// code point at position within input meets the condition condition:
while (position.position < input.length && condition(input[position.position])) {
// 1. Append that code point to the end of result.
result += input[position.position]
// 2. Advance position by 1.
position.position++
}
// 3. Return result.
return result
}
/**
* A faster collectASequenceOfCodePoints that only works when comparing a single character.
* @param {string} char
* @param {string} input
* @param {{ position: number }} position
* @returns {string}
*
* @see https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
*/
function collectASequenceOfCodePointsFast (char, input, position) {
const idx = input.indexOf(char, position.position)
const start = position.position
if (idx === -1) {
position.position = input.length
return input.slice(start)
}
position.position = idx
return input.slice(start, position.position)
}
const ASCII_WHITESPACE_REPLACE_REGEX = /[\u0009\u000A\u000C\u000D\u0020]/g // eslint-disable-line no-control-regex
/**
* @param {string} data
* @returns {Uint8Array | 'failure'}
*
* @see https://infra.spec.whatwg.org/#forgiving-base64-decode
*/
function forgivingBase64 (data) {
// 1. Remove all ASCII whitespace from data.
data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '')
let dataLength = data.length
// 2. If data’s code point length divides by 4 leaving
// no remainder, then:
if (dataLength % 4 === 0) {
// 1. If data ends with one or two U+003D (=) code points,
// then remove them from data.
if (data.charCodeAt(dataLength - 1) === 0x003D) {
--dataLength
if (data.charCodeAt(dataLength - 1) === 0x003D) {
--dataLength
}
}
}
// 3. If data’s code point length divides by 4 leaving
// a remainder of 1, then return failure.
if (dataLength % 4 === 1) {
return 'failure'
}
// 4. If data contains a code point that is not one of
// U+002B (+)
// U+002F (/)
// ASCII alphanumeric
// then return failure.
if (/[^+/0-9A-Za-z]/.test(data.length === dataLength ? data : data.substring(0, dataLength))) {
return 'failure'
}
const buffer = Buffer.from(data, 'base64')
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)
}
/**
* @param {number} char
* @returns {boolean}
*
* @see https://infra.spec.whatwg.org/#ascii-whitespace
*/
function isASCIIWhitespace (char) {
return (
char === 0x09 || // \t
char === 0x0a || // \n
char === 0x0c || // \f
char === 0x0d || // \r
char === 0x20 // space
)
}
/**
* @param {Uint8Array} input
* @returns {string}
*
* @see https://infra.spec.whatwg.org/#isomorphic-decode
*/
function isomorphicDecode (input) {
// 1. To isomorphic decode a byte sequence input, return a string whose code point
// length is equal to input’s length and whose code points have the same values
// as the values of input’s bytes, in the same order.
const length = input.length
if ((2 << 15) - 1 > length) {
return String.fromCharCode.apply(null, input)
}
let result = ''
let i = 0
let addition = (2 << 15) - 1
while (i < length) {
if (i + addition > length) {
addition = length - i
}
result += String.fromCharCode.apply(null, input.subarray(i, i += addition))
}
return result
}
const invalidIsomorphicEncodeValueRegex = /[^\x00-\xFF]/ // eslint-disable-line no-control-regex
/**
* @param {string} input
* @returns {string}
*
* @see https://infra.spec.whatwg.org/#isomorphic-encode
*/
function isomorphicEncode (input) {
// 1. Assert: input contains no code points greater than U+00FF.
assert(!invalidIsomorphicEncodeValueRegex.test(input))
// 2. Return a byte sequence whose length is equal to input’s code
// point length and whose bytes have the same values as the
// values of input’s code points, in the same order
return input
}
const nonASCIIRegex = /[^\x00-\x7F]/ // eslint-disable-line no-control-regex
/**
* @param {string} str
* @returns {string}
*
* @see https://infra.spec.whatwg.org/#ascii-lowercase
*/
function asciiLowercase (str) {
return nonASCIIRegex.test(str)
? str.replace(/[A-Z]+/g, (upper) => upper.toLowerCase())
: str.toLowerCase()
}
/**
* @see https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value
* @param {Uint8Array} bytes
*/
function parseJSONFromBytes (bytes) {
return JSON.parse(utf8DecodeBytes(bytes))
}
/**
* @param {string} str
* @param {boolean} [leading=true]
* @param {boolean} [trailing=true]
* @returns {string}
*
* @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
*/
function removeASCIIWhitespace (str, leading = true, trailing = true) {
return removeChars(str, leading, trailing, isASCIIWhitespace)
}
/**
* @param {string} str
* @param {boolean} leading
* @param {boolean} trailing
* @param {(charCode: number) => boolean} predicate
* @returns {string}
*/
function removeChars (str, leading, trailing, predicate) {
let lead = 0
let trail = str.length - 1
if (leading) {
while (lead < str.length && predicate(str.charCodeAt(lead))) lead++
}
if (trailing) {
while (trail >= lead && predicate(str.charCodeAt(trail))) trail--
}
return lead === 0 && trail === str.length - 1 ? str : str.slice(lead, trail + 1)
}
// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string
function serializeJavascriptValueToJSONString (value) {
// 1. Let result be ? Call(%JSON.stringify%, undefined, « value »).
const result = JSON.stringify(value)
// 2. If result is undefined, then throw a TypeError.
if (result === undefined) {
throw new TypeError('Value is not JSON serializable')
}
// 3. Assert: result is a string.
assert(typeof result === 'string')
// 4. Return result.
return result
}
module.exports = {
asciiLowercase,
collectASequenceOfCodePoints,
collectASequenceOfCodePointsFast,
forgivingBase64,
isASCIIWhitespace,
isomorphicDecode,
isomorphicEncode,
parseJSONFromBytes,
removeASCIIWhitespace,
removeChars,
serializeJavascriptValueToJSONString
}