@konce-pt/angular 0.8.3 → 0.9.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/CHANGELOG.md CHANGED
@@ -1,5 +1,538 @@
1
1
  # @konce-pt/angular
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 45b2f99: `KptButton` dostaje ikony, wariant link, `<a>` i pełną paletę statusów.
8
+
9
+ Wsparcie ikon w przycisku do tej pory nie istniało — przykłady działały przypadkiem. `.kpt-button__content`
10
+ było zwykłym inline `<span>` bez `display:flex` i bez `gap`, więc odstęp ikona↔tekst brał się z literalnej
11
+ spacji w szablonie: wystarczyło sformatować szablon na dwie linie i odstęp znikał. Token
12
+ `--kpt-button-gap` był przy tym martwy, bo `gap` siedział na `.kpt-button`, który bez `loading` miał jedno
13
+ dziecko. Ikona siadała na linii bazowej zamiast na środku, a jej rozmiar szedł za `font-size` przycisku,
14
+ czyli 13 px przy `size="sm"` — zupełnie inna skala niż w `kpt-icon-button`.
15
+
16
+ Teraz są dwie drogi i jeden wynik. `icon` i `iconEnd` biorą nazwę z rejestru i renderuje je komponent;
17
+ to, co wstawisz do slotu, układa się tak samo. Jedno i drugie dostaje odstęp z tokena i rozmiar idący za
18
+ `size` (0.875rem przy `xs` do 1.5rem przy `xl`). `iconClass` / `iconEndClass` (w React `iconClassName` /
19
+ `iconEndClassName`) dokładają klasę obok stałego haka `.kpt-button__icon`.
20
+
21
+ `loading` podmienia ikonę wiodącą na spinner, zamiast stawać obok niej — wcześniej dostawało się spinner,
22
+ ikonę i tekst w jednym rzędzie. Spinner bierze przy tym rozmiar ikony, więc etykieta nie przeskakuje
23
+ w momencie wejścia w ładowanie; w przycisku bez ikony spinner urósł z 1em do skali ikony i to jedyna
24
+ widoczna zmiana w istniejących użyciach.
25
+
26
+ Doszło pięć wariantów: `elevated` (cień z `--kpt-shadow-sm`, na hover `md`), `link` (podkreślenie,
27
+ przezroczyste tło, zero paddingu bocznego — inaczej niż `text`, który ma tło na hover) oraz `success`,
28
+ `warning` i `info`. Tokeny semantyczne na te trzy statusy istniały w bibliotece od dawna, tylko przycisk
29
+ po nie nie sięgał. Przy okazji `danger` przestał liczyć kolory `color-mix` w SCSS i dostał własne tokeny
30
+ komponentowe, jak każdy inny wariant.
31
+
32
+ Skala rozmiarów rozszerza się o `xs` i `xl`; `md` zostaje bez zmiany.
33
+
34
+ `href` renderuje `<a class="kpt-button">` zamiast `<button>` — to domyka jedyną asymetrię portów w tym
35
+ komponencie, bo React miał `asChild`, a Angular nie miał nic i `<a>` ze stylem przycisku wymagał własnego
36
+ CSS-a (z podkreśleniem z resetu w gratisie). Wyłączony odnośnik traci `href`, a nie tylko dostaje atrybut,
37
+ więc nie da się go kliknąć ani wejść na niego Tabem; dostaje też `aria-disabled` i `tabindex="-1"`.
38
+ `target="_blank"` bez jawnego `rel` dostaje `rel="noopener noreferrer"`. W React `asChild` ma pierwszeństwo
39
+ przed `href`. Wygląd wyłączenia bierze się teraz z `:is(:disabled, [aria-disabled='true'])`, bo `<a>` nie
40
+ matchuje `:disabled`.
41
+
42
+ `KptButtonGroup` renderował wyłącznie `{{ opt.label }}`, choć segmentowany przełącznik — widok listy czy
43
+ siatki, wyrównanie tekstu — jest najbardziej „ikonowym" komponentem rodziny. `KptOption` dostaje
44
+ opcjonalne pole `icon`, a grupa je rysuje w tej samej skali co przycisk. Nowe wejście `iconOnly` chowa
45
+ etykiety i przenosi je do `aria-label` oraz `title`, a pozycje robią się kwadratami.
46
+
47
+ Rdzeń ikon rośnie o trzy codzienne akcje formularza i paska narzędzi: `device-floppy`, `arrow-back-up`
48
+ i `printer` (95 wbudowanych zamiast 92).
49
+
50
+ Jedna zmiana do odnotowania przy aktualizacji: `.kpt-button__content` jest teraz kontenerem flex.
51
+ Treść złożona wyłącznie z tekstu i ikon wygląda tak samo albo lepiej, ale etykieta mieszająca tekst
52
+ z własnym znacznikiem inline (`Tekst <span>x</span>`) dostanie między nimi odstęp z `--kpt-button-gap`.
53
+
54
+ - 120d1b0: `KptDialog` dostaje szerokość, a jego stopka przestaje ucinać przyciski.
55
+
56
+ Okno miało dotąd jedną szerokość — `min(32rem, calc(100vw - 2rem))` — wpisaną na sztywno w arkusz,
57
+ bez żadnego wejścia, które by ją zmieniło, w obu portach. 512 px wystarcza na pytanie „usunąć?"
58
+ i dwa przyciski, ale nie na treść, której nie da się sensownie zwęzić: kod, tabelę, porównanie
59
+ dwóch kolumn. Jedyną drogą było sięgnięcie z aplikacji do wnętrza komponentu selektorem, czyli
60
+ dokładnie to, czego zabrania kontrakt bloków.
61
+
62
+ Nowe wejście `size`: `sm` 24rem, `md` 32rem, `lg` 48rem. Domyślne `md` to **dzisiejsza wartość**,
63
+ więc żadne istniejące okno nie zmienia szerokości. Przycięcie do okna przeglądarki obowiązuje
64
+ w każdym rozmiarze, bo to ono, a nie stała, decyduje na telefonie. Szerokość idzie przez prywatne
65
+ `--_width`, zgodnie z kontraktem komponentu — warianty przestawiają jedną liczbę zamiast powtarzać
66
+ całe `min()`.
67
+
68
+ **Stopka zawija.** `[kptDialogFooter]` dostaje `flex-wrap: wrap` i to jest naprawa błędu, nie
69
+ kosmetyka. Przeglądarka daje `<dialog>` własny kontener przewijania (`overflow: auto` z jej
70
+ arkusza), więc przyciski, które nie mieszczą się w szerokości okna, **nie wystają — zostają
71
+ ucięte**. Cztery przyciski w oknie `sm` wystarczały, żeby pierwszy z nich stał się nieczytelny
72
+ i nie do klikniecia, bez żadnego sygnału, że coś jest nie tak. Teraz przechodzą do drugiego rzędu.
73
+
74
+ Typ `KptDialogSize` eksportowany z obu portów. Arkusz jest wspólny, więc `@konce-pt/styles/components`
75
+ niesie warianty dla Reacta bez osobnego kodu — stąd ta paczka na liście.
76
+
77
+ - 269238e: `KptProgress` pokazuje wartość, buforuje i porządnie się kręci.
78
+
79
+ Pasek postępu mówił dotąd wyłącznie geometrią. Użytkownik widział, że jest „gdzieś koło trzech
80
+ czwartych", ale nie wiedział, czy to 73% czy 78%, ani ile z ilu. Doszedł obszar etykiety: `showValue`
81
+ go otwiera, a `valueFormat` decyduje o treści — `percent` daje `80%`, `fraction` składa `152 / 160`
82
+ z `value` i `max`, `none` zostawia pusto. Oba napisy idą przez słownik (`progress.percent`,
83
+ `progress.fraction`), więc nowy język może im zmienić kształt. `label` dokłada tekst opisowy obok
84
+ paska, a `labelPosition` sadza wiersz nad paskiem, pod nim albo w tej samej linii.
85
+
86
+ Rzutowana treść (`ng-content` / `children`) zastępuje sformatowaną wartość zamiast się do niej
87
+ dokładać — tak w środku koła staje duża liczba nad podpisem, zamiast samego procentu.
88
+
89
+ Skala przestała być zaszyta: `min` i `max` opisują dowolny zakres, a wartości spoza niego są
90
+ przycinane, nie odrzucane. Domyślne 0–100 zachowują dotychczasowe zachowanie co do znaku.
91
+
92
+ `bufferValue` rysuje drugi, przygaszony pasek za głównym — pobrane, ale jeszcze nieodtworzone.
93
+ Bufor nigdy nie cofa się za pasek główny, bo wyglądałby wtedy jak błąd renderowania.
94
+
95
+ `size` (`sm`/`md`/`lg`) steruje grubością szyny i szerokością łuku; `md` to dotychczasowa grubość,
96
+ więc istniejące paski wyglądają tak samo.
97
+
98
+ Tryb nieokreślony dostał własne klatki animacji zamiast pożyczonych z `kpt-spinner` — pasek liniowy
99
+ rozciąga się i kurczy w przebiegu, łuk kołowy obraca się razem z pulsującą długością. Przy
100
+ `prefers-reduced-motion: reduce` animacja zwalnia, zamiast się zatrzymać: zamrożony wskaźnik postępu
101
+ czyta się jak zawieszona aplikacja. Zniknął też hack `width: 40% !important` — nieokreślony pasek po
102
+ prostu nie dostaje już szerokości z szablonu.
103
+
104
+ Dostępność: doszły `aria-valuemin` i `aria-valuemax`, przy `valueFormat="fraction"` także
105
+ `aria-valuetext` (czytnik mówi „152 / 160", a nie odczytuje surowej liczby jako procentu), widoczna
106
+ wartość ma `aria-hidden`, a nazwa dostępna bierze się z `label` albo ze słownika.
107
+
108
+ Nowe tokeny komponentowe: `--kpt-progress-track-bg`, `--kpt-progress-thickness-sm|md|lg`,
109
+ `--kpt-progress-buffer-opacity`, `--kpt-progress-label-fg`, `--kpt-progress-value-fg`,
110
+ `--kpt-progress-gap`.
111
+
112
+ Zmiana w DOM: `data-variant` przeniósł się z elementu wewnętrznego na hosta, gdzie dołączyły mu
113
+ `data-type`, `data-size` i `data-label-position`. Arkusz dostał to samo przepięcie, więc styl
114
+ biblioteczny działa bez zmian — ale aplikacja celująca własnym CSS-em w `.kpt-progress__linear[data-variant]`
115
+ musi przepiąć selektor na hosta.
116
+
117
+ ### Patch Changes
118
+
119
+ - 457112a: Stonowane tła liczone z koloru akcentowego przestają wpadać w róż.
120
+
121
+ Siedem miejsc w bibliotece mieszało barwę akcentową z powierzchnią przez `color-mix(in oklch, …)`.
122
+ OKLCH interpoluje **odcień**, a barwa achromatyczna też ma odcień — biel to `oklch(1 0 0)`, czyli
123
+ kąt 0. Zmieszanie 10% błękitu (255°) z bielą szło więc po kole odcieni od 0 w stronę 255 i przy tak
124
+ małym udziale zatrzymywało się na 349,5° — różu. Tak samo zieleń (150°) wychodziła na 15°,
125
+ a bursztyn (60°) na 6°.
126
+
127
+ Skutek był widoczny gołym okiem i mylący: **wszystkie cztery warianty `kpt-alert` miały to samo
128
+ różowawe tło**, mimo że kreska i ikona zachowywały swoją barwę. Wariant przestawał cokolwiek
129
+ znaczyć. To samo dotyczyło tła wybranego chipa, strefy upuszczania pliku, zaznaczenia w roadmapie
130
+ i stanu hover przycisku ikonowego w wariancie niebezpiecznym.
131
+
132
+ Naprawa to zmiana przestrzeni na `oklab` — ta sama przestrzeń percepcyjna, ale we współrzędnych
133
+ prostokątnych, więc mieszanie nie przechodzi przez kąt i barwa achromatyczna niczego nie ciągnie.
134
+ Po zmianie tła niosą odcień swojego akcentu: info 255°, success 150°, warning 60°, danger 27°.
135
+ Idiom jest w repozytorium ustalony — `auth.component.scss` używał `in oklab` od początku, a te
136
+ siedem miejsc było odstępstwem.
137
+
138
+ Pozostałe 54 wystąpienia `color-mix(in oklch, …)` zostają bez zmian i **są poprawne**: mieszają
139
+ z `transparent`, a przy przezroczystym końcu CSS premnaża alfę, więc kanały barwy nie są
140
+ interpolowane i odcień przeżywa.
141
+
142
+ - Updated dependencies [45b2f99]
143
+ - Updated dependencies [f867af2]
144
+ - Updated dependencies [269238e]
145
+ - @konce-pt/icons@0.9.0
146
+ - @konce-pt/backdrop@0.9.0
147
+ - @konce-pt/i18n@0.9.0
148
+ - @konce-pt/chart@0.9.0
149
+ - @konce-pt/chat@0.9.0
150
+ - @konce-pt/datetime@0.9.0
151
+ - @konce-pt/map@0.9.0
152
+ - @konce-pt/mention@0.9.0
153
+ - @konce-pt/roadmap@0.9.0
154
+ - @konce-pt/table@0.9.0
155
+
156
+ ## 0.8.5
157
+
158
+ ### Minor Changes
159
+
160
+ - f615e93: Nowe wejście `adaptive` w `KptBackdrop`: barwy wpisane na sztywno przenoszą się na powierzchnię
161
+ bieżącego motywu.
162
+
163
+ Literał z założenia stoi w miejscu i tak ma być — `#5b8af2` to barwa marki, a nie sugestia.
164
+ Ale sekcja z `color="#000000"` na `background="#ffffff"` w motywie ciemnym zostawała białą kartą
165
+ wyciętą w ciemnej stronie, razem z treścią dobraną do tamtego tła. Do tej pory jedynym wyjściem
166
+ było przepisanie wejść na tokeny; teraz wystarczy `[adaptive]="true"`.
167
+
168
+ Adaptacja przenosi `color`, `colors` i `background` na powierzchnię bieżącego motywu, zachowując
169
+ odległość każdej barwy od tej powierzchni: czerń dobrana do bieli wychodzi bielą na tle
170
+ `neutral-950`, a białe tło wychodzi w jasności samej powierzchni. Rusza wyłącznie jasność — `a`
171
+ i `b` w OKLab zostają nietknięte, więc odcień i nasycenie przeżywają i barwa marki nie zmienia się
172
+ w inną barwę, tylko dobiera jasność do tła. Jasność wypychająca wynik poza sRGB zostaje przycięta
173
+ w drodze powrotnej, co kosztuje około trzech stopni odcienia i dziesiątą część nasycenia; za tłem
174
+ sekcji to niewidoczne, a własne mapowanie gamutu byłoby kosztem bez pokrycia.
175
+
176
+ Rusza wyłącznie literały. Puste wejście bierze token, a zapis przez `var()` też sięga po token —
177
+ jedno i drugie już idzie za motywem i adaptacja odwróciłaby je drugi raz, czyli z powrotem.
178
+ Punktem odniesienia jest motyw jasny, bo biblioteka trzyma jasny w `:root`, a ciemny jako
179
+ nadpisanie; to jedyne założenie, jakie ta zamiana robi.
180
+
181
+ W rdzeniu doszły `adaptLightness()` i `adaptToSurface()` — czysta arytmetyka z testami
182
+ `node --test` — oraz `readBackdropSurface()` i `adaptBackdropValue()` spinające je z drzewem.
183
+ Strażnik przebudowy liczy teraz kolor wiodący tą samą drogą co budowa; inaczej porównywałby
184
+ wartość sprzed adaptacji z zapamiętaną po adaptacji i przebudowywał renderer bez końca.
185
+
186
+ - f615e93: Sekcja z własnym tłem dobiera kolor treści do tego tła i ogłasza ton atrybutem `data-tone`.
187
+
188
+ Host malował `--kpt-color-surface` i zostawiał treści `--kpt-color-on-surface`. To jest słuszne,
189
+ dopóki sekcja nie dostanie własnego `background` — wtedy ten token opisuje stronę dookoła, a nie
190
+ to, na czym treść naprawdę leży. Sekcja z ciemnym tłem w motywie jasnym miała `color` prawie czarny
191
+ na prawie czarnym tle, czyli treść niewidoczną. Każdy konsument musiał to obchodzić sam; demo
192
+ playgroundu obchodziło, licząc jasność tła po swojej stronie, więc błąd nie był widoczny akurat
193
+ tam, gdzie się go szuka.
194
+
195
+ Teraz komponent liczy to sam: `color` na hoście idzie pod tło, które sekcja naprawdę maluje,
196
+ a wynik wychodzi na zewnątrz jako `data-tone` (`light` przy jasnym tle, `dark` przy ciemnym).
197
+ Napis bierze kolor dziedziczeniem, a to, czemu sam kolor tekstu nie wystarcza — przyciski i inne
198
+ komponenty w sekcji — podwiesza własne tokeny pod atrybut:
199
+
200
+ :where(kpt-backdrop[data-tone='light']) .hero { --kpt-button-filled-bg: #15181a; }
201
+
202
+ Ton liczy się po adaptacji, więc pod `adaptive` zgadza się z tłem malowanym, a nie z wpisanym
203
+ w szablonie. Sekcja bez własnego `background` nie ogłasza nic i treść zostaje na
204
+ `--kpt-color-on-surface` — dla niej nic się nie zmienia.
205
+
206
+ O kontraście rozstrzyga jasność względna WCAG (`relativeLuminance`, `prefersDarkContent`
207
+ w rdzeniu, z testami), a nie jasność OKLab: pierwsza mówi, ile z barwy wychodzi światła, druga jak
208
+ jasno barwa wygląda, i tylko na pierwszej stoi rachunek kontrastu.
209
+
210
+ Playground dostał przełącznik „Idź za motywem" dla `adaptive` i przestał liczyć jasność tła po
211
+ swojej stronie — podpina się pod `data-tone` komponentu, więc jedna polityka zamiast dwóch.
212
+
213
+ - f615e93: Rampa `colors` w `KptBackdrop` przyjmuje dowolny zapis CSS, nie tylko heksadecymalny.
214
+
215
+ Wstęga miesza barwy po biegunowej postaci OKLab, ale dostawały się tam dotąd wyłącznie przystanki
216
+ sparsowane przez `parseHexColor` — czyli `#rgb` i `#rrggbb`. `oklch()`, `color-mix()`, `rgb()`,
217
+ nazwy własne i `var(--kpt-*)` wypadały z rampy, a gdy wypadły wszystkie, renderer schodził do
218
+ `IDLE` i **kadr zostawał pusty bez żadnego sygnału**. Tym dotkliwiej, że rampa domyślna — ta,
219
+ której się nie podaje — od początku szła przez `readCssColor` i rozumiała każdy zapis, bo tokeny
220
+ biblioteki są zapisane w OKLCH. Podana rampa miała gorszy parser niż niepodana.
221
+
222
+ Teraz obie idą tą samą drogą: o znaczenie każdego przystanku pytamy przeglądarkę, a do renderera
223
+ trafia już sam hex — jego kontrakt się nie zmienia. Przystanek, którego przeglądarka nie przyjmuje,
224
+ wypada z rampy; gdy wypadną wszystkie, wchodzi rampa wyprowadzona z koloru wiodącego, zamiast gasić
225
+ sekcję. Najczęstsza pomyłka, `var(--kpt-color-primary)` w rampie, i tak celuje w ten właśnie kolor,
226
+ więc użytkownik dostaje niemal to, o co mu chodziło, plus jedno ostrzeżenie w konsoli w buildzie
227
+ dev. Wartość alfa wypada — tablica barw wstęgi jest RGB.
228
+
229
+ `readCssColor()` zwraca `null` na wartości, której przeglądarka nie uznaje za kolor. Dotąd dawała
230
+ czerń, bo `fillStyle` odrzuca taki zapis po cichu i zostaje przy poprzednim — nie dało się odróżnić
231
+ śmiecia od legalnej czerni ani o czymkolwiek ostrzec. Rozstrzyga to para różnych wartowników: przy
232
+ wartości nieprawidłowej każdy zostaje na swoim miejscu i oba przebiegi się rozchodzą. Przy okazji
233
+ sonda jest czyszczona przed każdym odczytem — barwa z kanałem alfa mieszała się z tym, co zostawiło
234
+ poprzednie wywołanie.
235
+
236
+ - f615e93: Wejścia koloru `KptBackdrop` rozumieją `var()`, więc barwa podana w szablonie może iść za motywem.
237
+
238
+ Kolor wiodący był dotąd brany z wejścia bez zmian, a sonda 1×1 jest oderwana od drzewa — zapis
239
+ `color="var(--kpt-color-primary)"` nie miał prawa zadziałać i lądował w ostrzeżeniu. Zamrożenie
240
+ literału jest poprawne (`#5b8af2` ma zostać `#5b8af2`), ale odcinało jedyny zapis, który sam
241
+ wiedziałby, co zrobić przy przełączeniu motywu: wyrażenie odwołujące się do tokenów.
242
+
243
+ Doszedł `resolveCssValue()`: wyrażenie trafia do własności-sondy na elemencie w drzewie, a z jego
244
+ wyliczonego stylu wraca już z podstawionym `var()`. Dalej idzie do `readCssColor`, bo `color-mix()`
245
+ zostaje w custom property niewyliczone, a canvas je wylicza. Dzięki temu
246
+
247
+ <kpt-backdrop color="color-mix(in oklab, #5b8af2, var(--kpt-color-surface) 30%)">
248
+
249
+ daje kwadraciki, które jaśnieją na jasnej powierzchni i ciemnieją na ciemnej, przy jednej wartości
250
+ w szablonie — przeliczane istniejącym już nasłuchem motywu. To samo działa dla przystanków rampy
251
+ `colors`. Biblioteka nie zgaduje przy tym, jaki kolor jest „odpowiedni" w drugim motywie: decyduje
252
+ autor wyrażenia. Zapis bez `var()` omija całą ścieżkę, więc literał nie płaci za nic.
253
+
254
+ Sonda musi pisać poza zasięgiem nasłuchu motywu, bo ten ogląda `style` hosta i jego przodków —
255
+ zapis na hoście budziłby nasłuch, ten sięgałby po kolor na nowo i pętla nie miałaby końca. Stąd
256
+ `resolveBackdropColors()` i `readBackdropLead()` dostają teraz canvas komponentu, a nie hosta;
257
+ tokeny czytają się z niego tak samo, bo `--_dot` dziedziczy się w dół.
258
+
259
+ Nazwa zmiennej, która nic nie daje, schodzi przy `color` do tokenu — tego chce autor literówki —
260
+ a przy `colors` wypada z rampy i trafia do istniejącego ostrzeżenia w buildzie dev.
261
+
262
+ - c1f8885: Nowe animowane tło `KptBackdrop` (`kpt-backdrop`) i pod nim nowa paczka `@konce-pt/backdrop` —
263
+ rdzeń pure-TS z testami `node --test`. Komponent jest opakowaniem sekcji: canvas leży pod spodem,
264
+ rzutowana treść warstwę wyżej i zachowuje się zwyczajnie, więc pod hero z nagłówkiem i przyciskami
265
+ nie trzeba układać niczego ręcznie.
266
+
267
+ Efekt `signal-grid` to drobna siatka kwadratów, które cicho migoczą — gęsta przy krawędzi wskazanej
268
+ przez `anchor`, wygasająca do pustki przy przeciwnej. Gradient bierze się z tego, **ile** kwadratów
269
+ się pali, a nie z przezroczystości nałożonej na równomierną siatkę: to różnica między czymś, co
270
+ czyta się jak sygnał, a fakturą z maską na wierzchu. Sterują tym `density` (udział zapalonych
271
+ komórek przy kotwicy) i `falloff` (jak szybko to gaśnie), a rozstawem `cell` i `gap`.
272
+
273
+ Każda komórka wylicza swoje losowania — czy się pali, fazę i tempo migotania — z własnej kolumny
274
+ i wiersza przez `kptHash01()`. Siatka nie może trzymać stanu między klatkami, bo przy zmianie
275
+ rozmiaru komórki wędrują i tablica `Math.random()` rozsypałaby cały obraz; przy losowaniu
276
+ z pozycji układ przeżywa zmianę rozmiaru okna zamiast tasować się od nowa. Tempa są rozstrzelone
277
+ w przedziale 0,45–1,55, bo przy jednym tempie cała siatka pulsuje zgodnie i zamiast migotania
278
+ wychodzi oddech. Siatka celowo wychodzi poza obszar, a nadmiar jest ucinany — siatka kończąca się
279
+ przed krawędzią wygląda na uciętą.
280
+
281
+ Rysowanie stoi, gdy nie ma czego oglądać: sekcja poza ekranem (`IntersectionObserver`), karta w tle,
282
+ ustawione `paused`, `speed` równe zeru albo `prefers-reduced-motion`, o który komponent pyta sam.
283
+ Zostaje wtedy jedna statyczna klatka ze zróżnicowaną jasnością, nie pustka — efekt jest częścią
284
+ kompozycji, nie ozdobą. `speed` wchodzi w zegar (`clock += krok * speed`), a nie w fazę, więc suwak
285
+ prędkości przyspiesza obraz zamiast go przeskakiwać.
286
+
287
+ Kolor kwadratów bierze się z `--kpt-color-primary`, tło z `--kpt-color-surface`; oba da się nadpisać
288
+ wejściami `color` i `background` albo własnościami `--kpt-backdrop-color` i `--kpt-backdrop-surface`,
289
+ bo sekcja promocyjna bywa w kolorach spoza palety aplikacji. Canvas nie rozwija `var()`, więc token
290
+ odczytuje się z wyliczonego stylu hosta i na nowo po zmianie motywu. Canvas nosi `aria-hidden="true"`
291
+ i `pointer-events: none` — to dekoracja bez treści i bez niczego do trafienia.
292
+
293
+ Rdzeń (`buildSignalGrid`, `signalGridAlpha`, `signalGridDensity`, `kptHash01`) jest re-eksportowany
294
+ z `@konce-pt/angular`, więc aplikacja nie instaluje paczki samodzielnie, a ten sam efekt da się
295
+ narysować własnym rendererem. Port Reacta dostanie komponent osobno.
296
+
297
+ - c1f8885: Drugi efekt tła — `wave` — i `KptBackdrop` w porcie Reacta.
298
+
299
+ `wave` to jedna miękka wstęga światła płynąca przez kadr, z barwami przesuwającymi się wzdłuż niej
300
+ i bez twardej krawędzi w całym obrazie. Linia środkowa jest sumą harmonicznych o niewspółmiernych
301
+ częstotliwościach, więc prąd faluje bez powtarzalnego wzoru; jasność opada od niej krzywą Gaussa
302
+ i to opadanie odróżnia prąd światła od paska. Nowe wejścia: `colors` (rampa barw), `rotation`
303
+ (kąt, pod jakim prąd przecina kadr — odpowiednik `anchor` z signal-grid, tyle że płynny), `band`
304
+ i `amplitude`. Wejście nienależące do bieżącego efektu po prostu nie jest czytane.
305
+
306
+ Rampa barw jest zamknięta w pętlę i mieszana po **biegunowej** postaci OKLab. Interpolacja sRGB
307
+ miesza wartości po gammie i wychodzi błotem, a nawet prostokątny OKLab przecina oś achromatyczną
308
+ dla przeciwnych odcieni: w połowie drogi z niebieskiego w żółty zostaje szarość dokładnie tam,
309
+ gdzie prąd świeci najmocniej. Odcień idzie krótszym łukiem, a przystanek bez nasycenia przejmuje
310
+ odcień sąsiada, bo `atan2` na zerowym nasyceniu zwraca kąt przypadkowy. Bez podanego `colors` rampą
311
+ jest `--kpt-color-primary` i dwie barwy wyprowadzone z niego obrotem odcienia — paleta biblioteki
312
+ jest neutralna, więc sięganie po tokeny statusowe do celów dekoracyjnych byłoby użyciem semantyki
313
+ nie na miejscu.
314
+
315
+ Wstęga liczy się w buforze o dłuższym boku 160 px i jest skalowana w górę: miękkość bierze się
316
+ z interpolacji przeglądarki, więc kilka tysięcy pikseli na klatkę obsługuje kadr dowolnej
317
+ wielkości. Krótszy bok ma osobną podłogę 72 próbek — przy proporcjonalnym skalowaniu szeroki kadr
318
+ miałby kilkadziesiąt wierszy i prąd obrócony blisko kąta prostego łamałby się właśnie wzdłuż tej
319
+ osi.
320
+
321
+ **Renderery przeniosły się do `@konce-pt/backdrop`.** Rysowanie nie potrzebuje frameworka, tylko
322
+ `CanvasRenderingContext2D`, więc `createBackdropRenderer()` (plus `resolveBackdropColors()`
323
+ i stałe cyklu życia) mieszka teraz w rdzeniu, a komponent w obu portach jest samym cyklem życia:
324
+ rAF, `ResizeObserver`, `IntersectionObserver`, zapytanie o ograniczony ruch i obserwator motywu.
325
+ Te same wejścia dają dzięki temu tę samą klatkę w Angularze i w Reakcie. Doszedł też
326
+ `readCssColor()` — tokeny są zapisane w OKLCH, a rampę trzeba wymieszać, nie tylko podać dalej,
327
+ więc o znaczenie zapisu pytamy przeglądarkę, malując kolor na canvasie 1×1.
328
+
329
+ `@konce-pt/react` dostaje `KptBackdrop` z kompletem propsów obu efektów, ten sam DOM i ten sam
330
+ arkusz `components.css`. Zmianę `colors` śledzi po treści tablicy, nie po jej tożsamości, więc
331
+ nowa tablica z renderu rodzica nie przebudowuje renderera.
332
+
333
+ - ce34ab3: Nowa rodzina `KptChat` w subpathu `@konce-pt/angular/chat`: `kpt-chat` (lista rozmowy),
334
+ `kpt-chat-message` (bąbel), `kpt-chat-typing` (wskaźnik pisania), `kpt-chat-composer` (pole
335
+ pisania) i `kpt-chat-dock` (pływający panel w rogu, zbudowany na `kpt-fab`). Subpath, nie rdzeń —
336
+ aplikacje bez czatu nie mają powodu pobierać docka razem z logiką breakpointów.
337
+
338
+ Nowa paczka `@konce-pt/chat` — rdzeń pure-TS z testami `node --test`. `buildChatRows()` zamienia
339
+ płaską listę wiadomości na wiersze do wyrenderowania: separatory dni, dzielnik nieprzeczytanych
340
+ i pozycję w grupie, z której wynika zapadnięty narożnik, ogonek, awatar i wiersz z godziną.
341
+ Grupa łamie się na zmianie autora, przerwie powyżej pięciu minut, granicy doby kalendarzowej,
342
+ wiadomości systemowej i dzielniku nieprzeczytanych. Doba liczona jest od północy do północy,
343
+ a nie odejmowaniem znaczników czasu — doba ze zmianą czasu ma 23 albo 25 godzin, więc „wczoraj
344
+ o 23:30" wypadałoby raz do roku o dzień obok.
345
+
346
+ Nieudana wysyłka umie o sobie powiedzieć: `status: 'failed'` obwodzi bąbel kolorem
347
+ `--kpt-color-danger-border` i stawia strzałkę ponowienia **poza** nim, po zewnętrznej stronie
348
+ listy. Taki bąbel pokazuje wiersz meta nawet w środku grupy — strzałka bez niczego, co ją
349
+ tłumaczy, byłaby zagadką. Pasek `[offline]` liczy wiadomości czekające na wysłanie przez
350
+ `pendingCount()`. Potwierdzenia z serwera przychodzą nie po kolei, więc `mergeStatus()` zostawia
351
+ wyższy stopień: spóźnione „dostarczone" nie cofnie `read`. Wyjątkiem jest `failed`, który wygrywa
352
+ zawsze — wiadomość odrzucona po wysłaniu nie może zostać na zawsze „wysłana".
353
+
354
+ Transport zostaje w aplikacji: komponent przyjmuje wiadomości i tylko zgłasza `(retry)`
355
+ oraz `(loadMore)`.
356
+
357
+ Bąble są rozłożone pod monochromatyczny motyw biblioteki: różnicę robi jasność, nie odcień.
358
+ Wiadomość przychodząca siada na `--kpt-color-muted`, własna domyślnie na pełnym
359
+ `--kpt-color-primary`. `--kpt-color-surface-variant` dzieli od tła listy 1,5% jasności, więc bąbel
360
+ przychodzący na nim po prostu znikał, a `--kpt-color-primary-subtle` ma dokładnie jasność
361
+ `--kpt-color-muted`, przez co własny i cudzy bąbel były nie do odróżnienia. Wariant `primary`
362
+ zostaje jako cichsza opcja dla własnych wiadomości; wariant `muted` zniknął, bo po tej zmianie
363
+ powielał `neutral`. Przyklejona pigułka separatora dnia przejeżdża nad wiadomościami, więc dostała
364
+ podniesione tło, ramkę i cień — bez nich zlewała się z bąblem, po którym właśnie jechała.
365
+
366
+ `kpt-scroll-top` przykleja się dokładnie w to samo miejsce co dock, z tym samym `--kpt-z-overlay`,
367
+ i połykał FAB czatu w całości — wystawała sama plakietka nieprzeczytanych, a przycisku nie dało się
368
+ kliknąć. Arkusz rozwiązuje to teraz sam: przy docku `bottom-end` przycisk powrotu na górę ustępuje
369
+ w bok o szerokość FAB-a. W bok, nie do góry, bo nad FAB-em stoi panel rozmowy — przesunięcie
370
+ w pionie zamieniłoby jedną kolizję na gorszą. Do tego `kpt-chat-dock` dostaje `offset` na wypadek
371
+ innych widgetów w tym rogu.
372
+
373
+ Słownik `@konce-pt/i18n` dostaje sekcję `chat` (EN i PL). Rdzeń zwraca **rodzaj** etykiety dnia
374
+ (`{ kind: 'today' }`), a nie napis — brzmienie należy do słownika, nazwa dnia tygodnia do `Intl`,
375
+ a paczka bez zależności nie ma po co wozić tłumaczeń.
376
+
377
+ Port Reacta dostaje subpath `@konce-pt/react/chat` z kompletem pięciu komponentów na tym samym
378
+ rdzeniu i tym samym arkuszu `components.css` — te same klasy `kpt-*` i te same atrybuty `data-*`,
379
+ więc oba porty renderują identycznie. `model()` z Angulara rozpada się na pary
380
+ controlled/uncontrolled (`value`/`defaultValue`/`onValueChange`, `open`/`defaultOpen`/`onOpenChange`),
381
+ a `<ng-template let-message>` na `renderMessage(message, row)`.
382
+
383
+ Zestaw wbudowany `@konce-pt/icons` rośnie z 86 do 92 ikon: `checks`, `paperclip`, `send`,
384
+ `message-circle`, `corner-up-left`, `mood-smile`. Bez `checks` nie dałoby się odróżnić
385
+ „dostarczono" od „wysłano", a komponenty biblioteki muszą działać bez `provideKptTablerIcons()`.
386
+
387
+ - 7626e71: `KptChip` dostaje wejście `textTransform` (`'none' | 'capitalize' | 'uppercase' | 'lowercase'`,
388
+ domyślnie `'none'`) w obu portach. Ląduje jako `data-text-transform` na `.kpt-chip` i tam ustawia
389
+ `text-transform`, więc etykieta zostaje w treści zapisana po swojemu, a o sposobie jej wyświetlenia
390
+ decyduje chip — pigułka nad nagłówkiem hero wygląda tak samo niezależnie od tego, czy tekst
391
+ przyszedł ze słownika, z CMS-a czy z API.
392
+
393
+ `capitalize` podnosi pierwszą literę każdego słowa — **także po dywizie** („Agent-Ready"), bo CSS
394
+ nie wie, gdzie kończy się wyraz złożony. Przy etykietach z myślnikiem pewniejszy jest zapis wprost
395
+ w treści; to samo ograniczenie ma każde `text-transform: capitalize`, a nie ta implementacja.
396
+
397
+ - a1c5468: `KptDataView` dostaje `pageSizeOptions`, a rozmiar strony staje się sterowalny — w Angularze
398
+ `pageSize` to teraz `model`, w React dochodzą `defaultPageSize` i `onPageSizeChange`. Do tej pory
399
+ komponent podawał paginatorowi `[pageSize()]` jako jedyną opcję, więc select „Wierszy" był martwą
400
+ kontrolką: pokazywał liczbę, której nie dało się zmienić. Pager zostaje widoczny także wtedy, gdy
401
+ wszystko mieści się na jednej stronie, o ile jest w czym wybierać — inaczej wybranie największego
402
+ rozmiaru chowałoby select, którego użytkownik właśnie użył.
403
+
404
+ Pasek paginatora w `KptDataView` znów stoi w jednym rzędzie po prawej. Opakowanie ustawiało
405
+ `.kpt-data-view .kpt-paginator { display: block }`, a selektor potomka bije `.kpt-paginator
406
+ { display: flex }` z tej samej warstwy — trzy bloki paginatora rozpadały się na trzy linie.
407
+
408
+ `KptDataView` pozwala ustawić gęstość siatki: `gridColumns` (dwukierunkowe), `gridColumnsOptions`
409
+ i `gridMinWidth`. Przy więcej niż jednej opcji w układzie siatki pasek narzędzi dostaje drugą grupę
410
+ segmentową z ikoną `grid-dots`. Wybrana liczba jest sufitem, nie sztywną liczbą — minimum ścieżki
411
+ rośnie do szerokości jednej z N kolumn, więc wąski kontener schodzi niżej, zamiast ściskać karty
412
+ poniżej `gridMinWidth`. Do tej pory o gęstości decydował wyłącznie arkusz aplikacji, bo komponent
413
+ miał `minmax(10rem, 1fr)` wpisane na sztywno.
414
+
415
+ Przyciski przełącznika układu niosą wreszcie `aria-pressed` — czytnik ekranu nie ogłaszał, który
416
+ układ jest włączony.
417
+
418
+ Przycisk siatki w przełączniku układu rysuje `layout-grid` zamiast `menu`: hamburger to
419
+ semantycznie lista, nie siatka. `layout-grid` wchodzi do zestawu wbudowanego `KPT_ICONS`
420
+ (86 ikon, razem z `grid-dots` dla paska gęstości), bo komponent biblioteki musi działać bez `provideKptTablerIcons()`.
421
+
422
+ - 0daa856: Nowa karta podglądu `KptPeek` (`kpt-peek` + `kptPeekTrigger`) i na niej wzmianki w tekście:
423
+ `KptMention` (pojedyncza wzmianka) oraz `KptMentions` (akapit rozbity na treść i wzmianki).
424
+ Obie rzeczy w rdzeniu biblioteki, nie w subpathu — karta jest mała i przyda się wszędzie tam,
425
+ gdzie tooltip za mało niesie, a popover za dużo zasłania.
426
+
427
+ `kptTooltip` ma `pointer-events: none`, więc linku w jego środku nie da się kliknąć, a `kpt-popover`
428
+ otwiera dopiero klik i kładzie na stronie backdrop. `kpt-peek` stoi pomiędzy: hover otwiera kartę
429
+ po `openDelay`, zjazd kursora zamyka po `closeDelay` (najazd na kartę anuluje zamknięcie, bo kursor
430
+ musi mieć czas przejechać z triggera na kartę), a treść karty jest w pełni interaktywna i niczego
431
+ nie przykrywa. Fokus otwiera bez zwłoki, a klik, Enter albo dotyk **przypina** kartę: zostaje mimo
432
+ zjazdu kursora, a fokus wchodzi do środka. To jedyna droga do treści karty tam, gdzie hovera nie ma
433
+ w ogóle, więc interaktywna karta zostaje osiągalna na dotyku i z klawiatury. Escape zamyka i oddaje
434
+ fokus triggerowi.
435
+
436
+ Nowa paczka `@konce-pt/mention` — rdzeń pure-TS z testami `node --test`. `parseMentionText()`
437
+ rozbija akapit na treść i wzmianki (`@id` albo `@[Nazwa ze spacją](id)`), `mentionQuery()` mówi,
438
+ czy kursor stoi w zapytaniu `@…`, a `filterMentions()` dopasowuje podpowiedzi. Sklejenie segmentów
439
+ wraca do oryginału znak w znak — podgląd rozjeżdżający się z zapisaną treścią byłby gorszy niż brak
440
+ podglądu. `mail@example.com` wzmianką nie jest: `@` musi stać na początku tekstu albo po znaku,
441
+ który nie jest literą, cyfrą ani podkreśleniem. Dopasowanie zdejmuje diakrytyki, a litery z kreską
442
+ mapuje ręcznie — `ł` to osobny znak Unicode, nie `l` ze znakiem diakrytycznym, więc samo NFD
443
+ zostawiłoby „Łukasza" poza zasięgiem zapytania „lukasz".
444
+
445
+ W treści zapisuje się **identyfikator**, nie nazwa: `id` przeżyje zmianę nazwiska, a etykieta
446
+ bierze się z listy pozycji. Identyfikator spoza listy też się renderuje — etykietą zostaje wtedy on
447
+ sam, bo notatka ma dać się przeczytać także wtedy, gdy ktoś odszedł z zespołu. `kpt-mentions` nie
448
+ renderuje HTML-a: akapit wchodzi jako zwykły napis, a wzmianka jest elementem, który biblioteka sama
449
+ zbudowała — aplikacja nie musi przepuszczać przez sanitizer treści pisanej przez użytkowników tylko
450
+ po to, żeby wyszły z niej wzmianki. Bez szablonu karty wzmianka jest wyróżnionym słowem, a nie
451
+ przyciskiem: nie ma czego otworzyć, więc niczego nie udaje.
452
+
453
+ `kpt-rich-text` dostaje `mentions`, `mentionLimit` i `(mentioned)`: napisanie `@` otwiera listę
454
+ podpowiedzi zaczepioną w miejscu kursora, strzałki po niej chodzą, Enter albo Tab wstawia, Escape
455
+ zamyka. Zapytanie czytamy z węzła tekstowego przy kursorze, nie z HTML-a — w treści siedzą tagi,
456
+ a `@` ma widzieć tylko to, co naprawdę napisano w tym miejscu. Wstawiona wzmianka to
457
+ `<span class="kpt-mention" data-mention="id" contenteditable="false">` plus twarda spacja:
458
+ identyfikator przeżywa obieg przez wartość, `contenteditable="false"` trzyma wzmiankę w jednym
459
+ kawałku (bez tego kursor wchodzi w środek nazwiska), a spacja daje mu gdzie stanąć za blokiem,
460
+ w który nie da się wejść. Oba atrybuty weszły na allowlistę sanitizera w obu portach — są bierne,
461
+ nie wykonują niczego i nie wskazują zasobów.
462
+
463
+ `KptIconButton` w porcie Angulara dostaje `ariaExpanded` — jak `ariaPressed` musi zjechać na
464
+ wewnętrzny `<button>`, bo host nie ma roli przycisku. Słownik `@konce-pt/i18n` dostaje
465
+ `richText.mentions` (EN i PL) na etykietę listy podpowiedzi.
466
+
467
+ Port Reacta dostaje komplet: `KptPeek`, `KptMention`, `KptMentions` i te same trzy propsy
468
+ w `KptRichText`, na tym samym rdzeniu i tym samym arkuszu `components.css`. Propsy wyzwalacza
469
+ `KptPeek` lądują na samym elemencie podanym w `trigger` (klon z doklejonymi handlerami), więc
470
+ `aria-expanded` siedzi tam, gdzie czytnik ekranu naprawdę je przeczyta.
471
+
472
+ - `KptPickList` i `KptOrderList` dostają przeciąganie i dwuklik — w obu portach.
473
+
474
+ Nowe wejście `dragdrop` jest domyślnie wyłączone, tak samo jak `reorderable` w tabeli danych:
475
+ przeciąganie to zachowanie, na które trzeba się zgodzić, a nie takie, które ma się włączyć samo
476
+ komuś, kto tylko wyświetla listę. Włączone, porządkuje pozycje wewnątrz panelu, a w pick-liście
477
+ przenosi je także między panelami, na wskazane miejsce. Upuszczenie na pusty obszar listy dopina
478
+ element na koniec.
479
+
480
+ Upuszczony element ląduje **na pozycji tego, na który go upuszczono** — nie zamienia się z nim
481
+ miejscami. To ten sam wzór co przestawianie kolumn w tabeli; zamiana miejscami wygląda przy
482
+ dłuższej liście na przypadek, bo element skacze w dwa miejsca naraz.
483
+
484
+ Dwuklik w pick-liście przerzuca pojedynczą pozycję na drugą stronę, w obie strony i niezależnie
485
+ od zaznaczenia. Nie wymaga `dragdrop`, bo dwa kliknięcia pod spodem i tak się znoszą.
486
+
487
+ Pod spodem natywny HTML5 drag and drop, nie CDK. Port React ma mieć to samo zachowanie i ten sam
488
+ arkusz stylów, a dla CDK nie byłoby tam odpowiednika — jedna implementacja zamiast dwóch
489
+ rozjeżdżających się. Przyciski zostają pełną ścieżką z klawiatury, bo przeciąganie nie ma
490
+ klawiaturowego odpowiednika i bez nich sortowanie stałoby się niedostępne.
491
+
492
+ ### Patch Changes
493
+
494
+ - f615e93: `KptBackdrop` idzie za tokenami, a nie za samym atrybutem motywu.
495
+
496
+ Canvas nie rozwija `var()`, więc kolor wiodący jest odczytywany raz i zapiekany w rendererze —
497
+ zmianę tokenów trzeba zauważyć i odczytać je na nowo. Dotąd nasłuch obejmował `data-theme`
498
+ i `class` na `<html>`, co wystarczało na przełącznik jasny/ciemny i zostawiało trzy dziury:
499
+ nadpisanie tokenu inline (konfigurator marki ustawia `--kpt-color-primary` na `<html>` — reszta
500
+ strony się przemalowywała, tło zostawało na starej barwie), motyw zakresowy (`[data-theme="dark"]`
501
+ nie jest przywiązany do `:root`, więc niesie go dowolny przodek) oraz aplikację trzymającą ciemne
502
+ tokeny w media query zamiast w atrybucie.
503
+
504
+ Nasłuch idzie teraz po łańcuchu od hosta aż do `<html>` i patrzy u każdego przodka na `data-theme`,
505
+ `class` i `style`, a obok tego na zapytanie `prefers-color-scheme`. Szersza sieć łapie też zmiany
506
+ niemające nic wspólnego z barwą — klasa nakładki na `<html>` przy otwarciu dialogu, dowolny zapis
507
+ inline — więc renderer przebudowuje się dopiero wtedy, gdy odczyt naprawdę dał inny kolor. Sam
508
+ strażnik jest tańszy niż dawna bezwarunkowa przebudowa: jeden `getComputedStyle`, bez sondy 1×1
509
+ i bez arytmetyki OKLab.
510
+
511
+ W rdzeniu doszły `readBackdropLead()` (sam kolor wiodący, wydzielony z `resolveBackdropColors()`)
512
+ i `observeBackdropTheme()` (nasłuch razem z odpięciem). Oba porty wołają to samo, bo rozjazd
513
+ znaczyłby, że ta sama sekcja reaguje na motyw inaczej w Angularze niż w Reakcie.
514
+
515
+ - Updated dependencies [f615e93]
516
+ - Updated dependencies [f615e93]
517
+ - Updated dependencies [f615e93]
518
+ - Updated dependencies [f615e93]
519
+ - Updated dependencies [c1f8885]
520
+ - Updated dependencies [f615e93]
521
+ - Updated dependencies [c1f8885]
522
+ - Updated dependencies [ce34ab3]
523
+ - Updated dependencies [a1c5468]
524
+ - Updated dependencies [0daa856]
525
+ - @konce-pt/backdrop@0.8.5
526
+ - @konce-pt/chat@0.8.5
527
+ - @konce-pt/icons@0.8.5
528
+ - @konce-pt/i18n@0.8.5
529
+ - @konce-pt/mention@0.8.5
530
+ - @konce-pt/chart@0.8.5
531
+ - @konce-pt/datetime@0.8.5
532
+ - @konce-pt/map@0.8.5
533
+ - @konce-pt/roadmap@0.8.5
534
+ - @konce-pt/table@0.8.5
535
+
3
536
  ## 0.8.3
4
537
 
5
538
  ### Patch Changes