@konce-pt/react 0.8.2 → 0.8.5

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.
Files changed (53) hide show
  1. package/README.md +11 -4
  2. package/dist/backdrop/backdrop.d.ts +79 -0
  3. package/dist/backdrop/backdrop.js +195 -0
  4. package/dist/backdrop/llms.txt +132 -0
  5. package/dist/chat/chat.d.ts +53 -0
  6. package/dist/chat/chat.js +160 -0
  7. package/dist/chat/llms.txt +78 -0
  8. package/dist/chat-composer/chat-composer.d.ts +41 -0
  9. package/dist/chat-composer/chat-composer.js +81 -0
  10. package/dist/chat-composer/llms.txt +56 -0
  11. package/dist/chat-dock/chat-dock.d.ts +40 -0
  12. package/dist/chat-dock/chat-dock.js +67 -0
  13. package/dist/chat-dock/llms.txt +71 -0
  14. package/dist/chat-entry/public-api.d.ts +12 -0
  15. package/dist/chat-entry/public-api.js +10 -0
  16. package/dist/chat-message/chat-message.d.ts +53 -0
  17. package/dist/chat-message/chat-message.js +112 -0
  18. package/dist/chat-message/llms.txt +73 -0
  19. package/dist/chat-typing/chat-typing.d.ts +23 -0
  20. package/dist/chat-typing/chat-typing.js +30 -0
  21. package/dist/chat-typing/llms.txt +41 -0
  22. package/dist/chip/chip.d.ts +10 -1
  23. package/dist/chip/chip.js +3 -2
  24. package/dist/chip/llms.txt +9 -0
  25. package/dist/data-view/data-view.d.ts +15 -3
  26. package/dist/data-view/data-view.js +22 -5
  27. package/dist/data-view/llms.txt +20 -7
  28. package/dist/internal/dev.d.ts +1 -0
  29. package/dist/internal/dev.js +3 -0
  30. package/dist/internal/overlay.d.ts +6 -1
  31. package/dist/internal/overlay.js +5 -3
  32. package/dist/mention/llms.txt +60 -0
  33. package/dist/mention/mention.d.ts +38 -0
  34. package/dist/mention/mention.js +29 -0
  35. package/dist/mention/mentions.d.ts +31 -0
  36. package/dist/mention/mentions.js +24 -0
  37. package/dist/order-list/llms.txt +16 -5
  38. package/dist/order-list/order-list.d.ts +5 -3
  39. package/dist/order-list/order-list.js +69 -4
  40. package/dist/peek/llms.txt +42 -0
  41. package/dist/peek/peek.d.ts +35 -0
  42. package/dist/peek/peek.js +127 -0
  43. package/dist/pick-list/llms.txt +19 -5
  44. package/dist/pick-list/pick-list.d.ts +7 -2
  45. package/dist/pick-list/pick-list.js +123 -17
  46. package/dist/public-api.d.ts +13 -1
  47. package/dist/public-api.js +6 -0
  48. package/dist/rich-text/llms.txt +10 -0
  49. package/dist/rich-text/rich-text.d.ts +11 -1
  50. package/dist/rich-text/rich-text.js +157 -5
  51. package/dist/rich-text/sanitize.js +10 -1
  52. package/dist/roadmap/roadmap.js +1 -3
  53. package/package.json +16 -9
package/README.md CHANGED
@@ -169,9 +169,9 @@ Data table with sorting, filtering, pagination and a custom cell:
169
169
  | **Navigation** | `KptTabs`, `KptAccordion`, `KptBreadcrumb`, `KptStepper`, `KptMenu`, `KptMenubar`, `KptMegamenu`, `KptContextMenu` |
170
170
  | **Data** | `KptDataTable`, `KptPaginator`, `KptTree`, `KptTimeline`, `KptCarousel`, `KptDataView`, `KptPickList`, `KptOrderList`, `KptGalleria`, `KptMeterGroup` |
171
171
  | **Feedback & overlay** | `KptAlert`, `KptDialog`, `kptToast` + `KptToastContainer`, `KptTooltip`, `KptPopover`, `KptDrawer`, `KptBottomSheet`, `kptConfirm`, `KptBadge`, `KptChip`, `KptAvatar`, `KptAvatarGroup`, `KptSpinner`, `KptProgress`, `KptSkeleton`, `KptEmpty`, `KptImage`, `KptAuth` |
172
- | **Subpaths** | `@konce-pt/react/charts` (`KptChart`), `/roadmap` (`KptRoadmap`), `/grid` (`KptGrid`, `KptCol`, `KptFlex`), `/map` (`KptMap` + legend, POI card, search), `/icons` (full Tabler set) |
172
+ | **Subpaths** | `@konce-pt/react/charts` (`KptChart`), `/chat` (`KptChat`, `KptChatMessage`, `KptChatTyping`, `KptChatComposer`, `KptChatDock`), `/roadmap` (`KptRoadmap`), `/grid` (`KptGrid`, `KptCol`, `KptFlex`), `/map` (`KptMap` + legend, POI card, search), `/icons` (full Tabler set) |
173
173
 
