Aanvulling op 1.0.6 — waarom een Engelse tokenizer opgeslagen tabs onvindbaar maakt
Mijn eerste bericht in dit topic ging over het herstellen van tabgroepen zonder de globale tabvolgorde kwijt te raken. De vorige aanvulling ging over het verschil tussen een geslaagde IndexedDB-request en een werkelijk voltooide transactie. Deze update zit in een andere laag: opgeslagen tabs terugvinden wanneer titel, URL en groepsnaam niet netjes Engelstalig zijn.
Ik ben Danila Pryadko en ik bouw Tabwell zelf. Dit is dus een technische bouwupdate van de maker, geen onafhankelijke aanbeveling. AI heeft geholpen bij de Nederlandse taalredactie; de technische beweringen hieronder heb ik gecontroleerd tegen de onveranderde bron van de publiek uitgebrachte versie 1.0.6.
Eén document per tab, maar met de context erbij
De zoekindex draait in het geheugen met Orama. Bij elke volledige indexering wordt een nieuwe index opgebouwd. Voor iedere opgeslagen tab ontstaat één document met onder meer:
{
id: `${snapshot.id}:${tab.id}`,
snapshotId: snapshot.id,
tabId: tab.id,
title: tab.title,
url: tab.url,
groupTitle
}
De titel van de groep wordt daarbij gedenormaliseerd. Dat kost een beetje extra indexruimte, maar een zoekresultaat bevat dan meteen de snapshot, de tab en de groepscontext. De presentatielaag hoeft niet na elke hit nog een tweede lookup uit te voeren om te kunnen tonen waar het resultaat vandaan komt.
Titel, URL en groepsnaam blijven bovendien drie afzonderlijke zoekproperties. Dat is belangrijk voor de uitleg in de interface: een match in een paginatitel heeft voor een gebruiker een andere betekenis dan een match in een URL of in de naam van een tabgroep.

Echte Nederlandse interface: zoekresultaten blijven gekoppeld aan hun opgeslagen snapshot en bronveld.
Waarom toLowerCase() niet genoeg is
Een standaard Engelse tokenizer kan Cyrillische of Oost-Aziatische tekens verliezen voordat de zoekmachine ze ooit ziet. Alleen de invoer naar kleine letters omzetten lost dat niet op. De tokenizer in 1.0.6 begint daarom met Unicode-normalisatie:
const normalized = raw
.normalize('NFKD')
.toLocaleLowerCase()
.replace(/[\u0300-\u036f]/g, '');
NFKD splitst bijvoorbeeld veel Latijnse letters en diakritische tekens op. Daarna verwijdert de code de combining characters in het bereik U+0300–U+036F. Daardoor kan een zoekopdracht als cafe ook een titel met Café vinden. De keuze is bewust beperkt: dit is geen belofte dat ieder schrift of iedere denkbare Unicode-equivalentie hetzelfde wordt behandeld.
Vervolgens worden woorden geselecteerd met Unicode-properties, niet met een ASCII-klasse:
/[\p{L}\p{M}\p{N}_'-]+/gu
Letters, combining marks en cijfers uit verschillende schriften blijven zo behouden, evenals underscore, apostrof en koppelteken. Dat laatste is praktisch voor URL-hosts en technische termen. Dubbele tokens worden verwijderd voordat ze naar de index gaan.
Segmenteren waar spaties geen betrouwbare grens zijn
Voor een reeks met Han-, Hiragana- of Katakana-tekens gebruikt de tokenizer Intl.Segmenter met woordgranulariteit, als die API in de runtime beschikbaar is. Alleen segmenten met isWordLike komen in de tokenlijst. Dat maakt een Japanse of Chinese titel doorzoekbaar zonder te doen alsof een spatie de enige geldige woordgrens is.
Dit is expres geen brede claim over “alle CJK”. De trigger in deze versie noemt exact Han, Hiragana en Katakana. Hangul volgt de algemene Unicode-woordroute. En wanneer Intl.Segmenter ontbreekt, blijft zo’n ongesegmenteerde reeks één token. Ook gebruikt Intl.Segmenter(undefined, …) de locale van de runtime; de implementatie legt dus geen vaste Nederlandse, Japanse of Chinese locale op.

Technische uitlegvisual: normalisatie en segmentatie voeden drie afzonderlijke zoekvelden; ranking en zichtbare markering blijven expliciete lagen.
Zoeken en uitleggen zijn nog niet exact dezelfde bewerking
De zoekaanroep doorzoekt title, url en groupTitle, standaard met maximaal vijftig resultaten. Fuzzy tolerance staat op nul. Daarna bepaalt een kleine presentatiefunctie het gematchte bronveld: eerst titel, dan URL, dan groepsnaam. De markeerfunctie bewaart de originele hoofdletters en splitst de zichtbare tekst in gematchte en niet-gematchte segmenten.
Daar zit in 1.0.6 een interessante grens. De index gebruikt NFKD en diacritic folding, terwijl de bronveldkeuze en markering alleen een lowercase substringvergelijking doen. cafe kan dus via de index een document met Café opleveren, zonder dat de huidige letterlijke markeerfunctie noodzakelijk precies dezelfde passage markeert. Bij de zichtbare zoekopdracht ontwerp uit de schermafbeelding bestaat dat verschil niet, maar als algemeen contract is het wel relevant.
Mijn huidige voorkeur is om ranking en presentatie als twee expliciete lagen te blijven behandelen, en tegelijk de normalisatie-offsets beschikbaar te maken zodat een accentongevoelige match toch op de juiste originele tekens kan worden gemarkeerd.
Wat zouden jullie in zo’n geval prioriteren: de markeerlaag exact dezelfde Unicode-normalisatie laten volgen als de index, of alleen letterlijke tekens markeren en de accentongevoelige match op een andere manier uitleggen?