@konce-pt/styles 0.8.3 → 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 (2) hide show
  1. package/components.css +1028 -6
  2. package/package.json +2 -2
package/components.css CHANGED
@@ -1115,6 +1115,38 @@
1115
1115
  }
1116
1116
  }
1117
1117
 
1118
+ /* src/lib/backdrop/backdrop.component.scss */
1119
+ @charset "UTF-8";
1120
+ @layer kpt.components {
1121
+ .kpt-backdrop-host {
1122
+ /* Kolor kwadratów czyta z tej własności TypeScript — canvas nie rozwija `var()` sam. */
1123
+ --_dot: var(--kpt-backdrop-color, var(--kpt-color-primary));
1124
+ position: relative;
1125
+ display: block;
1126
+ /* Siatka celowo wychodzi poza obszar, żeby nie kończyć się przed krawędzią — tu się ją ucina. */
1127
+ overflow: hidden;
1128
+ /* Własny kontekst nakładania: `z-index` treści nie ma się o co bić z resztą strony. */
1129
+ isolation: isolate;
1130
+ background: var(--kpt-backdrop-surface, var(--kpt-color-surface));
1131
+ color: var(--kpt-color-on-surface);
1132
+ }
1133
+ .kpt-backdrop__canvas {
1134
+ position: absolute;
1135
+ inset: 0;
1136
+ z-index: 0;
1137
+ display: block;
1138
+ inline-size: 100%;
1139
+ block-size: 100%;
1140
+ /* Tło nie łapie kliknięć ani zaznaczenia — pod spodem nie ma czego dotykać. */
1141
+ pointer-events: none;
1142
+ }
1143
+ .kpt-backdrop__content {
1144
+ position: relative;
1145
+ z-index: 1;
1146
+ block-size: 100%;
1147
+ }
1148
+ }
1149
+
1118
1150
  /* src/lib/badge/badge.component.scss */
1119
1151
  @charset "UTF-8";
1120
1152
  @layer kpt.components {
@@ -1729,6 +1761,21 @@
1729
1761
  --_bg: color-mix(in oklch, var(--kpt-color-info) 16%, transparent);
1730
1762
  --_fg: var(--kpt-color-info);
1731
1763
  }
1764
+ .kpt-chip[data-text-transform=capitalize] {
1765
+ text-transform: capitalize;
1766
+ font-size: var(--kpt-font-size-sm, 0.85rem);
1767
+ font-weight: var(--kpt-font-weight-light, 100);
1768
+ }
1769
+ .kpt-chip[data-text-transform=uppercase] {
1770
+ text-transform: uppercase;
1771
+ font-size: var(--kpt-font-size-sm, 0.85rem);
1772
+ font-weight: var(--kpt-font-weight-light, 100);
1773
+ }
1774
+ .kpt-chip[data-text-transform=lowercase] {
1775
+ text-transform: lowercase;
1776
+ font-size: var(--kpt-font-size-sm, 0.75rem);
1777
+ font-weight: var(--kpt-font-weight-light, 100);
1778
+ }
1732
1779
  .kpt-chip--disabled {
1733
1780
  opacity: 0.55;
1734
1781
  }
@@ -2332,13 +2379,20 @@
2332
2379
  color: var(--kpt-color-on-surface-muted);
2333
2380
  font-size: var(--kpt-font-size-sm, 0.875rem);
2334
2381
  }
2335
- .kpt-data-view__switch {
2382
+ .kpt-data-view__controls {
2383
+ display: inline-flex;
2384
+ align-items: center;
2385
+ gap: var(--kpt-space-2, 0.5rem);
2386
+ }
2387
+ .kpt-data-view__switch,
2388
+ .kpt-data-view__columns {
2336
2389
  display: inline-flex;
2337
2390
  border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
2338
2391
  border-radius: var(--kpt-radius-md, 0.5rem);
2339
2392
  overflow: hidden;
2340
2393
  }
2341
- .kpt-data-view__switch button {
2394
+ .kpt-data-view__switch button,
2395
+ .kpt-data-view__columns button {
2342
2396
  display: inline-flex;
2343
2397
  align-items: center;
2344
2398
  justify-content: center;
@@ -2349,10 +2403,21 @@
2349
2403
  color: var(--kpt-color-on-surface-muted);
2350
2404
  cursor: pointer;
2351
2405
  }
2352
- .kpt-data-view__switch button.is-active {
2406
+ .kpt-data-view__switch button.is-active,
2407
+ .kpt-data-view__columns button.is-active {
2353
2408
  background: var(--kpt-color-primary);
2354
2409
  color: var(--kpt-color-on-primary);
2355
2410
  }
2411
+ .kpt-data-view__columns button {
2412
+ gap: var(--kpt-space-1, 0.25rem);
2413
+ width: auto;
2414
+ padding-inline: var(--kpt-space-2, 0.5rem);
2415
+ font-size: var(--kpt-font-size-sm, 0.875rem);
2416
+ font-weight: var(--kpt-font-weight-medium, 500);
2417
+ }
2418
+ .kpt-data-view__content {
2419
+ --_gap: var(--kpt-space-3, 0.75rem);
2420
+ }
2356
2421
  .kpt-data-view__content[data-layout=list] {
2357
2422
  display: flex;
2358
2423
  flex-direction: column;
@@ -2360,8 +2425,11 @@
2360
2425
  }
2361
2426
  .kpt-data-view__content[data-layout=grid] {
2362
2427
  display: grid;
2363
- grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
2364
- gap: var(--kpt-space-3, 0.75rem);
2428
+ gap: var(--_gap);
2429
+ grid-template-columns: repeat(auto-fill, minmax(var(--_min, 10rem), 1fr));
2430
+ }
2431
+ .kpt-data-view__content[data-layout=grid][data-columns] {
2432
+ grid-template-columns: repeat(auto-fill, minmax(max(var(--_min, 10rem), (100% - (var(--_cols) - 1) * var(--_gap)) / var(--_cols)), 1fr));
2365
2433
  }
2366
2434
  .kpt-data-view__item {
2367
2435
  border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
@@ -2376,7 +2444,7 @@
2376
2444
  }
2377
2445
  .kpt-data-view .kpt-paginator {
2378
2446
  margin-top: var(--kpt-space-3, 0.75rem);
2379
- display: block;
2447
+ flex-wrap: wrap;
2380
2448
  }
2381
2449
  }
2382
2450
 
@@ -4107,6 +4175,61 @@
4107
4175
  }
4108
4176
  }
4109
4177
 