174
- The heavy pieces (charts, roadmap, maps, the full icon set) live in subpaths on purpose, so the core
174
+ The heavy pieces (charts, chat, roadmap, maps, the full icon set) live in subpaths on purpose, so the core
175
175
  entry point stays small. Every component ships an `llms.txt` API sheet next to its source.
176
176
 
177
177
  ## Theming
@@ -275,6 +275,9 @@ the Angular port.
275
275
  - **`llms.txt`** — LLM-friendly API sheets per component, shipped inside the package; the
276
276
  package-wide sheet lives at [ui.konce.pt/llms/react/llms.txt](https://ui.konce.pt/llms/react/llms.txt)
277
277
  (PL mirror: [llms-pl.txt](https://ui.konce.pt/llms/react/llms-pl.txt)).
278
+ - **Security scanners** — Socket.dev and similar tools flag i18n keys as domain names
279
+ (`.email`, `.today` and friends are real TLDs) and the map's default endpoints; the
280
+ [project sheet](https://ui.konce.pt/llms.txt) explains each alert under "Security scanners".
278
281
  - Repository: [gitlab.com/konce-pt/koncept-ui](https://gitlab.com/konce-pt/koncept-ui)
279
282
 
280
283
  ## License
@@ -390,8 +393,8 @@ Angulara.
390
393
  ### Komponenty (80+)
391
394
 
392
395
  Formularze, przyciski i akcje, layout, nawigacja, dane (z flagową `KptDataTable`), feedback
393
- i overlay — pełna lista w tabeli powyżej. Ciężkie rzeczy (wykresy, roadmapa, mapy, pełny Tabler)
394
- siedzą w subpathach `@konce-pt/react/{charts,roadmap,grid,map,icons}`, żeby nie obciążać rdzenia.
396
+ i overlay — pełna lista w tabeli powyżej. Ciężkie rzeczy (wykresy, czat, roadmapa, mapy, pełny Tabler)
397
+ siedzą w subpathach `@konce-pt/react/{charts,chat,roadmap,grid,map,icons}`, żeby nie obciążać rdzenia.
395
398
  Każdy komponent ma obok źródła plik `llms.txt` z opisem API.
396
399
 
397
400
  ### Dokumentacja
@@ -401,6 +404,10 @@ Playground z demami: [ui.konce.pt/react](https://ui.konce.pt/react). Opisy API d
401
404
  [`llms-pl.txt`](https://ui.konce.pt/llms/react/llms-pl.txt) (PL). Repozytorium:
402
405
  [gitlab.com/konce-pt/koncept-ui](https://gitlab.com/konce-pt/koncept-ui).
403
406
 
407
+ Alerty skanerów łańcucha dostaw (Socket.dev i podobne) — klucze i18n czytane jak domeny
408
+ oraz domyślne endpointy mapy — omawia sekcja „Skanery bezpieczeństwa" w
409
+ [arkuszu projektu](https://ui.konce.pt/llms-pl.txt).
410
+
404
411
  ### Licencja
405
412
 
406
413
  MIT © konce.pt
@@ -0,0 +1,79 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import { type KptBackdropAnchor, type KptBackdropEffect } from '@konce-pt/backdrop';
3
+ export type { KptBackdropAnchor, KptBackdropEffect } from '@konce-pt/backdrop';
4
+ export interface KptBackdropProps extends ComponentPropsWithRef<'div'> {
5
+ /** Rysowany efekt. */
6
+ effect?: KptBackdropEffect;
7
+ /** Tło sekcji. Puste bierze `--kpt-color-surface`. */
8
+ background?: string;
9
+ /** Tempo; 1 to spokojne tempo obu efektów, 0 zatrzymuje obraz. */
10
+ speed?: number;
11
+ /** Ziarno losowania — ta sama wartość daje ten sam układ kwadratów i ten sam przebieg wstęgi. */
12
+ seed?: number;
13
+ /** Zatrzymuje animację, zostawiając ostatnią klatkę. */
14
+ paused?: boolean;
15
+ /**
16
+ * Przenosi barwy wpisane na sztywno na powierzchnię bieżącego motywu: czerń dobrana do białego
17
+ * tła staje się bielą, gdy tło jest ciemne. Rusza wyłącznie jasność — odcień i nasycenie
18
+ * zostają, więc barwa marki nie zmienia się w inną barwę.
19
+ *
20
+ * Dotyczy `color`, `colors` i `background`, ale tylko wtedy, gdy podano je literałem. Prop pusty
21
+ * bierze token, a zapis z `var()` sam po token sięga — jedno i drugie już idzie za motywem
22
+ * i adaptacja odwróciłaby je drugi raz.
23
+ */
24
+ adaptive?: boolean;
25
+ /**
26
+ * `signal-grid`: kolor kwadratów — dowolny zapis CSS, także sięgający po tokeny, np.
27
+ * `color-mix(in oklab, #5b8af2, var(--kpt-color-surface) 30%)`. Taki zapis przelicza się przy
28
+ * zmianie motywu; literał zostaje literałem. Puste bierze `--kpt-color-primary`.
29
+ */
30
+ color?: string;
31
+ /** `signal-grid`: przy której krawędzi siatka jest najgęstsza. */
32
+ anchor?: KptBackdropAnchor;
33
+ /** `signal-grid`: bok kwadratu (px). */
34
+ cell?: number;
35
+ /** `signal-grid`: odstęp między kwadratami (px). */
36
+ gap?: number;
37
+ /** `signal-grid`: udział zapalonych komórek przy kotwicy, 0–1. */
38
+ density?: number;
39
+ /** `signal-grid`: jak szybko siatka gaśnie ku przeciwnej krawędzi. */
40
+ falloff?: number;
41
+ /**
42
+ * `wave`: rampa barw, po której przesuwa się wstęga — każdy przystanek dowolnym zapisem CSS.
43
+ * Zamknięta w pętlę, więc ostatnia barwa wraca do pierwszej. Pusta bierze `--kpt-color-primary`
44
+ * i dwie barwy wyprowadzone z niego obrotem odcienia — paleta biblioteki jest neutralna, więc
45
+ * domyślna wstęga jest jednobarwna i przygaszona. Po kolory sięga się tu, nie do tokenów
46
+ * statusowych. Zapis może sięgać po tokeny i wtedy idzie za motywem. Przystanek, którego
47
+ * przeglądarka nie uznaje za kolor, wypada z rampy; gdy wypadną wszystkie, zostaje rampa
48
+ * domyślna.
49
+ */
50
+ colors?: readonly string[];
51
+ /** `wave`: kąt, pod jakim prąd przecina kadr (stopnie); 0 to poziomo w prawo. */
52
+ rotation?: number;
53
+ /** `wave`: szerokość wstęgi jako ułamek wysokości kadru. */
54
+ band?: number;
55
+ /** `wave`: jak mocno faluje linia środkowa. Zero zatrzymuje falowanie, nie barwy. */
56
+ amplitude?: number;
57
+ /** Propsy warstwy z treścią (`.kpt-backdrop__content`). */
58
+ contentProps?: ComponentPropsWithRef<'div'>;
59
+ }
60
+ /**
61
+ * Dekoracyjna warstwa tła dla sekcji: komponent obejmuje treść i maluje pod nią animowany canvas.
62
+ * Sięga się po niego tam, gdzie płaska powierzchnia wygląda na niedokończoną, a wideo albo duży
63
+ * obraz kosztowałyby transfer i nie poszłyby za motywem.
64
+ *
65
+ * Komponent jest opakowaniem: canvas leży pod spodem, a dzieci idą warstwę wyżej i zachowują się
66
+ * zwyczajnie. Rysowanie stoi, gdy nie ma czego oglądać — sekcja poza ekranem, karta w tle,
67
+ * `paused`, `speed` równe zeru albo ograniczony ruch w systemie; wtedy zostaje jedna statyczna
68
+ * klatka zamiast pustki, bo efekt jest częścią kompozycji, nie ozdobą.
69
+ *
70
+ * Rysowanie i cała matematyka siedzą w `@konce-pt/backdrop`, wspólnym z portem Angulara —
71
+ * ten komponent to sam cykl życia.
72
+ *
73
+ * @example
74
+ * <KptBackdrop effect="wave" rotation={18} colors={['#5b8af2', '#a86df2', '#f25b9e']}>
75
+ * <h1>Build your app from the first prompt</h1>
76
+ * <KptButton>Get started</KptButton>
77
+ * </KptBackdrop>
78
+ */
79
+ export declare function KptBackdrop({ effect, background, speed, seed, paused, adaptive, color, anchor, cell, gap, density, falloff, colors, rotation, band, amplitude, contentProps, className, style, children, ...rest }: Readonly<KptBackdropProps>): import("react").JSX.Element;
@@ -0,0 +1,195 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { KPT_BACKDROP_MAX_DPR, KPT_BACKDROP_MAX_STEP, KPT_SIGNAL_GRID_CELL, KPT_SIGNAL_GRID_DENSITY, KPT_SIGNAL_GRID_FALLOFF, KPT_SIGNAL_GRID_GAP, KPT_WAVE_AMPLITUDE, KPT_WAVE_BAND, KPT_WAVE_ROTATION, adaptBackdropValue, createBackdropRenderer, observeBackdropTheme, readBackdropInk, readBackdropLead, readBackdropSurface, resolveBackdropColors, } from '@konce-pt/backdrop';
4
+ import { cn } from "../internal/cn.js";
5
+ import { isDevBuild } from "../internal/dev.js";
6
+ const EMPTY = [];
7
+ /**
8
+ * Dekoracyjna warstwa tła dla sekcji: komponent obejmuje treść i maluje pod nią animowany canvas.
9
+ * Sięga się po niego tam, gdzie płaska powierzchnia wygląda na niedokończoną, a wideo albo duży
10
+ * obraz kosztowałyby transfer i nie poszłyby za motywem.
11
+ *
12
+ * Komponent jest opakowaniem: canvas leży pod spodem, a dzieci idą warstwę wyżej i zachowują się
13
+ * zwyczajnie. Rysowanie stoi, gdy nie ma czego oglądać — sekcja poza ekranem, karta w tle,
14
+ * `paused`, `speed` równe zeru albo ograniczony ruch w systemie; wtedy zostaje jedna statyczna
15
+ * klatka zamiast pustki, bo efekt jest częścią kompozycji, nie ozdobą.
16
+ *
17
+ * Rysowanie i cała matematyka siedzą w `@konce-pt/backdrop`, wspólnym z portem Angulara —
18
+ * ten komponent to sam cykl życia.
19
+ *
20
+ * @example
21
+ * <KptBackdrop effect="wave" rotation={18} colors={['#5b8af2', '#a86df2', '#f25b9e']}>
22
+ * <h1>Build your app from the first prompt</h1>
23
+ * <KptButton>Get started</KptButton>
24
+ * </KptBackdrop>
25
+ */
26
+ export function KptBackdrop({ effect = 'signal-grid', background = '', speed = 1, seed = 0, paused = false, adaptive = false, color = '', anchor = 'right', cell = KPT_SIGNAL_GRID_CELL, gap = KPT_SIGNAL_GRID_GAP, density = KPT_SIGNAL_GRID_DENSITY, falloff = KPT_SIGNAL_GRID_FALLOFF, colors = EMPTY, rotation = KPT_WAVE_ROTATION, band = KPT_WAVE_BAND, amplitude = KPT_WAVE_AMPLITUDE, contentProps, className, style, children, ...rest }) {
27
+ const host = useRef(null);
28
+ const canvas = useRef(null);
29
+ const context = useRef(null);
30
+ const renderer = useRef(null);
31
+ const size = useRef({ width: 0, height: 0 });
32
+ /** Zegar animacji w sekundach — w refie, żeby przebudowa renderera nie cofała obrazu. */
33
+ const clock = useRef(0);
34
+ /** Kolor wiodący, z którym zbudowany jest bieżący renderer — punkt odniesienia dla zmiany motywu. */
35
+ const lead = useRef('');
36
+ /** Ostatnio zgłoszony zestaw odrzuconych barw — żeby nie powtarzać tego samego ostrzeżenia. */
37
+ const reported = useRef('');
38
+ /** Tło po adaptacji. Puste znaczy „nie ma czego podmieniać" i wtedy liczy się samo `background`. */
39
+ const [adapted, setAdapted] = useState('');
40
+ /**
41
+ * Ton i kolor treści dobrane do malowanego tła. Puste znaczy „sekcja nie ma własnego tła"
42
+ * i wtedy treść zostaje przy `--kpt-color-on-surface` ze stylu.
43
+ */
44
+ const [ink, setInk] = useState({ tone: '', color: '' });
45
+ const rate = useRef(speed);
46
+ rate.current = speed;
47
+ const [onScreen, setOnScreen] = useState(true);
48
+ const [reduced, setReduced] = useState(false);
49
+ /** Licznik zmian motywu; podbicie każe przeliczyć kolory z tokenów na nowo. */
50
+ const [theme, setTheme] = useState(0);
51
+ const paint = useCallback(() => {
52
+ if (context.current)
53
+ renderer.current?.draw(context.current, clock.current);
54
+ }, []);
55
+ const measure = useCallback(() => {
56
+ const element = canvas.current;
57
+ if (!element)
58
+ return;
59
+ context.current ??= element.getContext('2d');
60
+ const rect = element.getBoundingClientRect();
61
+ const dpr = Math.min(globalThis.devicePixelRatio || 1, KPT_BACKDROP_MAX_DPR);
62
+ const width = Math.round(rect.width);
63
+ const height = Math.round(rect.height);
64
+ size.current = { width, height };
65
+ element.width = Math.round(width * dpr);
66
+ element.height = Math.round(height * dpr);
67
+ // Zapis width/height zeruje kontekst, więc skala musi wrócić po każdym pomiarze.
68
+ context.current?.setTransform(dpr, 0, 0, dpr, 0, 0);
69
+ }, []);
70
+ // Tablica barw przychodzi nowa przy każdym renderze rodzica, więc do zależności idzie jej treść.
71
+ const ramp = colors.join('|');
72
+ const list = useMemo(() => (ramp === '' ? EMPTY : ramp.split('|')), [ramp]);
73
+ // Każda zmiana wejść buduje renderer od nowa — zamiast doklejać ścieżki „co się zmieniło".
74
+ useEffect(() => {
75
+ // Kolory rozwiązuje się na canvasie, nie na hoście: sonda `var()` pisze inline style, a nasłuch
76
+ // motywu ogląda `style` hosta i jego przodków — zapis na hoście zapętliłby przebudowy.
77
+ const scope = canvas.current;
78
+ if (!scope)
79
+ return undefined;
80
+ measure();
81
+ const resolved = resolveBackdropColors(scope, color, list, adaptive);
82
+ lead.current = resolved.color;
83
+ const painted = adaptive ? adaptBackdropValue(background, readBackdropSurface(scope)) : background;
84
+ setAdapted(adaptive ? painted : '');
85
+ // Ton liczymy z tła, które sekcja naprawdę pomaluje — czyli już po adaptacji.
86
+ setInk(readBackdropInk(scope, painted));
87
+ // Efekt chodzi przy każdej zmianie wejść, więc ciągnięcie suwaka nad wadliwą rampą zasypałoby
88
+ // konsolę tym samym zdaniem — zgłaszamy dopiero zestaw inny niż poprzednio.
89
+ const rejected = resolved.invalid.join(', ');
90
+ if (rejected !== reported.current) {
91
+ reported.current = rejected;
92
+ if (rejected !== '' && isDevBuild()) {
93
+ console.warn(`[KptBackdrop] Pominięto barwy, których przeglądarka nie rozpoznaje: ${rejected}`);
94
+ }
95
+ }
96
+ const current = createBackdropRenderer({
97
+ effect,
98
+ color: resolved.color,
99
+ colors: resolved.colors,
100
+ anchor,
101
+ cell,
102
+ gap,
103
+ density,
104
+ falloff,
105
+ rotation,
106
+ band,
107
+ amplitude,
108
+ seed,
109
+ });
110
+ current.resize(size.current.width, size.current.height);
111
+ renderer.current = current;
112
+ paint();
113
+ return () => {
114
+ current.dispose();
115
+ renderer.current = null;
116
+ };
117
+ }, [effect, color, list, anchor, cell, gap, density, falloff, rotation, band, amplitude, seed, adaptive, background, theme, measure, paint]);
118
+ // Sam rozmiar nie zmienia ustawień, więc renderer zostaje — przelicza tylko geometrię.
119
+ useEffect(() => {
120
+ const element = canvas.current;
121
+ if (!element)
122
+ return undefined;
123
+ const observer = new ResizeObserver(() => {
124
+ const rect = element.getBoundingClientRect();
125
+ if (Math.round(rect.width) === size.current.width && Math.round(rect.height) === size.current.height)
126
+ return;
127
+ measure();
128
+ renderer.current?.resize(size.current.width, size.current.height);
129
+ paint();
130
+ });
131
+ observer.observe(element);
132
+ return () => observer.disconnect();
133
+ }, [measure, paint]);
134
+ // Sekcja poza ekranem i karta w tle to ten sam przypadek: nie ma czego oglądać.
135
+ useEffect(() => {
136
+ const element = host.current;
137
+ if (!element)
138
+ return undefined;
139
+ const update = (visible) => setOnScreen(visible && !document.hidden);
140
+ const observer = new IntersectionObserver((entries) => update(entries.some((entry) => entry.isIntersecting)));
141
+ observer.observe(element);
142
+ const onTabSwitch = () => setOnScreen(!document.hidden);
143
+ document.addEventListener('visibilitychange', onTabSwitch);
144
+ return () => {
145
+ observer.disconnect();
146
+ document.removeEventListener('visibilitychange', onTabSwitch);
147
+ };
148
+ }, []);
149
+ useEffect(() => {
150
+ const query = matchMedia('(prefers-reduced-motion: reduce)');
151
+ setReduced(query.matches);
152
+ const onChange = (event) => setReduced(event.matches);
153
+ query.addEventListener('change', onChange);
154
+ return () => query.removeEventListener('change', onChange);
155
+ }, []);
156
+ // Zmiana motywu przestawia tokeny, a kolory renderera są już wyliczone — trzeba je odczytać
157
+ // na nowo. Nasłuch trafia też w zmiany niezwiązane z barwą, więc licznik podbija się dopiero
158
+ // wtedy, gdy odczyt naprawdę dał inny kolor.
159
+ useEffect(() => {
160
+ const element = host.current;
161
+ if (!element)
162
+ return undefined;
163
+ return observeBackdropTheme(element, () => {
164
+ const scope = canvas.current;
165
+ if (!scope)
166
+ return;
167
+ // Ten sam rachunek co w efekcie budującym — inaczej strażnik porównywałby wartość sprzed
168
+ // adaptacji z zapamiętaną po adaptacji i podbijałby licznik bez przerwy.
169
+ const next = adaptive ? adaptBackdropValue(color, readBackdropSurface(scope)) : color;
170
+ if (readBackdropLead(scope, next) === lead.current)
171
+ return;
172
+ setTheme((count) => count + 1);
173
+ });
174
+ }, [color, adaptive]);
175
+ useEffect(() => {
176
+ if (paused || reduced || !onScreen || speed <= 0)
177
+ return undefined;
178
+ let frame = 0;
179
+ let last = 0;
180
+ const tick = (now) => {
181
+ const step = last === 0 ? 0 : Math.min(now - last, KPT_BACKDROP_MAX_STEP);
182
+ last = now;
183
+ // Prędkość wchodzi w zegar, nie w fazę — suwak przyspiesza obraz zamiast go przeskakiwać.
184
+ clock.current += (step / 1000) * rate.current;
185
+ paint();
186
+ frame = requestAnimationFrame(tick);
187
+ };
188
+ frame = requestAnimationFrame(tick);
189
+ return () => cancelAnimationFrame(frame);
190
+ }, [paused, reduced, onScreen, speed, paint]);
191
+ const { className: contentClassName, ...contentRest } = contentProps ?? {};
192
+ /** Tło malowane na hoście: po adaptacji, jeśli jest, inaczej to, co podano. */
193
+ const surface = adapted || background;
194
+ return (_jsxs("div", { ref: host, className: cn('kpt-backdrop-host', className), "data-effect": effect, "data-tone": ink.tone || undefined, style: { ...(surface ? { background: surface } : null), ...(ink.color ? { color: ink.color } : null), ...style }, ...rest, children: [_jsx("canvas", { ref: canvas, className: "kpt-backdrop__canvas", "aria-hidden": "true" }), _jsx("div", { className: cn('kpt-backdrop__content', contentClassName), ...contentRest, children: children })] }));
195
+ }
@@ -0,0 +1,132 @@
1
+ # KptBackdrop (@konce-pt/react)
2
+
3
+ A decorative background layer for a section: the component wraps your content and paints an
4
+ animated canvas beneath it. You reach for it where a flat surface looks unfinished, and where
5
+ a video or a large image would cost bandwidth and refuse to follow the theme.
6
+ Children sit a layer above the canvas and behave normally.
7
+ Import: `import { KptBackdrop } from '@konce-pt/react';`
8
+
9
+ ## DOM
10
+ <div class="kpt-backdrop-host" data-effect="wave">
11
+ <canvas class="kpt-backdrop__canvas" aria-hidden="true"></canvas>
12
+ <div class="kpt-backdrop__content">…</div>
13
+ </div>
14
+
15
+ ## Props (shared)
16
+ - `effect`: 'signal-grid' | 'wave' (default 'signal-grid')
17
+ - `background`: string — the section background; empty takes `--kpt-color-surface`
18
+ - `speed`: number (default 1) — 1 is the calm pace of either effect, 0 freezes the picture
19
+ - `seed`: number (default 0) — the same value gives the same layout and the same sway
20
+ - `paused`: boolean — stops the animation, leaving the last frame
21
+ - `adaptive`: boolean — carries hardcoded colours onto the current theme's surface
22
+ - `contentProps` — props for the `.kpt-backdrop__content` layer
23
+ - `className`, `ref` and every other `<div>` prop go to `.kpt-backdrop-host`
24
+
25
+ ## Props (signal-grid)
26
+ - `color`: string — the square colour, any CSS notation, tokens included; empty takes
27
+ `--kpt-color-primary`
28
+ - `anchor`: 'left' | 'right' (default 'right') — the edge the grid is densest at
29
+ - `cell`: number (default 5), `gap`: number (default 11) — the square and the space between, in px
30
+ - `density`: number (default 0.8) — the share of lit cells at the anchor, 0–1
31
+ - `falloff`: number (default 1.4) — how fast the grid fades towards the far edge
32
+
33
+ ## Props (wave)
34
+ - `colors`: string[] — the colour ramp the current slides along, closed into a loop; every
35
+ stop takes any CSS notation, `color`'s rules exactly
36
+ - `rotation`: number (default 12) — the angle the current crosses the frame at, in degrees
37
+ - `band`: number (default 0.34) — the band's width as a fraction of the frame's height
38
+ - `amplitude`: number (default 0.16) — how far the centre line sways
39
+
40
+ A prop that does not belong to the current effect is simply not read.
41
+
42
+ ## Examples
43
+ <KptBackdrop effect="wave" rotation={18} colors={['#5B8AF2', '#A86DF2', '#F25B9E']}>
44
+ <h1>Build your app from the first prompt</h1>
45
+ <KptButton>Get started</KptButton>
46
+ </KptBackdrop>
47
+
48
+ ## The two effects
49
+ `signal-grid` is a fine grid of squares that quietly twinkle, dense at one edge and fading to
50
+ empty at the other; the gradient comes from **how many** squares are lit, not from opacity laid
51
+ over an even grid. `wave` is one soft current of light with hues sliding along it and no hard edge
52
+ anywhere; `rotation` turns it, `band` widens it, `amplitude` sways it.
53
+
54
+ Without `colors` the wave's ramp is `--kpt-color-primary` plus two hues derived from it by
55
+ rotation. The library's palette is neutral, so that default current is single-hued and muted.
56
+
57
+ Every stop is put to the browser, so `oklch()`, `color-mix()`, `rgb()` and named colours are read
58
+ the same as hex; alpha is dropped. A stop the browser refuses falls out of the ramp, and if every
59
+ one of them falls out the default ramp takes over rather than the section going blank — a dev
60
+ build says in the console which stops it dropped.
61
+
62
+ ## The same picture in both ports
63
+ Everything that decides how it looks — the grid layout, the density ramp, the twinkle, the current
64
+ and the colour ramp, plus the renderers themselves — lives in `@konce-pt/backdrop`, shared with
65
+ the Angular port. This component is the lifecycle: `rAF`, `ResizeObserver`, `IntersectionObserver`,
66
+ the reduced-motion query and the theme watch. The same props therefore give the
67
+ same frame in either port.
68
+
69
+ ## Rest
70
+ Drawing stops when there is nothing to watch: the section is off-screen, the tab is in the
71
+ background, `paused` is set, `speed` is 0, or the system asks for reduced motion. What is left is
72
+ a single static frame, not emptiness. `speed` feeds the clock rather than the phase, so a speed
73
+ slider accelerates the picture instead of jumping it. A change of `colors` is tracked by content,
74
+ not by array identity, so a fresh array from a parent render does not rebuild the renderer.
75
+
76
+ ## Accessibility
77
+ The canvas carries `aria-hidden="true"` and `pointer-events: none` — it is decoration with no
78
+ content and nothing to hit. Children keep their own roles and tab order.
79
+
80
+ ## Tokens
81
+ `--kpt-color-primary` (squares and the default ramp), `--kpt-color-surface` (background),
82
+ `--kpt-color-on-surface`. Section-wide overrides: `--kpt-backdrop-color`, `--kpt-backdrop-surface`.
83
+
84
+ ## Adaptive colours
85
+ A literal stays put by design, which is right for a brand colour and wrong for a section meant to
86
+ read in both themes. `adaptive` closes that gap: it carries `color`, `colors` and `background`
87
+ onto the surface of the current theme, keeping each one's distance from that surface. Black picked
88
+ for a white section comes out white on a near-black ground, and that white background comes out at
89
+ the surface's own lightness. Only lightness moves — hue and chroma stay, so a brand colour keeps
90
+ being that colour and only changes how light it is.
91
+
92
+ It touches literals only. An empty input takes a token, and a value reaching through `var()`
93
+ reaches a token too; both already follow the theme, and adapting them would turn them back again.
94
+ Values written for the light theme are the assumption — the library keeps light in `:root` and
95
+ dark as the override.
96
+
97
+ ## Content on a custom background
98
+ The host paints `--kpt-color-surface` and lets content inherit `--kpt-color-on-surface`, which is
99
+ right until the section gets a `background` of its own — then that token describes the page around
100
+ it, not the ground the content actually sits on. A dark section in a light theme would come out
101
+ black on black.
102
+
103
+ So a section with its own background works out its own ink: the host sets `color` to match what it
104
+ paints, and announces the result as `data-tone` (`light` for a light background, `dark` for a dark
105
+ one). Text follows by inheritance; anything needing more than a text colour — buttons in the
106
+ section, say — can hang its own tokens off the attribute:
107
+
108
+ :where(div.kpt-backdrop-host[data-tone='light']) .hero { --kpt-button-filled-bg: #15181a; }
109
+
110
+ The tone is computed from the background the section really paints, so with `adaptive` on it
111
+ matches the adapted value, not the one written in the template. A section without its own
112
+ background announces nothing and content stays on `--kpt-color-on-surface`.
113
+
114
+ ## Theme
115
+ Canvas does not expand `var()`, so the colour is read once and baked into the renderer — the
116
+ component therefore watches for anything that can restyle the tokens under it and reads them
117
+ again. `[data-theme="dark"]` is not tied to `:root`, so the watch covers `data-theme`, `class`
118
+ and `style` on every ancestor from the host up to `<html>`, plus the `prefers-color-scheme` query
119
+ for an application that keeps its dark tokens in a media query. A runtime override of
120
+ `--kpt-color-primary` is picked up the same way as a theme switch. The renderer is rebuilt only
121
+ when the colour actually came out different, so an unrelated class landing on `<html>` costs one
122
+ cheap read and nothing else.
123
+
124
+ Whether an explicit `color` or `colors` follows the theme is up to how it is written. A literal
125
+ stays put — `#5b8af2` is `#5b8af2` in either theme, which is what someone spelling out a brand
126
+ colour wants. An expression reaching for a token is resolved against the tree on every rebuild, so
127
+ it moves with the theme on its own:
128
+
129
+ <KptBackdrop color="color-mix(in oklab, #5b8af2, var(--kpt-color-surface) 30%)">
130
+
131
+ `var(--kpt-color-primary)` works the same way. A name that resolves to nothing falls back to the
132
+ token for `color`, and drops out of the ramp for `colors`.
@@ -0,0 +1,53 @@
1
+ import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
+ import type { KptChatAuthor, KptChatMessage as KptChatMessageModel, KptChatMessageRow, KptChatReactionOption, KptChatReplyRef } from '@konce-pt/chat';
3
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
4
+ /** Wybór reakcji na wiadomości — komponent mówi, w co kliknięto, dopisanie należy do aplikacji. */
5
+ export interface KptChatReactEvent<T = unknown> {
6
+ message: KptChatMessageModel<T>;
7
+ emoji: string;
8
+ }
9
+ export interface KptChatProps<T = unknown> extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
10
+ /** Wiadomości w kolejności chronologicznej — komponent ich nie sortuje. */
11
+ messages?: readonly KptChatMessageModel<T>[];
12
+ /** `id` nadawcy uznawanego za „mnie": jego wiadomości idą na prawo i mają status. */
13
+ currentUser?: string;
14
+ /** Przerwa łamiąca grupę wiadomości jednego nadawcy. */
15
+ groupGapMs?: number;
16
+ /** `id` pierwszej nieprzeczytanej wiadomości — nad nią stanie dzielnik. */
17
+ unreadFrom?: string | null;
18
+ /** Brak połączenia: pasek u góry listy z liczbą wiadomości czekających na wysłanie. */
19
+ offline?: boolean;
20
+ /** Osoby piszące w tej chwili. */
21
+ typing?: readonly KptChatAuthor[];
22
+ /** Dosuwanie widoku do najnowszej wiadomości, dopóki użytkownik nie odjedzie w górę. */
23
+ autoScroll?: boolean;
24
+ /** Akcje bąbla pod kursorem: odpowiedz i zareaguj. */
25
+ actions?: boolean;
26
+ /** Zestaw wybieraka reakcji, przekazywany bąblom. Domyślnie pięć wbudowanych. */
27
+ reactionOptions?: readonly KptChatReactionOption[];
28
+ /** Własny komunikat pustej rozmowy. */
29
+ emptyLabel?: string;
30
+ onRetry?: (message: KptChatMessageModel<T>) => void;
31
+ onReply?: (message: KptChatMessageModel<T>) => void;
32
+ onReact?: (event: KptChatReactEvent<T>) => void;
33
+ onQuoted?: (quote: KptChatReplyRef) => void;
34
+ /** Przewinięcie na sam szczyt — moment na dociągnięcie starszych wiadomości. */
35
+ onLoadMore?: () => void;
36
+ /** Szablon treści bąbla — odpowiednik `<ng-template let-message>` z Angulara. */
37
+ renderMessage?: (message: KptChatMessageModel<T>, row: KptChatMessageRow<T>) => ReactNode;
38
+ }
39
+ /**
40
+ * Lista rozmowy — grupuje wiadomości, wstawia separatory dni i dzielnik nieprzeczytanych,
41
+ * trzyma widok przy dole i pokazuje pasek offline z liczbą wiadomości czekających na wysłanie.
42
+ *
43
+ * Sama logika wierszy mieszka w `@konce-pt/chat` (`buildChatRows`), więc port Angulara liczy ją
44
+ * tak samo. Komponent nie ma nic wspólnego z transportem: wiadomości dostaje, a `onRetry`
45
+ * i `onLoadMore` tylko zgłasza — ponowieniem wysyłki i dociąganiem historii zajmuje się aplikacja.
46
+ *
47
+ * Kompozytor (`KptChatComposer`) jest osobnym komponentem i stoi obok listy — dzięki temu
48
+ * czat bez pola wpisywania (podgląd, archiwum) nie wozi go ze sobą.
49
+ *
50
+ * @example
51
+ * <KptChat messages={messages} currentUser="me" typing={typing} onRetry={resend} />
52
+ */
53
+ export declare function KptChat<T = unknown>({ messages, currentUser, groupGapMs, unreadFrom, offline, typing, autoScroll, actions, reactionOptions, emptyLabel, onRetry, onReply, onReact, onQuoted, onLoadMore, renderMessage, locale, dictionary, className, ...rest }: Readonly<KptChatProps<T>>): import("react").JSX.Element;