VersionDiff

Versiovertailu

Kahden version ero avain–arvo-riveinä, muutos päällekkäin −/+ ja muutosloki aikajanana.

Käyttöpaikat

Suoraan: palvelu tuo osan itse. Välillisesti: osa tulee mukaan toisen paketin osan kautta, ja sen muutos näkyy palvelussa silti. Laskettu julkaisun yhteydessä 23.10.2026.

TP-IAM
TP-PERUSTUS
TP-VASTAANOTTO
Ostohinta
Poistettu: 29,86 €
Lisätty: 31,20 €

1 muuttunut · 1 lisätty · 1 poistettu

Versio 3
Versio 4

Nimi — muuttunut

Battery for MT9557

Akku MT9557, 3,87 V

Paino — lisätty

—

0,12 kg

Väri — poistettu

musta

—

1 muuttumatonta kenttää

EAN

5711045123456

5711045123456

Esimerkki valitulla teemalla ja sävyllä; vaihda ne yläpalkista.

Käyttö

Paketin README:n luku sellaisenaan.

VersionDiff — kahden version ero

const rivit = diffEntries(
  vanha.map((a) => ({ key: a.name, value: a.value, meta: a.source })),
  uusi.map((a) => ({ key: a.name, value: a.value, meta: a.source })),
);
<VersionDiff entries={rivit} beforeLabel="v3" afterLabel="v4" />

Vertailu on `lib/diff.ts`:ssä, komponentti on pelkkä esitys. Sama jako kuin lajittelussa ja sivutuksessa.

diffEntries(before, after, opts)rivit tiloineen: added · removed · changed · unchanged
diffSummary(entries)montako kussakin tilassa
hasChanges(entries)onko mitään eroa

Vertailun kolme sääntöä, kaikki tarpeen ulkopuolelta tulevalle datalle:

  • null, undefined ja "" ovat sama asia — puuttuva kenttä voi olla mikä tahansa niistä
  • 5 ja "5" ovat sama arvo, mutta `"5 kg"` ei ole `5`
  • 0 ja false eivät ole tyhjiä

> `compareMeta` on oletuksena `true`. Arvon alkuperän muutos on muutos, > vaikka arvo pysyisi samana: *ehdotus on väite lähteestä, tallennettu arvo > on päätös.* Perustelu on AIMO:lta, ja se korjasi oletukseni. false jos > alkuperää ei ole tai se on kohinaa.

Muuttumattomat rivit ovat suljetussa <details>-lohkossa — natiivi disclosure eikä React-tila, joten komponentti pysyy palvelinpuolella ja toimii ilman JavaScriptiä.

ChangeRow ja ChangePair — muutos kahtena rivinä

Vanha arvo punaisella «−», uusi vihreällä «+», päällekkäin kuten koodidiffissä. Tämä on muutoshistorian ainoa esitystapa (omistajan päätös): vierekkäiset sarakkeet pakottavat silmän hyppäämään, ja «35 → 35.0» ei kerro kumpi on vanha ennen kuin konvention on oppinut.

<ChangePair label="Ostohinta alv 0 %" before="19,31 €" after="20,33 €" source="lähde toimittaja" />
<ChangePair label="Tullikoodi" after="39241000" />   // uusi tieto: vain +

Ruudunlukijalle sama sanoin (Poistettu: / Lisätty:); etumerkki on aria-hidden. Teksti pysyy text-foregroundilla ja väriä kantaa reuna ja etumerkki — sama jako kuin Badgein pehmeillä varianteilla. VersionDiff on edelleen oikea työkalu kahden kokonaisen version vertailuun avaimittain; tämä on yhden arvon muutos siellä missä se näytetään.

Changelog — versiohistoriasivu

Jokaisella TP-palvelulla on /versiohistoria-sivu joka näyttää oman CHANGELOG.md:nsä. Changelog renderöi sen aikajanana.

// app/(app)/versiohistoria/page.tsx
import { readFile } from "node:fs/promises";
import { join } from "node:path";

import { Changelog, PageHeader } from "@karkkainen/ui";

export default async function VersiohistoriaPage() {
  const md = await readFile(join(process.cwd(), "CHANGELOG.md"), "utf8");

  return (
    <>
      <PageHeader
        title="Versiohistoria"
        subtitle="Kaikki merkittävät muutokset. Näkymä peilaa repon CHANGELOG.md:tä."
      />
      <Changelog markdown={md} />
    </>
  );
}

Komponentti ei lue tiedostoa itse. Polku on palvelukohtainen, ja node:fs rikkoisi komponentin asiakaspuolella — anna markdown merkkijonona.

PropsiOletusMerkitys
markdown—CHANGELOG.md:n sisältö
initialCount10Montako uusinta julkaisua näytetään avattuna; loput <details>-elementin taakse. 0 = kaikki auki
summarytrueYhteenvetokortit: nykyinen versio ja julkaisemattomien määrä

Tuettu markdown: versio-otsikot sekä ajatus- (–) että tavuviivalla (-), versiottomat osiot (## Julkaisematon), vapaamuotoiset ###-otsikot, koodilohkot, taulukot, sisäkkäiset listat ja inline-muotoilut.

Jos haluat renderöidä itse, parseChangelog on viety erikseen:

import { parseChangelog, type ChangelogRelease } from "@karkkainen/ui";

const { intro, releases } = parseChangelog(md);

Konventiot:

  • Näkymä peilaa CHANGELOG.md:tä. Älä ylläpidä erillistä listaa sivulla — jos ne erkanevat, sivu on väärässä eikä kukaan huomaa.
  • Otsikkotyypit (Lisätty, Muutettu, Korjattu, Poistettu, Turvallisuus, Vanhentunut) saavat automaattisesti oman Badge-varianttinsa. Ne tunnistetaan myös muodosta ### Lisätty: kuvaus.
  • Tyhjää `## Julkaisematon` -osiota ei kannata jättää — se renderöityy sisällöttömänä julkaisuna.

Tuonti ja lähde

import { VersionDiff, ChangeRow, ChangePair, ChangelogText, Changelog } from "@karkkainen/ui";
  • src/components/version-diff.tsx
  • src/components/change-row.tsx
  • src/components/changelog.tsx