4178
+ /* src/lib/mention/mention.component.scss */
4179
+ @charset "UTF-8";
4180
+ @layer kpt.components {
4181
+ /* Wzmianka żyje w zdaniu, więc host jest `inline` — `inline-block` nie łamałby się razem
4182
+ z wierszem i długi akapit rozjeżdżałby się na wąskim ekranie. */
4183
+ .kpt-mention-host,
4184
+ .kpt-mentions-host,
4185
+ .kpt-mentions__text {
4186
+ display: inline;
4187
+ }
4188
+ /* Trigger jest `<button>` albo `<a>`, więc zdejmujemy z niego wygląd kontrolki: w akapicie ma
4189
+ czytać się jak wyróżnione słowo, a nie jak przycisk wciśnięty w zdanie. */
4190
+ .kpt-mention {
4191
+ display: inline;
4192
+ margin: 0;
4193
+ padding: 0;
4194
+ border: 0;
4195
+ border-radius: var(--kpt-radius-sm, 0.25rem);
4196
+ background: transparent;
4197
+ color: var(--kpt-color-primary);
4198
+ font: inherit;
4199
+ /* Kropkowana linia mówi „tu coś jest do podejrzenia", zanim ktokolwiek najedzie — ciągłe
4200
+ podkreślenie znaczyłoby link, a wzmianka zwykle nigdzie nie prowadzi, tylko otwiera kartę.
4201
+ W spoczynku jest jasna, żeby nie rozbijała akapitu; pod kursorem i przy otwartej karcie
4202
+ ciemnieje do koloru samej wzmianki. */
4203
+ text-decoration: underline dotted var(--kpt-color-primary-border);
4204
+ text-decoration-thickness: 1px;
4205
+ text-underline-offset: 0.2em;
4206
+ cursor: pointer;
4207
+ transition: text-decoration-color var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard, ease);
4208
+ }
4209
+ .kpt-mention:hover, .kpt-mention[aria-expanded=true] {
4210
+ text-decoration-color: var(--kpt-color-primary);
4211
+ }
4212
+ .kpt-mention:focus-visible {
4213
+ outline: 2px solid var(--kpt-color-focus-ring);
4214
+ outline-offset: 2px;
4215
+ }
4216
+ @media (prefers-reduced-motion: reduce) {
4217
+ .kpt-mention {
4218
+ transition: none;
4219
+ }
4220
+ }
4221
+ .kpt-mention {
4222
+ /* Wzmianka bez karty: wyróżniona, ale bez obietnicy, że coś się pod nią kryje — więc także
4223
+ bez kropkowanej linii, która tę obietnicę składa. */
4224
+ }
4225
+ .kpt-mention[data-plain] {
4226
+ color: var(--kpt-color-text);
4227
+ font-weight: var(--kpt-font-weight-medium, 500);
4228
+ text-decoration: none;
4229
+ cursor: inherit;
4230
+ }
4231
+ }
4232
+
4110
4233
  /* src/lib/menu/menu.component.scss */
4111
4234
  @charset "UTF-8";
4112
4235
  @layer kpt.components {
@@ -4371,6 +4494,9 @@
4371
4494
  max-height: 16rem;
4372
4495
  overflow-y: auto;
4373
4496
  }
4497
+ .kpt-order-list__items.is-dragover {
4498
+ background: var(--kpt-color-muted);
4499
+ }
4374
4500
  .kpt-order-list__item {
4375
4501
  padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
4376
4502
  border-radius: var(--kpt-radius-sm, 0.25rem);
@@ -4384,6 +4510,19 @@
4384
4510
  background: color-mix(in oklch, var(--kpt-color-primary) 16%, transparent);
4385
4511
  color: var(--kpt-color-primary);
4386
4512
  }
4513
+ .kpt-order-list__item--draggable {
4514
+ cursor: grab;
4515
+ user-select: none;
4516
+ }
4517
+ .kpt-order-list__item--draggable:active {
4518
+ cursor: grabbing;
4519
+ }
4520
+ .kpt-order-list__item--dragover {
4521
+ box-shadow: inset 0 2px 0 0 var(--kpt-color-primary);
4522
+ }
4523
+ .kpt-order-list__item.is-dragging {
4524
+ opacity: 0.5;
4525
+ }
4387
4526
  }
4388
4527
 
4389
4528
  /* src/lib/paginator/paginator.component.scss */
@@ -4570,6 +4709,36 @@
4570
4709
  }
4571
4710
  }
4572
4711
 
4712
+ /* src/lib/peek/peek.component.scss */
4713
+ @charset "UTF-8";
4714
+ @layer kpt.components {
4715
+ /* Host i zaczepienie są `inline`, nie `inline-flex`: karta wisi najczęściej na wzmiance w środku
4716
+ akapitu, a element flex nie łamie się razem z wierszem tekstu. */
4717
+ .kpt-peek-host,
4718
+ .kpt-peek__origin {
4719
+ display: inline;
4720
+ }
4721
+ .kpt-peek__panel {
4722
+ max-inline-size: 22rem;
4723
+ padding: var(--kpt-space-4, 1rem);
4724
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
4725
+ border-radius: var(--kpt-radius-md, 0.5rem);
4726
+ background: var(--kpt-color-surface-raised);
4727
+ color: var(--kpt-color-on-surface);
4728
+ font-size: var(--kpt-font-size-sm, 0.875rem);
4729
+ box-shadow: var(--kpt-elevation-3);
4730
+ /* Karta otwarta hoverem nie pokazuje obwódki fokusu — dostaje ją dopiero przypięta, bo wtedy
4731
+ naprawdę trzyma fokus i użytkownik musi wiedzieć, gdzie jest. */
4732
+ }
4733
+ .kpt-peek__panel:focus {
4734
+ outline: none;
4735
+ }
4736
+ .kpt-peek__panel[data-pinned]:focus-visible {
4737
+ outline: 2px solid var(--kpt-color-focus-ring);
4738
+ outline-offset: 2px;
4739
+ }
4740
+ }
4741
+
4573
4742
  /* src/lib/pick-list/pick-list.component.scss */
