Files
TREK/client/src/components/Studio/bookIds.ts
T
Maurice 15f22058e9 refactor(studio): split the content panel and cover the new paths
Three things Sonar was right about. The content panel had grown a filter, a
menu and an upload into one function of thirty-two branches: the filtering
is four pure rules in photoFilter.ts, the row that drives it is its own
component, and what is left in the panel is the grid and the drop zone. The
gallery rows behind an entry upload move out of the store's setter into a
named function, since five levels of nesting is a place bugs hide. And the
ids for placed elements come from getRandomValues rather than Math.random,
in one module instead of four copies of the same line: two editors minting
the same id for two elements is one of them losing their work.

The new paths carry tests now, which is what the gate was really asking
for: the filter's rules, the ids, the upload helper, the store's derived
gallery rows, the canvas file drop, and the shell's upload, drop and stop
switch.
2026-09-03 10:47:08 +02:00

32 lines
1.3 KiB
TypeScript

/**
* Ids for the things a book is made of.
*
* Every panel that places an element mints one, and they were four copies of
* the same line: a prefix, `Math.random().toString(36)` and a slice. Copies of
* a line drift, and this one had already drifted in its slice length.
*
* `getRandomValues` rather than `Math.random`, for the reason
* `utils/randomId.ts` gives at greater length: TREK's own quickstart is plain
* http on a LAN address, where `crypto.randomUUID` is undefined but
* `getRandomValues` still works. A book document is shared over a websocket and
* merged by whoever saves last, so two editors minting the same id for two
* different elements is a collision that shows up as one of them losing their
* work. Short of Web Crypto entirely, the old arithmetic is still there.
*
* Seven characters, as before: 36^7 is about 78 billion, against a document
* that holds a few hundred elements.
*/
const ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789'
export function elementId(prefix: string): string {
const c = globalThis.crypto
if (c && typeof c.getRandomValues === 'function') {
const bytes = new Uint8Array(7)
c.getRandomValues(bytes)
let out = ''
for (const b of bytes) out += ALPHABET[b % ALPHABET.length]
return `${prefix}-${out}`
}
return `${prefix}-${Math.random().toString(36).slice(2, 9)}`
}