4574
4743
  @layer kpt.components {
4575
4744
  .kpt-pick-list-host {
@@ -4605,6 +4774,9 @@
4605
4774
  overflow-y: auto;
4606
4775
  flex: 1;
4607
4776
  }
4777
+ .kpt-pick-list__items.is-dragover {
4778
+ background: var(--kpt-color-muted);
4779
+ }
4608
4780
  .kpt-pick-list__item {
4609
4781
  padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
4610
4782
  border-radius: var(--kpt-radius-sm, 0.25rem);
@@ -4618,6 +4790,19 @@
4618
4790
  background: color-mix(in oklch, var(--kpt-color-primary) 16%, transparent);
4619
4791
  color: var(--kpt-color-primary);
4620
4792
  }
4793
+ .kpt-pick-list__item--draggable {
4794
+ cursor: grab;
4795
+ user-select: none;
4796
+ }
4797
+ .kpt-pick-list__item--draggable:active {
4798
+ cursor: grabbing;
4799
+ }
4800
+ .kpt-pick-list__item--dragover {
4801
+ box-shadow: inset 0 2px 0 0 var(--kpt-color-primary);
4802
+ }
4803
+ .kpt-pick-list__item.is-dragging {
4804
+ opacity: 0.5;
4805
+ }
4621
4806
  .kpt-pick-list__controls {
4622
4807
  display: flex;
4623
4808
  flex-direction: column;
@@ -4824,6 +5009,7 @@
4824
5009
  }
4825
5010
 
4826
5011
  /* src/lib/rich-text/rich-text.component.scss */
5012
+ @charset "UTF-8";
4827
5013
  @layer kpt.components {
4828
5014
  .kpt-rich-text-host {
4829
5015
  display: block;
@@ -5173,6 +5359,38 @@
5173
5359
  font-size: var(--kpt-font-size-xs, 0.75rem);
5174
5360
  color: var(--kpt-color-on-surface-muted);
5175
5361
  }
5362
+ /* Lista podpowiedzi wzmianek — ten sam wygląd co menu kontekstowe tabeli, bo obie wychodzą
5363
+ spod kursora w środku treści i mają się czytać jak jedna rodzina. */
5364
+ .kpt-rt-mentions {
5365
+ min-inline-size: 12rem;
5366
+ max-inline-size: 20rem;
5367
+ margin: 0;
5368
+ padding: 0.25rem;
5369
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
5370
+ border-radius: var(--kpt-radius-md, 0.5rem);
5371
+ background: var(--kpt-color-surface-raised);
5372
+ list-style: none;
5373
+ box-shadow: var(--kpt-elevation-3);
5374
+ }
5375
+ .kpt-rt-mentions__item {
5376
+ display: flex;
5377
+ align-items: baseline;
5378
+ gap: var(--kpt-space-2, 0.5rem);
5379
+ padding: var(--kpt-space-1, 0.25rem) var(--kpt-space-2, 0.5rem);
5380
+ border-radius: var(--kpt-radius-sm, 0.25rem);
5381
+ font-size: var(--kpt-font-size-sm, 0.875rem);
5382
+ cursor: pointer;
5383
+ /* Podświetlenie idzie z klawiatury i z kursora tym samym atrybutem: lista ma jedną
5384
+ aktywną pozycję, więc Enter zawsze wstawia to, co widać jako wybrane. */
5385
+ }
5386
+ .kpt-rt-mentions__item[data-active] {
5387
+ background: var(--kpt-color-surface-hover);
5388
+ }
5389
+ .kpt-rt-mentions__kind {
5390
+ margin-inline-start: auto;
5391
+ color: var(--kpt-color-text-muted);
5392
+ font-size: var(--kpt-font-size-xs, 0.75rem);
5393
+ }
5176
5394
  .kpt-rt-menu {
5177
5395
  min-width: 12rem;
5178
5396
  padding: 0.25rem;
@@ -6869,6 +7087,810 @@
6869
7087
  }
6870
7088
  }
6871
7089
 
7090
+ /* chat/src/lib/chat-composer.component.scss */
7091
+ @charset "UTF-8";
7092
+ @layer kpt.components {
7093
+ .kpt-chat-composer-host {
7094
+ display: block;
7095
+ border-block-start: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7096
+ background: var(--kpt-color-surface);
7097
+ }
7098
+ .kpt-chat-composer {
7099
+ display: flex;
7100
+ align-items: flex-end;
7101
+ gap: var(--kpt-space-2, 0.5rem);
7102
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7103
+ }
7104
+ .kpt-chat-composer__field {
7105
+ --_max-rows: 6;
7106
+ --_line: 1.5em;
7107
+ flex: 1 1 auto;
7108
+ min-width: 0;
7109
+ /* Wysokość ustawia skrypt od treści; ten limit zamienia dalszy wzrost na przewijanie. */
7110
+ max-block-size: calc(var(--_max-rows) * var(--_line) + var(--kpt-space-2, 0.5rem));
7111
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7112
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7113
+ border-radius: var(--kpt-radius-lg, 0.75rem);
7114
+ background: var(--kpt-color-surface-variant);
7115
+ color: var(--kpt-color-text);
7116
+ font: inherit;
7117
+ font-size: var(--kpt-font-size-sm, 0.875rem);
7118
+ line-height: 1.5;
7119
+ /* Zmiana rozmiaru myszą kłóciłaby się z autorośnięciem — wysokość liczy komponent. */
7120
+ resize: none;
7121
+ overflow-y: auto;
7122
+ }
7123
+ .kpt-chat-composer__field::placeholder {
7124
+ color: var(--kpt-color-text-subtle);
7125
+ }
7126
+ .kpt-chat-composer__field:focus-visible {
7127
+ outline: 2px solid var(--kpt-color-focus-ring);
7128
+ outline-offset: -1px;
7129
+ }
7130
+ .kpt-chat-composer__field:disabled {
7131
+ opacity: 0.6;
7132
+ cursor: not-allowed;
7133
+ }
7134
+ /* Pole pliku jest sterowane przyciskiem; natywny widget nie da się ostylować. */
7135
+ .kpt-chat-composer__picker {
7136
+ display: none;
7137
+ }
7138
+ .kpt-chat-composer__reply {
7139
+ display: flex;
7140
+ align-items: center;
7141
+ gap: var(--kpt-space-2, 0.5rem);
7142
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7143
+ border-inline-start: 2px solid var(--kpt-color-primary);
7144
+ background: var(--kpt-color-surface-variant);
7145
+ }
7146
+ .kpt-chat-composer__reply-body {
7147
+ display: flex;
7148
+ flex: 1 1 auto;
7149
+ flex-direction: column;
7150
+ min-width: 0;
7151
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7152
+ }
7153
+ .kpt-chat-composer__reply-author {
7154
+ color: var(--kpt-color-primary);
7155
+ font-weight: var(--kpt-font-weight-semibold, 600);
7156
+ }
7157
+ .kpt-chat-composer__reply-text {
7158
+ color: var(--kpt-color-text-muted);
7159
+ overflow: hidden;
7160
+ text-overflow: ellipsis;
7161
+ white-space: nowrap;
7162
+ }
7163
+ .kpt-chat-composer__files {
7164
+ display: flex;
7165
+ flex-wrap: wrap;
7166
+ gap: var(--kpt-space-2, 0.5rem);
7167
+ margin: 0;
7168
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem) 0;
7169
+ list-style: none;
7170
+ }
7171
+ .kpt-chat-composer__file {
7172
+ display: inline-flex;
7173
+ align-items: center;
7174
+ gap: 0.25rem;
7175
+ max-inline-size: 14rem;
7176
+ padding-inline-start: var(--kpt-space-2, 0.5rem);
7177
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7178
+ border-radius: var(--kpt-radius-full, 9999px);
7179
+ background: var(--kpt-color-surface-variant);
7180
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7181
+ }
7182
+ .kpt-chat-composer__file-name {
7183
+ overflow: hidden;
7184
+ text-overflow: ellipsis;
7185
+ white-space: nowrap;
7186
+ }
7187
+ }
7188
+
7189
+ /* chat/src/lib/chat-dock.component.scss */
7190
+ @charset "UTF-8";
7191
+ @layer kpt.components {
7192
+ /* Host nie może tworzyć własnego pudełka: FAB i panel są `fixed`, a host wstawiony
7193
+ w środek layoutu aplikacji zajmowałby miejsce w przepływie. */
7194
+ .kpt-chat-dock-host {
7195
+ display: contents;
7196
+ }
7197
+ .kpt-chat-dock__trigger {
7198
+ --_offset: var(--kpt-space-6, 1.5rem);
7199
+ position: fixed;
7200
+ inset-block-end: var(--_offset);
7201
+ z-index: var(--kpt-z-overlay, 1000);
7202
+ }
7203
+ .kpt-chat-dock__panel {
7204
+ --_width: 22.5rem;
7205
+ --_offset: var(--kpt-space-6, 1.5rem);
7206
+ /* Opóźnienie ukrycia: przy zamykaniu panel ma najpierw zjechać, przy otwieraniu
7207
+ pojawia się natychmiast. Ta sama sztuczka co w kpt-drawer. */
7208
+ --_vis-delay: var(--kpt-motion-duration-base, 200ms);
7209
+ position: fixed;
7210
+ /* 4,5rem to wysokość FAB-a plus prześwit — panel stoi nad przyciskiem. */
7211
+ inset-block-end: calc(var(--_offset) + 4.5rem);
7212
+ z-index: var(--kpt-z-overlay, 1000);
7213
+ display: flex;
7214
+ flex-direction: column;
7215
+ inline-size: var(--_width);
7216
+ max-inline-size: calc(100vw - 2 * var(--_offset));
7217
+ block-size: min(32rem, 100dvh - 9rem);
7218
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7219
+ border-radius: var(--kpt-radius-lg, 0.75rem);
7220
+ background: var(--kpt-color-surface);
7221
+ color: var(--kpt-color-text);
7222
+ overflow: hidden;
7223
+ box-shadow: var(--kpt-elevation-3);
7224
+ /* Stan zamknięty siedzi w regule bazowej, nie pod `[data-position]`: Angular ustawia
7225
+ atrybuty o klatkę za późno, więc panel mrugnąłby otwarty przy pierwszym renderze.
7226
+ `visibility` zamiast `display: none` — przejścia nie da się zanimować z `none`. */
7227
+ visibility: hidden;
7228
+ opacity: 0;
7229
+ transform: translateY(var(--kpt-space-4, 1rem)) scale(0.98);
7230
+ transform-origin: bottom center;
7231
+ transition: opacity var(--kpt-motion-duration-base, 200ms) var(--kpt-motion-easing-standard, ease), transform var(--kpt-motion-duration-base, 200ms) var(--kpt-motion-easing-emphasized, ease), visibility 0s linear var(--_vis-delay);
7232
+ }
7233
+ .kpt-chat-dock__panel.is-open {
7234
+ visibility: visible;
7235
+ opacity: 1;
7236
+ transform: none;
7237
+ transition-delay: 0s;
7238
+ }
7239
+ .kpt-chat-dock__header {
7240
+ display: flex;
7241
+ flex: 0 0 auto;
7242
+ align-items: center;
7243
+ gap: var(--kpt-space-2, 0.5rem);
7244
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-2, 0.5rem) var(--kpt-space-2, 0.5rem) var(--kpt-space-4, 1rem);
7245
+ border-block-end: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7246
+ }
7247
+ .kpt-chat-dock__title {
7248
+ flex: 1 1 auto;
7249
+ font-weight: var(--kpt-font-weight-semibold, 600);
7250
+ }
7251
+ .kpt-chat-dock__content {
7252
+ display: flex;
7253
+ flex: 1 1 auto;
7254
+ flex-direction: column;
7255
+ min-height: 0;
7256
+ }
7257
+ /* Pozycja pozioma — logiczne własności, więc RTL odwraca róg bez osobnej reguły.
7258
+ Odsunięcie od rogu jest wspólne dla obu osi i steruje nim input `offset`: aplikacja
7259
+ z drugim widgetem w rogu (np. kpt-scroll-top) odsuwa dock, zamiast walczyć ze stylami. */
7260
+ .kpt-chat-dock__trigger,
7261
+ .kpt-chat-dock__panel {
7262
+ inset-inline-end: var(--_offset);
7263
+ }
7264
+ .kpt-chat-dock__trigger[data-position=bottom-start],
7265
+ .kpt-chat-dock__panel[data-position=bottom-start] {
7266
+ inset-inline: var(--_offset) auto;
7267
+ }
7268
+ /*
7269
+ * Współdzielony róg. `kpt-scroll-top` przykleja się dokładnie w to samo miejsce, z tym samym
7270
+ * `--kpt-z-overlay`, więc bez tej reguły FAB czatu znika pod nim w całości — wystaje sama
7271
+ * plakietka nieprzeczytanych, a przycisku nie da się kliknąć.
7272
+ *
7273
+ * Ustępuje przycisk powrotu na górę, bo czat jest akcją główną i większym celem. Odsuwamy go
7274
+ * W BOK, nie do góry: nad FAB-em stoi panel rozmowy, więc przesunięcie w pionie zamieniłoby
7275
+ * jedną kolizję na drugą, gorszą — przycisk lądowałby na treści wiadomości.
7276
+ *
7277
+ * `:has()` na korzeniu dokumentu jest tu jedyną drogą: oba komponenty są `position: fixed`
7278
+ * i nic ich nie łączy w drzewie DOM. Reguła stoi w arkuszu czatu, nie scroll-topu, bo zależność
7279
+ * ma iść w stronę grafu paczek — subpath zna rdzeń, rdzeń nie zna subpathu.
7280
+ *
7281
+ * Dotyczy wyłącznie `bottom-end`: dock po lewej i tak nie koliduje. Gdy aplikacja przestawi
7282
+ * `offset`, sama odpowiada też za ustawienie scroll-topu — ta reguła liczy od wartości domyślnej.
7283
+ */
7284
+ :root:has(.kpt-chat-dock__trigger[data-position=bottom-end]) .kpt-scroll-top {
7285
+ /* 3,5rem to szerokość FAB-a, 0,75rem prześwit między przyciskami. */
7286
+ right: calc(var(--kpt-space-6, 1.5rem) + 4.25rem);
7287
+ }
7288
+ /* Na telefonie panel wchodzi na pełny ekran: 22,5 rem w oknie szerokim na 20 rem
7289
+ to nie panel, tylko szpara. FAB zostaje na wierzchu jako przycisk zamknięcia. */
7290
+ @media (width < 640px) {
7291
+ .kpt-chat-dock__panel {
7292
+ inset: 0;
7293
+ inline-size: auto;
7294
+ max-inline-size: none;
7295
+ block-size: auto;
7296
+ border: 0;
7297
+ border-radius: 0;
7298
+ transform: translateY(var(--kpt-space-6, 1.5rem));
7299
+ }
7300
+ }
7301
+ @media (prefers-reduced-motion: reduce) {
7302
+ .kpt-chat-dock__panel {
7303
+ transition: opacity var(--kpt-motion-duration-fast, 120ms) linear, visibility 0s linear var(--_vis-delay);
7304
+ transform: none;
7305
+ }
7306
+ }
7307
+ }
7308
+
7309
+ /* chat/src/lib/chat-message.component.scss */
7310
+ @charset "UTF-8";
7311
+ @layer kpt.components {
7312
+ .kpt-chat-message-host {
7313
+ display: block;
7314
+ }
7315
+ /* Komunikat systemowy nie jest wypowiedzią — nie dostaje bąbla ani kolumny awatara. */
7316
+ .kpt-chat-message__system {
7317
+ margin: var(--kpt-space-2, 0.5rem) 0;
7318
+ text-align: center;
7319
+ color: var(--kpt-color-text-muted);
7320
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7321
+ }
7322
+ .kpt-chat-message__row {
7323
+ display: flex;
7324
+ align-items: flex-end;
7325
+ gap: var(--kpt-space-2, 0.5rem);
7326
+ /* Własna wiadomość: odwrócony kierunek dosuwa stos do prawej krawędzi, a kolumnę
7327
+ akcji (strzałka ponowienia) wypycha na zewnątrz bąbla — po stronie treści. */
7328
+ }
7329
+ .kpt-chat-message__row[data-align=end] {
7330
+ flex-direction: row-reverse;
7331
+ }
7332
+ /* Stała szerokość także wtedy, gdy awatara nie ma: bąble w grupie nie skaczą w poziomie. */
7333
+ .kpt-chat-message__avatar {
7334
+ flex: 0 0 2rem;
7335
+ align-self: flex-end;
7336
+ }
7337
+ .kpt-chat-message__stack {
7338
+ display: flex;
7339
+ flex-direction: column;
7340
+ min-width: 0;
7341
+ max-inline-size: min(42rem, 78%);
7342
+ }
7343
+ .kpt-chat-message__author {
7344
+ margin-inline-start: var(--kpt-space-3, 0.75rem);
7345
+ margin-block-end: var(--kpt-space-1, 0.25rem);
7346
+ color: var(--kpt-color-text-muted);
7347
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7348
+ font-weight: var(--kpt-font-weight-medium, 500);
7349
+ }
7350
+ .kpt-chat-message__bubble {
7351
+ /* Motyw jest monochromatyczny — różnicę między bąblami robi jasność, nie odcień.
7352
+ Wariant przychodzący siada na `muted`, bo `surface-variant` dzieli od tła listy
7353
+ 1,5% jasności i w jasnym motywie bąbel po prostu znika. */
7354
+ --_bg: var(--kpt-color-muted);
7355
+ --_fg: var(--kpt-color-on-muted);
7356
+ --_radius: var(--kpt-radius-lg, 0.75rem);
7357
+ --_radius-tight: var(--kpt-radius-sm, 0.25rem);
7358
+ position: relative;
7359
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7360
+ border-radius: var(--_radius);
7361
+ background: var(--_bg);
7362
+ color: var(--_fg);
7363
+ font-size: var(--kpt-font-size-sm, 0.875rem);
7364
+ line-height: var(--kpt-font-lineHeight-normal, 1.5);
7365
+ /* Cichszy wariant własnej wiadomości — dla rozmów, w których pełny kolor po prawej
7366
+ stronie jest za mocny. */
7367
+ }
7368
+ .kpt-chat-message__bubble[data-variant=primary] {
7369
+ --_bg: var(--kpt-color-primary-subtle);
7370
+ --_fg: var(--kpt-color-text);
7371
+ }
7372
+ .kpt-chat-message__bubble[data-variant=solid] {
7373
+ --_bg: var(--kpt-color-primary);
7374
+ --_fg: var(--kpt-color-on-primary);
7375
+ }
7376
+ .kpt-chat-message__bubble[data-variant=ghost] {
7377
+ --_bg: transparent;
7378
+ --_fg: var(--kpt-color-text);
7379
+ padding-inline: 0;
7380
+ }
7381
+ .kpt-chat-message__bubble {
7382
+ /* Nieudana wysyłka. Obwódka wewnętrznym cieniem, nie `border` — ramka przesunęłaby
7383
+ treść o piksel w chwili, w której status się zmienia. */
7384
+ }
7385
+ .kpt-chat-message__bubble[data-status=failed] {
7386
+ --_bg: var(--kpt-color-danger-subtle);
7387
+ /* Kolor tekstu też musi wrócić: własna wiadomość jest domyślnie `solid`, więc bez
7388
+ tego biały napis zostałby na bladoróżowym tle błędu. */
7389
+ --_fg: var(--kpt-color-text);
7390
+ box-shadow: inset 0 0 0 var(--kpt-border-width-thin, 1px) var(--kpt-color-danger-border);
7391
+ }
7392
+ .kpt-chat-message__bubble {
7393
+ /* Zapadnięty narożnik od strony nadawcy scala bąble w jedną kolumnę wypowiedzi. */
7394
+ }
7395
+ .kpt-chat-message__bubble[data-align=end][data-position=first] {
7396
+ border-end-end-radius: var(--_radius-tight);
7397
+ }
7398
+ .kpt-chat-message__bubble[data-align=end][data-position=middle] {
7399
+ border-start-end-radius: var(--_radius-tight);
7400
+ border-end-end-radius: var(--_radius-tight);
7401
+ }
7402
+ .kpt-chat-message__bubble[data-align=end][data-position=last] {
7403
+ border-start-end-radius: var(--_radius-tight);
7404
+ }
7405
+ .kpt-chat-message__bubble[data-align=start][data-position=first] {
7406
+ border-end-start-radius: var(--_radius-tight);
7407
+ }
7408
+ .kpt-chat-message__bubble[data-align=start][data-position=middle] {
7409
+ border-start-start-radius: var(--_radius-tight);
7410
+ border-end-start-radius: var(--_radius-tight);
7411
+ }
7412
+ .kpt-chat-message__bubble[data-align=start][data-position=last] {
7413
+ border-start-start-radius: var(--_radius-tight);
7414
+ }
7415
+ .kpt-chat-message__bubble {
7416
+ /* Narożnik pod ogonkiem jest prosty. Zaokrąglony odsuwał się od ogonka łukiem i między
7417
+ nimi prześwitywało tło listy — ogonek czytał się jak osobna strzałka doklejona obok
7418
+ bąbla, a nie jak jego przedłużenie. Bramka na `[data-tail]`: bąble bez ogonka (środek
7419
+ grupy, `ghost`, `system`, `[tail]="false"`) mają zostać zaokrąglone. */
7420
+ }
7421
+ .kpt-chat-message__bubble[data-tail][data-align=end] {
7422
+ border-end-end-radius: 0;
7423
+ }
7424
+ .kpt-chat-message__bubble[data-tail][data-align=start] {
7425
+ border-end-start-radius: 0;
7426
+ }
7427
+ .kpt-chat-message__bubble {
7428
+ /* Ogonek wycięty maską, nie obróconym kwadratem: obrót zostawia widoczny szew
7429
+ na półprzezroczystym tle i nie daje wklęsłego łuku. */
7430
+ }
7431
+ .kpt-chat-message__bubble[data-tail]::after {
7432
+ content: "";
7433
+ position: absolute;
7434
+ inset-block-end: 0;
7435
+ inline-size: 0.75rem;
7436
+ block-size: 0.75rem;
7437
+ background: var(--_bg);
7438
+ mask: radial-gradient(circle at 100% 0, transparent 0 0.75rem, #000 0.75rem);
7439
+ }
7440
+ .kpt-chat-message__bubble[data-tail][data-align=end]::after {
7441
+ inset-inline-end: -0.5rem;
7442
+ }
7443
+ .kpt-chat-message__bubble {
7444
+ /* Odbicie zamiast drugiej maski — obraca się razem z `inset-inline`, więc RTL
7445
+ nie wymaga osobnej reguły. */
7446
+ }
7447
+ .kpt-chat-message__bubble[data-tail][data-align=start]::after {
7448
+ inset-inline-start: -0.5rem;
7449
+ transform: scaleX(-1);
7450
+ }
7451
+ .kpt-chat-message__bubble {
7452
+ /* Reakcje wychodzą poza dolną krawędź, więc bąbel musi im zostawić miejsce. */
7453
+ }
7454
+ .kpt-chat-message__bubble:has(.kpt-chat-message__reactions) {
7455
+ margin-block-end: 0.875rem;
7456
+ }
7457
+ .kpt-chat-message__quote {
7458
+ display: flex;
7459
+ flex-direction: column;
7460
+ gap: 0.125rem;
7461
+ width: 100%;
7462
+ margin-block-end: var(--kpt-space-1, 0.25rem);
7463
+ padding: var(--kpt-space-1, 0.25rem) var(--kpt-space-2, 0.5rem);
7464
+ border: 0;
7465
+ border-inline-start: 2px solid var(--kpt-color-primary);
7466
+ border-radius: var(--kpt-radius-sm, 0.25rem);
7467
+ background: color-mix(in oklch, currentColor 8%, transparent);
7468
+ color: inherit;
7469
+ font: inherit;
7470
+ text-align: start;
7471
+ cursor: pointer;
7472
+ }
7473
+ .kpt-chat-message__quote-author {
7474
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7475
+ font-weight: var(--kpt-font-weight-semibold, 600);
7476
+ }
7477
+ .kpt-chat-message__quote-text {
7478
+ display: -webkit-box;
7479
+ -webkit-box-orient: vertical;
7480
+ -webkit-line-clamp: 2;
7481
+ overflow: hidden;
7482
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7483
+ opacity: 0.8;
7484
+ }
7485
+ .kpt-chat-message__text {
7486
+ margin: 0;
7487
+ /* Kompozytor łamie linie na Shift+Enter — bez `pre-wrap` zlepiłyby się w akapit. */
7488
+ white-space: pre-wrap;
7489
+ overflow-wrap: anywhere;
7490
+ }
7491
+ .kpt-chat-message__files {
7492
+ display: flex;
7493
+ flex-direction: column;
7494
+ gap: var(--kpt-space-1, 0.25rem);
7495
+ margin: var(--kpt-space-2, 0.5rem) 0 0;
7496
+ padding: 0;
7497
+ list-style: none;
7498
+ }
7499
+ .kpt-chat-message__file {
7500
+ display: flex;
7501
+ align-items: center;
7502
+ gap: var(--kpt-space-2, 0.5rem);
7503
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7504
+ }
7505
+ .kpt-chat-message__file-thumb {
7506
+ width: 2rem;
7507
+ height: 2rem;
7508
+ border-radius: var(--kpt-radius-sm, 0.25rem);
7509
+ object-fit: cover;
7510
+ }
7511
+ .kpt-chat-message__file-name {
7512
+ color: inherit;
7513
+ overflow: hidden;
7514
+ text-overflow: ellipsis;
7515
+ white-space: nowrap;
7516
+ }
7517
+ .kpt-chat-message__file-size {
7518
+ color: inherit;
7519
+ opacity: 0.6;
7520
+ }
7521
+ .kpt-chat-message__meta {
7522
+ display: flex;
7523
+ align-items: center;
7524
+ justify-content: flex-end;
7525
+ gap: 0.25rem;
7526
+ margin-block-start: 0.125rem;
7527
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7528
+ opacity: 0.7;
7529
+ }
7530
+ .kpt-chat-message__status {
7531
+ /* Ikona ma 1em, a wiersz meta jest już zmniejszony — bez tego status ginie. */
7532
+ font-size: 0.875rem;
7533
+ }
7534
+ /* Kolor statusu schodzi z `data-status` bąbla, a nie z drugiego takiego atrybutu na ikonie:
7535
+ jeden stan w DOM znaczy, że oba porty nie mają czego rozjechać. */
7536
+ .kpt-chat-message__bubble[data-status=read] .kpt-chat-message__status {
7537
+ color: var(--kpt-color-primary);
7538
+ opacity: 1;
7539
+ }
7540
+ /* Na bąblu `solid` akcent jest kolorem TŁA, więc „przeczytano" zniknęłoby w nim
7541
+ bez śladu. Tam wyróżnia je pełne krycie własnego koloru tekstu. */
7542
+ .kpt-chat-message__bubble[data-variant=solid][data-status=read] .kpt-chat-message__status {
7543
+ color: currentcolor;
7544
+ }
7545
+ .kpt-chat-message__bubble[data-status=failed] .kpt-chat-message__status {
7546
+ color: var(--kpt-color-danger);
7547
+ opacity: 1;
7548
+ }
7549
+ .kpt-chat-message__reactions {
7550
+ position: absolute;
7551
+ inset-block-end: -0.75rem;
7552
+ display: flex;
7553
+ gap: 0.25rem;
7554
+ margin: 0;
7555
+ padding: 0;
7556
+ list-style: none;
7557
+ }
7558
+ .kpt-chat-message__reactions[data-align=end] {
7559
+ inset-inline-end: var(--kpt-space-2, 0.5rem);
7560
+ }
7561
+ .kpt-chat-message__reactions[data-align=start] {
7562
+ inset-inline-start: var(--kpt-space-2, 0.5rem);
7563
+ }
7564
+ .kpt-chat-message__reaction {
7565
+ display: inline-flex;
7566
+ align-items: center;
7567
+ gap: 0.125rem;
7568
+ padding: 0.0625rem 0.375rem;
7569
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7570
+ border-radius: var(--kpt-radius-full, 9999px);
7571
+ background: var(--kpt-color-surface-raised);
7572
+ color: var(--kpt-color-text);
7573
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7574
+ line-height: 1.25;
7575
+ box-shadow: var(--kpt-elevation-1);
7576
+ }
7577
+ .kpt-chat-message__reaction[data-reacted] {
7578
+ border-color: var(--kpt-color-primary-border);
7579
+ background: var(--kpt-color-primary-subtle);
7580
+ }
7581
+ .kpt-chat-message__reaction-count {
7582
+ opacity: 0.7;
7583
+ }
7584
+ .kpt-chat-message__actions {
7585
+ display: flex;
7586
+ flex: 0 0 auto;
7587
+ align-items: center;
7588
+ gap: 0.125rem;
7589
+ /* Odpowiedz i zareaguj wchodzą pod kursorem oraz przy fokusie — inaczej lista
7590
+ wyglądałaby jak panel sterowania. Ponowienie jest wyjątkiem: musi być widoczne
7591
+ zawsze, bo bez niego nieudana wiadomość nie ma czym o sobie powiedzieć. */
7592
+ }
7593
+ .kpt-chat-message__actions .kpt-icon-button-host {
7594
+ opacity: 0;
7595
+ transition: opacity var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard, ease);
7596
+ }
7597
+ .kpt-chat-message__actions .kpt-chat-message__retry {
7598
+ opacity: 1;
7599
+ color: var(--kpt-color-danger);
7600
+ }
7601
+ .kpt-chat-message__row:hover .kpt-chat-message__actions .kpt-icon-button-host,
7602
+ .kpt-chat-message__row:focus-within .kpt-chat-message__actions .kpt-icon-button-host,
7603
+ .kpt-chat-message__actions[data-open] .kpt-icon-button-host {
7604
+ opacity: 1;
7605
+ }
7606
+ /* Kotwica wybieraka — pozycjonuje go względem przycisku „zareaguj". */
7607
+ .kpt-chat-message__react {
7608
+ position: relative;
7609
+ display: inline-flex;
7610
+ }
7611
+ /* Wybierak reakcji: rząd emoji nad przyciskiem. Rozwija się w stronę bąbla, nie od niego:
7612
+ akcje stoją przy zewnętrznej krawędzi listy, więc wybierak wyśrodkowany na przycisku
7613
+ wychodziłby poza viewport (a ten ma `overflow-y: auto`, czyli przycina też w poziomie)
7614
+ — w wąskim docku ucięłoby połowę reakcji. */
7615
+ .kpt-chat-message__picker {
7616
+ position: absolute;
7617
+ inset-block-end: calc(100% + var(--kpt-space-1, 0.25rem));
7618
+ inset-inline-end: 0;
7619
+ z-index: var(--kpt-z-raised, 1);
7620
+ display: flex;
7621
+ gap: 0.125rem;
7622
+ padding: 0.1875rem;
7623
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7624
+ border-radius: var(--kpt-radius-full, 9999px);
7625
+ background: var(--kpt-color-surface-raised);
7626
+ box-shadow: var(--kpt-elevation-2);
7627
+ }
7628
+ /* Własna wiadomość ma odwrócony wiersz — akcje lądują z lewej, więc wybierak idzie w prawo. */
7629
+ .kpt-chat-message__row[data-align=end] .kpt-chat-message__picker {
7630
+ inset-inline: 0 auto;
7631
+ }
7632
+ .kpt-chat-message__pick {
7633
+ display: inline-flex;
7634
+ align-items: center;
7635
+ justify-content: center;
7636
+ inline-size: 1.75rem;
7637
+ block-size: 1.75rem;
7638
+ padding: 0;
7639
+ border: 0;
7640
+ border-radius: var(--kpt-radius-full, 9999px);
7641
+ background: transparent;
7642
+ font: inherit;
7643
+ font-size: var(--kpt-font-size-md, 1rem);
7644
+ line-height: 1;
7645
+ cursor: pointer;
7646
+ transition: transform var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard, ease);
7647
+ }
7648
+ .kpt-chat-message__pick:hover {
7649
+ background: var(--kpt-color-surface-hover);
7650
+ transform: scale(1.15);
7651
+ }
7652
+ .kpt-chat-message__pick:focus-visible {
7653
+ outline: 2px solid var(--kpt-color-focus-ring);
7654
+ outline-offset: 1px;
7655
+ }
7656
+ .kpt-chat-message__pick {
7657
+ /* Reakcja już dana: ta sama plakietka co pod bąblem, żeby było widać, co się zdejmie. */
7658
+ }
7659
+ .kpt-chat-message__pick[aria-pressed=true] {
7660
+ background: var(--kpt-color-primary-subtle);
7661
+ }
7662
+ @media (prefers-reduced-motion: reduce) {
7663
+ .kpt-chat-message__actions .kpt-icon-button-host,
7664
+ .kpt-chat-message__pick {
7665
+ transition: none;
7666
+ }
7667
+ .kpt-chat-message__pick:hover {
7668
+ transform: none;
7669
+ }
7670
+ }
7671
+ }
7672
+
7673
+ /* chat/src/lib/chat-typing.component.scss */
7674
+ @charset "UTF-8";
7675
+ @layer kpt.components {
7676
+ .kpt-chat-typing-host {
7677
+ display: block;
7678
+ }
7679
+ .kpt-chat-typing {
7680
+ display: flex;
7681
+ align-items: center;
7682
+ gap: var(--kpt-space-2, 0.5rem);
7683
+ /* Wcięcie równe kolumnie awatara — wskaźnik staje tam, gdzie stanie bąbel. */
7684
+ padding-inline-start: calc(2rem + var(--kpt-space-2, 0.5rem));
7685
+ }
7686
+ .kpt-chat-typing__bubble {
7687
+ display: inline-flex;
7688
+ align-items: center;
7689
+ gap: 0.1875rem;
7690
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7691
+ border-radius: var(--kpt-radius-lg, 0.75rem);
7692
+ border-end-start-radius: var(--kpt-radius-sm, 0.25rem);
7693
+ background: var(--kpt-color-surface-variant);
7694
+ }
7695
+ .kpt-chat-typing__dot {
7696
+ width: 0.375rem;
7697
+ height: 0.375rem;
7698
+ border-radius: var(--kpt-radius-full, 9999px);
7699
+ background: var(--kpt-color-text-muted);
7700
+ animation: kpt-chat-typing-bounce 1.2s infinite ease-in-out;
7701
+ }
7702
+ .kpt-chat-typing__dot:nth-child(2) {
7703
+ animation-delay: 150ms;
7704
+ }
7705
+ .kpt-chat-typing__dot:nth-child(3) {
7706
+ animation-delay: 300ms;
7707
+ }
7708
+ .kpt-chat-typing__label {
7709
+ color: var(--kpt-color-text-muted);
7710
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7711
+ }
7712
+ @keyframes kpt-chat-typing-bounce {
7713
+ 0%, 60%, 100% {
7714
+ transform: translateY(0);
7715
+ opacity: 0.5;
7716
+ }
7717
+ 30% {
7718
+ transform: translateY(-0.1875rem);
7719
+ opacity: 1;
7720
+ }
7721
+ }
7722
+ @keyframes kpt-chat-typing-pulse {
7723
+ 0%, 100% {
7724
+ opacity: 0.35;
7725
+ }
7726
+ 50% {
7727
+ opacity: 1;
7728
+ }
7729
+ }
7730
+ /* Skok zamieniamy na pulsowanie, a nie wyłączamy animację: kropki są jedynym
7731
+ nośnikiem informacji „ktoś pisze" i nieruchome nie odróżniają się od ozdoby. */
7732
+ @media (prefers-reduced-motion: reduce) {
7733
+ .kpt-chat-typing__dot {
7734
+ animation-name: kpt-chat-typing-pulse;
7735
+ animation-duration: 1.8s;
7736
+ }
7737
+ }
7738
+ }
7739
+
7740
+ /* chat/src/lib/chat.component.scss */
7741
+ @charset "UTF-8";
7742
+ @layer kpt.components {
7743
+ .kpt-chat-host {
7744
+ --_gap: var(--kpt-space-1, 0.25rem);
7745
+ --_group-gap: var(--kpt-space-4, 1rem);
7746
+ display: flex;
7747
+ flex-direction: column;
7748
+ min-height: 0;
7749
+ background: var(--kpt-color-surface);
7750
+ color: var(--kpt-color-text);
7751
+ }
7752
+ /* Pasek offline. `role="status"` bez `aria-live="assertive"` — utrata sieci ma być
7753
+ zauważona, ale nie przerywać czytania wiadomości w połowie zdania. */
7754
+ .kpt-chat__offline {
7755
+ display: flex;
7756
+ align-items: center;
7757
+ gap: var(--kpt-space-2, 0.5rem);
7758
+ margin: 0;
7759
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7760
+ border-block-end: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-warning-border);
7761
+ background: var(--kpt-color-warning-subtle);
7762
+ color: var(--kpt-color-text);
7763
+ font-size: var(--kpt-font-size-sm, 0.875rem);
7764
+ }
7765
+ .kpt-chat__offline-count {
7766
+ margin-inline-start: auto;
7767
+ color: var(--kpt-color-text-muted);
7768
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7769
+ }
7770
+ /* Kontener pozycjonujący dla pigułki „nowe wiadomości" — pigułka ma pływać nad listą,
7771
+ a nie nad całą stroną, więc nie jest `fixed` jak kpt-scroll-top. */
7772
+ .kpt-chat__body {
7773
+ position: relative;
7774
+ display: flex;
7775
+ flex: 1 1 auto;
7776
+ min-height: 0;
7777
+ }
7778
+ .kpt-chat__viewport {
7779
+ display: flex;
7780
+ flex: 1 1 auto;
7781
+ flex-direction: column;
7782
+ gap: var(--_gap);
7783
+ min-height: 0;
7784
+ padding: var(--kpt-space-4, 1rem);
7785
+ overflow-y: auto;
7786
+ overscroll-behavior: contain;
7787
+ }
7788
+ /* Przerwa między grupami: wiadomość otwierająca grupę odsuwa się od poprzedniej.
7789
+ Wewnątrz grupy zostaje ciasny `--_gap`, dzięki czemu bąble czytają się jako jedna
7790
+ wypowiedź, a zapadnięte narożniki mają się do czego przykleić. */
7791
+ .kpt-chat__viewport > .kpt-chat-message-host:has(.kpt-chat-message__bubble[data-position=first]),
7792
+ .kpt-chat__viewport > .kpt-chat-message-host:has(.kpt-chat-message__bubble[data-position=single]) {
7793
+ margin-block-start: var(--_group-gap);
7794
+ }
7795
+ .kpt-chat__viewport > .kpt-chat-message-host:first-child {
7796
+ margin-block-start: 0;
7797
+ }
7798
+ .kpt-chat__empty {
7799
+ margin: auto;
7800
+ padding: var(--kpt-space-8, 2rem);
7801
+ color: var(--kpt-color-text-muted);
7802
+ font-size: var(--kpt-font-size-sm, 0.875rem);
7803
+ text-align: center;
7804
+ }
7805
+ /* Separator dnia w liście i pływająca pigułka to ta sama etykieta w dwóch rolach, więc
7806
+ wygląd mają wspólny. */
7807
+ .kpt-chat__date,
7808
+ .kpt-chat__day {
7809
+ padding: 0.125rem var(--kpt-space-3, 0.75rem);
7810
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7811
+ border-radius: var(--kpt-radius-full, 9999px);
7812
+ background: var(--kpt-color-surface-raised);
7813
+ color: var(--kpt-color-text-muted);
7814
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7815
+ box-shadow: var(--kpt-elevation-1);
7816
+ }
7817
+ /* Separator jedzie z treścią i nie jest `sticky`: przyklejony zostawał na górze, więc przy
7818
+ dłuższej rozmowie rósł tam stos ramek, a że pigułki różnią się szerokością, starsze
7819
+ wystawały zza najnowszej. Rolę „w jakim dniu jestem" przejęła jedna pływająca pigułka. */
7820
+ .kpt-chat__date {
7821
+ align-self: center;
7822
+ margin: var(--kpt-space-4, 1rem) 0 var(--kpt-space-2, 0.5rem);
7823
+ }
7824
+ /* Pływająca pigułka dnia: jedna na całą listę, z tekstem podmienianym przy przewijaniu.
7825
+ Wisi nad listą, a nie w niej, więc `absolute` względem `.kpt-chat__body` — tak samo jak
7826
+ pigułka „nowe wiadomości" przy dolnej krawędzi. Gaśnie przez `visibility`, nie
7827
+ `display: none`, bo skrypt musi móc zmierzyć jej pas także wtedy, gdy jej nie widać. */
7828
+ .kpt-chat__day {
7829
+ position: absolute;
7830
+ inset-block-start: var(--kpt-space-2, 0.5rem);
7831
+ /* Prawy odstęp kompensuje pasek przewijania: lista oddaje mu kawałek swojej szerokości,
7832
+ więc pigułka wyśrodkowana względem całego pola stałaby odrobinę za bardzo w prawo
7833
+ względem samego tekstu. Logicznie, nie przez `right` — w RTL pasek jest po drugiej
7834
+ stronie i przesunięcie musi iść za nim. */
7835
+ inset-inline: 0 var(--kpt-space-3, 0.75rem);
7836
+ z-index: var(--kpt-z-raised, 1);
7837
+ width: fit-content;
7838
+ /* Margines bloku na zero: to `<p>`, a domyślny margines UA zepchnąłby pigułkę spod
7839
+ swojego `inset-block-start`. */
7840
+ margin: 0 auto;
7841
+ pointer-events: none;
7842
+ /* Pusta pigułka musi mieć tę samą wysokość co pełna: skrypt mierzy jej pas również wtedy,
7843
+ gdy jest zgaszona, a zapadnięty wiersz tekstu dałby za wąski pas i pigułka zapaliłaby się
7844
+ nad etykietą, która właśnie pod nią wjeżdża. Spacja zerowej szerokości trzyma linię. */
7845
+ }
7846
+ .kpt-chat__day::before {
7847
+ content: "​";
7848
+ }
7849
+ .kpt-chat__day[data-hidden] {
7850
+ visibility: hidden;
7851
+ }
7852
+ .kpt-chat__unread {
7853
+ display: flex;
7854
+ align-items: center;
7855
+ gap: var(--kpt-space-3, 0.75rem);
7856
+ margin: var(--kpt-space-4, 1rem) 0 var(--kpt-space-2, 0.5rem);
7857
+ color: var(--kpt-color-primary);
7858
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7859
+ }
7860
+ .kpt-chat__unread::before, .kpt-chat__unread::after {
7861
+ content: "";
7862
+ flex: 1 1 auto;
7863
+ block-size: var(--kpt-border-width-thin, 1px);
7864
+ background: var(--kpt-color-primary-border);
7865
+ }
7866
+ .kpt-chat__jump {
7867
+ position: absolute;
7868
+ inset-block-end: var(--kpt-space-3, 0.75rem);
7869
+ inset-inline: 0;
7870
+ display: inline-flex;
7871
+ align-items: center;
7872
+ gap: var(--kpt-space-1, 0.25rem);
7873
+ width: fit-content;
7874
+ margin-inline: auto;
7875
+ padding: var(--kpt-space-1, 0.25rem) var(--kpt-space-3, 0.75rem);
7876
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-primary-border);
7877
+ border-radius: var(--kpt-radius-full, 9999px);
7878
+ background: var(--kpt-color-surface-raised);
7879
+ color: var(--kpt-color-primary);
7880
+ font: inherit;
7881
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7882
+ cursor: pointer;
7883
+ box-shadow: var(--kpt-elevation-2);
7884
+ }
7885
+ .kpt-chat__jump:hover {
7886
+ background: var(--kpt-color-surface-hover);
7887
+ }
7888
+ .kpt-chat__jump:focus-visible {
7889
+ outline: 2px solid var(--kpt-color-focus-ring);
7890
+ outline-offset: 2px;
7891
+ }
7892
+ }
7893
+
6872
7894
  /* roadmap/src/lib/roadmap.component.scss */
6873
7895
  @layer kpt.components {
6874
7896
  .kpt-roadmap {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konce-pt/styles",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Base CSS for Koncept UI — reset, cascade @layer ordering and neutral light/dark theme (SCSS source).",
5
5
  "license": "MIT",
6
6
  "author": "konce.pt",
@@ -52,7 +52,7 @@
52
52
  "src"
53
53
  ],
54
54
  "peerDependencies": {
55
- "@konce-pt/tokens": "0.8.3"
55
+ "@konce-pt/tokens": "0.8.5"
56
56
  },
57
57
  "devDependencies": {
58
58
  "sass": "^1.77.0"