@konce-pt/styles 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.
Files changed (2) hide show
  1. package/components.css +1284 -57
  2. package/package.json +2 -2
package/components.css CHANGED
@@ -92,7 +92,7 @@
92
92
  border: var(--kpt-border-width-thin, 1px) solid color-mix(in oklch, var(--_accent) 35%, transparent);
93
93
  border-inline-start: 3px solid var(--_accent);
94
94
  border-radius: var(--kpt-radius-md, 0.5rem);
95
- background: color-mix(in oklch, var(--_accent) 10%, var(--kpt-color-surface));
95
+ background: color-mix(in oklab, var(--_accent) 10%, var(--kpt-color-surface));
96
96
  color: var(--kpt-color-on-surface);
97
97
  font-size: var(--kpt-font-size-sm, 0.875rem);
98
98
  }
@@ -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 {
@@ -1322,6 +1354,7 @@
1322
1354
  cursor: pointer;
1323
1355
  background-color: var(--_bg);
1324
1356
  color: var(--_fg);
1357
+ text-decoration: none;
1325
1358
  transition: opacity var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard);
1326
1359
  }
1327
1360
  .kpt-button::after {
@@ -1335,33 +1368,65 @@
1335
1368
  pointer-events: none;
1336
1369
  transition: opacity var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard), background-color var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard);
1337
1370
  }
1338
- .kpt-button:hover:not(:disabled) {
1371
+ .kpt-button:hover:not(:disabled):not([aria-disabled=true]) {
1339
1372
  --_state: var(--_bg-hover);
1340
1373
  }
1341
- .kpt-button:hover:not(:disabled)::after {
1374
+ .kpt-button:hover:not(:disabled):not([aria-disabled=true])::after {
1342
1375
  opacity: 1;
1343
1376
  }
1344
- .kpt-button:active:not(:disabled) {
1377
+ .kpt-button:active:not(:disabled):not([aria-disabled=true]) {
1345
1378
  --_state: var(--_bg-active, var(--_bg-hover));
1346
1379
  }
1347
- .kpt-button:disabled {
1380
+ .kpt-button:is(:disabled, [aria-disabled=true]) {
1348
1381
  cursor: not-allowed;
1349
1382
  opacity: 0.5;
1350
1383
  }
1384
+ .kpt-button__content {
1385
+ display: inline-flex;
1386
+ align-items: center;
1387
+ gap: var(--kpt-button-gap, 0.5rem);
1388
+ }
1389
+ .kpt-button :is(.kpt-button__icon, .kpt-button__content .kpt-icon-host) {
1390
+ font-size: var(--_icon, 1.125rem);
1391
+ }
1392
+ .kpt-button[data-size=xs] {
1393
+ --_h: 1.75rem;
1394
+ --_px: 0.5rem;
1395
+ --_fs: 0.75rem;
1396
+ --_icon: 0.875rem;
1397
+ }
1351
1398
  .kpt-button[data-size=sm] {
1352
1399
  --_h: 2rem;
1353
1400
  --_px: 0.75rem;
1354
1401
  --_fs: 0.8125rem;
1402
+ --_icon: 1rem;
1355
1403
  }
1356
1404
  .kpt-button[data-size=md] {
1357
1405
  --_h: 2.5rem;
1358
1406
  --_px: 1rem;
1359
1407
  --_fs: 0.875rem;
1408
+ --_icon: 1.125rem;
1360
1409
  }
1361
1410
  .kpt-button[data-size=lg] {
1362
1411
  --_h: 3rem;
1363
1412
  --_px: 1.5rem;
1364
1413
  --_fs: 1rem;
1414
+ --_icon: 1.25rem;
1415
+ }
1416
+ .kpt-button[data-size=xl] {
1417
+ --_h: 3.5rem;
1418
+ --_px: 2rem;
1419
+ --_fs: 1.125rem;
1420
+ --_icon: 1.5rem;
1421
+ }
1422
+ .kpt-button[data-variant=elevated] {
1423
+ --_bg: var(--kpt-button-elevated-bg);
1424
+ --_fg: var(--kpt-button-elevated-fg);
1425
+ --_bg-hover: var(--kpt-button-elevated-bg-hover);
1426
+ box-shadow: var(--kpt-shadow-sm);
1427
+ }
1428
+ .kpt-button[data-variant=elevated]:hover:not(:disabled):not([aria-disabled=true]) {
1429
+ box-shadow: var(--kpt-shadow-md);
1365
1430
  }
1366
1431
  .kpt-button[data-variant=tonal] {
1367
1432
  --_bg: var(--kpt-button-tonal-bg);
@@ -1382,18 +1447,44 @@
1382
1447
  --_bg-hover: var(--kpt-button-text-bg-hover);
1383
1448
  --_bg-active: var(--kpt-button-text-bg-hover);
1384
1449
  }
1450
+ .kpt-button[data-variant=link] {
1451
+ --_bg: transparent;
1452
+ --_fg: var(--kpt-button-link-fg);
1453
+ --_bg-hover: transparent;
1454
+ --_px: 0;
1455
+ text-decoration: underline;
1456
+ text-underline-offset: 0.2em;
1457
+ }
1458
+ .kpt-button[data-variant=link]:hover:not(:disabled):not([aria-disabled=true]) {
1459
+ --_fg: var(--kpt-button-link-fg-hover);
1460
+ }
1385
1461
  .kpt-button[data-variant=danger] {
1386
- --_bg: var(--kpt-color-danger);
1387
- --_fg: var(--kpt-color-on-danger);
1388
- --_bg-hover: color-mix(in oklch, var(--kpt-color-danger) 88%, black);
1389
- --_bg-active: color-mix(in oklch, var(--kpt-color-danger) 78%, black);
1462
+ --_bg: var(--kpt-button-danger-bg);
1463
+ --_fg: var(--kpt-button-danger-fg);
1464
+ --_bg-hover: var(--kpt-button-danger-bg-hover);
1465
+ }
1466
+ .kpt-button[data-variant=success] {
1467
+ --_bg: var(--kpt-button-success-bg);
1468
+ --_fg: var(--kpt-button-success-fg);
1469
+ --_bg-hover: var(--kpt-button-success-bg-hover);
1470
+ }
1471
+ .kpt-button[data-variant=warning] {
1472
+ --_bg: var(--kpt-button-warning-bg);
1473
+ --_fg: var(--kpt-button-warning-fg);
1474
+ --_bg-hover: var(--kpt-button-warning-bg-hover);
1475
+ }
1476
+ .kpt-button[data-variant=info] {
1477
+ --_bg: var(--kpt-button-info-bg);
1478
+ --_fg: var(--kpt-button-info-fg);
1479
+ --_bg-hover: var(--kpt-button-info-bg-hover);
1390
1480
  }
1391
1481
  .kpt-button--loading .kpt-button__content {
1392
1482
  opacity: 0.75;
1393
1483
  }
1394
1484
  .kpt-button__spinner {
1395
- width: 1em;
1396
- height: 1em;
1485
+ width: var(--_icon, 1.125rem);
1486
+ height: var(--_icon, 1.125rem);
1487
+ flex: none;
1397
1488
  border: 2px solid currentColor;
1398
1489
  border-right-color: transparent;
1399
1490
  border-radius: 50%;
@@ -1435,10 +1526,14 @@
1435
1526
  border-inline-start: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
1436
1527
  background: none;
1437
1528
  color: var(--kpt-color-on-surface);
1438
- padding: 0 var(--kpt-space-4, 1rem);
1439
- height: 2.5rem;
1529
+ display: inline-flex;
1530
+ align-items: center;
1531
+ justify-content: center;
1532
+ gap: var(--kpt-button-gap, 0.5rem);
1533
+ padding: 0 var(--_px, 1rem);
1534
+ height: var(--_h, 2.5rem);
1440
1535
  font: inherit;
1441
- font-size: var(--kpt-font-size-sm, 0.875rem);
1536
+ font-size: var(--_fs, 0.875rem);
1442
1537
  font-weight: var(--kpt-font-weight-medium, 500);
1443
1538
  cursor: pointer;
1444
1539
  transition: background-color var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard);
@@ -1457,15 +1552,30 @@
1457
1552
  opacity: 0.5;
1458
1553
  cursor: not-allowed;
1459
1554
  }
1460
- .kpt-button-group[data-size=sm] .kpt-button-group__item {
1461
- height: 2rem;
1462
- padding-inline: 0.75rem;
1463
- font-size: 0.8125rem;
1555
+ .kpt-button-group__icon {
1556
+ font-size: var(--_icon, 1.125rem);
1464
1557
  }
1465
- .kpt-button-group[data-size=lg] .kpt-button-group__item {
1466
- height: 3rem;
1467
- padding-inline: 1.5rem;
1468
- font-size: 1rem;
1558
+ .kpt-button-group {
1559
+ --_h: 2.5rem;
1560
+ --_px: 1rem;
1561
+ --_fs: 0.875rem;
1562
+ --_icon: 1.125rem;
1563
+ }
1564
+ .kpt-button-group[data-size=sm] {
1565
+ --_h: 2rem;
1566
+ --_px: 0.75rem;
1567
+ --_fs: 0.8125rem;
1568
+ --_icon: 1rem;
1569
+ }
1570
+ .kpt-button-group[data-size=lg] {
1571
+ --_h: 3rem;
1572
+ --_px: 1.5rem;
1573
+ --_fs: 1rem;
1574
+ --_icon: 1.25rem;
1575
+ }
1576
+ .kpt-button-group[data-icon-only] .kpt-button-group__item {
1577
+ --_px: 0;
1578
+ width: var(--_h);
1469
1579
  }
1470
1580
  }
1471
1581
 
@@ -1710,7 +1820,7 @@
1710
1820
  white-space: nowrap;
1711
1821
  }
1712
1822
  .kpt-chip[data-variant=primary] {
1713
- --_bg: color-mix(in oklch, var(--kpt-color-primary) 14%, var(--kpt-color-surface));
1823
+ --_bg: color-mix(in oklab, var(--kpt-color-primary) 14%, var(--kpt-color-surface));
1714
1824
  --_fg: var(--kpt-color-primary);
1715
1825
  }
1716
1826
  .kpt-chip[data-variant=success] {
@@ -1729,6 +1839,21 @@
1729
1839
  --_bg: color-mix(in oklch, var(--kpt-color-info) 16%, transparent);
1730
1840
  --_fg: var(--kpt-color-info);
1731
1841
  }
1842
+ .kpt-chip[data-text-transform=capitalize] {
1843
+ text-transform: capitalize;
1844
+ font-size: var(--kpt-font-size-sm, 0.85rem);
1845
+ font-weight: var(--kpt-font-weight-light, 100);
1846
+ }
1847
+ .kpt-chip[data-text-transform=uppercase] {
1848
+ text-transform: uppercase;
1849
+ font-size: var(--kpt-font-size-sm, 0.85rem);
1850
+ font-weight: var(--kpt-font-weight-light, 100);
1851
+ }
1852
+ .kpt-chip[data-text-transform=lowercase] {
1853
+ text-transform: lowercase;
1854
+ font-size: var(--kpt-font-size-sm, 0.75rem);
1855
+ font-weight: var(--kpt-font-weight-light, 100);
1856
+ }
1732
1857
  .kpt-chip--disabled {
1733
1858
  opacity: 0.55;
1734
1859
  }
@@ -2332,13 +2457,20 @@
2332
2457
  color: var(--kpt-color-on-surface-muted);
2333
2458
  font-size: var(--kpt-font-size-sm, 0.875rem);
2334
2459
  }
2335
- .kpt-data-view__switch {
2460
+ .kpt-data-view__controls {
2461
+ display: inline-flex;
2462
+ align-items: center;
2463
+ gap: var(--kpt-space-2, 0.5rem);
2464
+ }
2465
+ .kpt-data-view__switch,
2466
+ .kpt-data-view__columns {
2336
2467
  display: inline-flex;
2337
2468
  border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
2338
2469
  border-radius: var(--kpt-radius-md, 0.5rem);
2339
2470
  overflow: hidden;
2340
2471
  }
2341
- .kpt-data-view__switch button {
2472
+ .kpt-data-view__switch button,
2473
+ .kpt-data-view__columns button {
2342
2474
  display: inline-flex;
2343
2475
  align-items: center;
2344
2476
  justify-content: center;
@@ -2349,10 +2481,21 @@
2349
2481
  color: var(--kpt-color-on-surface-muted);
2350
2482
  cursor: pointer;
2351
2483
  }
2352
- .kpt-data-view__switch button.is-active {
2484
+ .kpt-data-view__switch button.is-active,
2485
+ .kpt-data-view__columns button.is-active {
2353
2486
  background: var(--kpt-color-primary);
2354
2487
  color: var(--kpt-color-on-primary);
2355
2488
  }
2489
+ .kpt-data-view__columns button {
2490
+ gap: var(--kpt-space-1, 0.25rem);
2491
+ width: auto;
2492
+ padding-inline: var(--kpt-space-2, 0.5rem);
2493
+ font-size: var(--kpt-font-size-sm, 0.875rem);
2494
+ font-weight: var(--kpt-font-weight-medium, 500);
2495
+ }
2496
+ .kpt-data-view__content {
2497
+ --_gap: var(--kpt-space-3, 0.75rem);
2498
+ }
2356
2499
  .kpt-data-view__content[data-layout=list] {
2357
2500
  display: flex;
2358
2501
  flex-direction: column;
@@ -2360,8 +2503,11 @@
2360
2503
  }
2361
2504
  .kpt-data-view__content[data-layout=grid] {
2362
2505
  display: grid;
2363
- grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
2364
- gap: var(--kpt-space-3, 0.75rem);
2506
+ gap: var(--_gap);
2507
+ grid-template-columns: repeat(auto-fill, minmax(var(--_min, 10rem), 1fr));
2508
+ }
2509
+ .kpt-data-view__content[data-layout=grid][data-columns] {
2510
+ grid-template-columns: repeat(auto-fill, minmax(max(var(--_min, 10rem), (100% - (var(--_cols) - 1) * var(--_gap)) / var(--_cols)), 1fr));
2365
2511
  }
2366
2512
  .kpt-data-view__item {
2367
2513
  border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
@@ -2376,7 +2522,7 @@
2376
2522
  }
2377
2523
  .kpt-data-view .kpt-paginator {
2378
2524
  margin-top: var(--kpt-space-3, 0.75rem);
2379
- display: block;
2525
+ flex-wrap: wrap;
2380
2526
  }
2381
2527
  }
2382
2528
 
@@ -2961,7 +3107,8 @@
2961
3107
  @layer kpt.components {
2962
3108
  .kpt-dialog {
2963
3109
  margin: auto;
2964
- width: min(32rem, 100vw - 2rem);
3110
+ --_width: 32rem;
3111
+ width: min(var(--_width), 100vw - 2rem);
2965
3112
  max-height: calc(100vh - 4rem);
2966
3113
  padding: 0;
2967
3114
  border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
@@ -2970,6 +3117,12 @@
2970
3117
  color: var(--kpt-color-on-surface);
2971
3118
  box-shadow: var(--kpt-elevation-4);
2972
3119
  }
3120
+ .kpt-dialog[data-size=sm] {
3121
+ --_width: 24rem;
3122
+ }
3123
+ .kpt-dialog[data-size=lg] {
3124
+ --_width: 48rem;
3125
+ }
2973
3126
  .kpt-dialog::backdrop {
2974
3127
  background: var(--kpt-color-overlay-scrim);
2975
3128
  }
@@ -3027,6 +3180,13 @@
3027
3180
  .kpt-dialog [kptDialogFooter] {
3028
3181
  flex: none;
3029
3182
  display: flex;
3183
+ /*
3184
+ * Zawijanie jest obowiązkowe, nie kosmetyczne. `<dialog>` dostaje z arkusza przeglądarki
3185
+ * `overflow: auto`, więc stopka, która nie mieści się w szerokości okna, nie wystaje —
3186
+ * zostaje **ucięta**. Cztery przyciski w oknie `sm` wystarczą, żeby pierwszy z nich
3187
+ * przestał być czytelny, a użytkownik nie ma jak się do niego dostać.
3188
+ */
3189
+ flex-wrap: wrap;
3030
3190
  justify-content: flex-end;
3031
3191
  gap: var(--kpt-space-3, 0.75rem);
3032
3192
  padding: var(--kpt-space-4, 1rem) var(--kpt-space-6, 1.5rem);
@@ -3341,7 +3501,7 @@
3341
3501
  }
3342
3502
  .kpt-file-upload__zone:hover:not(.is-disabled), .kpt-file-upload__zone.is-dragover {
3343
3503
  border-color: var(--kpt-color-primary);
3344
- background: color-mix(in oklch, var(--kpt-color-primary) 6%, var(--kpt-color-surface));
3504
+ background: color-mix(in oklab, var(--kpt-color-primary) 6%, var(--kpt-color-surface));
3345
3505
  }
3346
3506
  .kpt-file-upload__zone.is-disabled {
3347
3507
  opacity: 0.6;
@@ -3660,7 +3820,7 @@
3660
3820
  .kpt-icon-button[data-variant=danger] {
3661
3821
  --_bg: var(--kpt-color-danger);
3662
3822
  --_fg: var(--kpt-color-on-danger);
3663
- --_bg-hover: color-mix(in oklch, var(--kpt-color-danger) 88%, black);
3823
+ --_bg-hover: color-mix(in oklab, var(--kpt-color-danger) 88%, black);
3664
3824
  }
3665
3825
  @media (prefers-reduced-motion: reduce) {
3666
3826
  .kpt-icon-button::after {
@@ -4107,6 +4267,61 @@
4107
4267
  }
4108
4268
  }
4109
4269
 
4270
+ /* src/lib/mention/mention.component.scss */
4271
+ @charset "UTF-8";
4272
+ @layer kpt.components {
4273
+ /* Wzmianka żyje w zdaniu, więc host jest `inline` — `inline-block` nie łamałby się razem
4274
+ z wierszem i długi akapit rozjeżdżałby się na wąskim ekranie. */
4275
+ .kpt-mention-host,
4276
+ .kpt-mentions-host,
4277
+ .kpt-mentions__text {
4278
+ display: inline;
4279
+ }
4280
+ /* Trigger jest `<button>` albo `<a>`, więc zdejmujemy z niego wygląd kontrolki: w akapicie ma
4281
+ czytać się jak wyróżnione słowo, a nie jak przycisk wciśnięty w zdanie. */
4282
+ .kpt-mention {
4283
+ display: inline;
4284
+ margin: 0;
4285
+ padding: 0;
4286
+ border: 0;
4287
+ border-radius: var(--kpt-radius-sm, 0.25rem);
4288
+ background: transparent;
4289
+ color: var(--kpt-color-primary);
4290
+ font: inherit;
4291
+ /* Kropkowana linia mówi „tu coś jest do podejrzenia", zanim ktokolwiek najedzie — ciągłe
4292
+ podkreślenie znaczyłoby link, a wzmianka zwykle nigdzie nie prowadzi, tylko otwiera kartę.
4293
+ W spoczynku jest jasna, żeby nie rozbijała akapitu; pod kursorem i przy otwartej karcie
4294
+ ciemnieje do koloru samej wzmianki. */
4295
+ text-decoration: underline dotted var(--kpt-color-primary-border);
4296
+ text-decoration-thickness: 1px;
4297
+ text-underline-offset: 0.2em;
4298
+ cursor: pointer;
4299
+ transition: text-decoration-color var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard, ease);
4300
+ }
4301
+ .kpt-mention:hover, .kpt-mention[aria-expanded=true] {
4302
+ text-decoration-color: var(--kpt-color-primary);
4303
+ }
4304
+ .kpt-mention:focus-visible {
4305
+ outline: 2px solid var(--kpt-color-focus-ring);
4306
+ outline-offset: 2px;
4307
+ }
4308
+ @media (prefers-reduced-motion: reduce) {
4309
+ .kpt-mention {
4310
+ transition: none;
4311
+ }
4312
+ }
4313
+ .kpt-mention {
4314
+ /* Wzmianka bez karty: wyróżniona, ale bez obietnicy, że coś się pod nią kryje — więc także
4315
+ bez kropkowanej linii, która tę obietnicę składa. */
4316
+ }
4317
+ .kpt-mention[data-plain] {
4318
+ color: var(--kpt-color-text);
4319
+ font-weight: var(--kpt-font-weight-medium, 500);
4320
+ text-decoration: none;
4321
+ cursor: inherit;
4322
+ }
4323
+ }
4324
+
4110
4325
  /* src/lib/menu/menu.component.scss */
4111
4326
  @charset "UTF-8";
4112
4327
  @layer kpt.components {
@@ -4371,6 +4586,9 @@
4371
4586
  max-height: 16rem;
4372
4587
  overflow-y: auto;
4373
4588
  }
4589
+ .kpt-order-list__items.is-dragover {
4590
+ background: var(--kpt-color-muted);
4591
+ }
4374
4592
  .kpt-order-list__item {
4375
4593
  padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
4376
4594
  border-radius: var(--kpt-radius-sm, 0.25rem);
@@ -4384,6 +4602,19 @@
4384
4602
  background: color-mix(in oklch, var(--kpt-color-primary) 16%, transparent);
4385
4603
  color: var(--kpt-color-primary);
4386
4604
  }
4605
+ .kpt-order-list__item--draggable {
4606
+ cursor: grab;
4607
+ user-select: none;
4608
+ }
4609
+ .kpt-order-list__item--draggable:active {
4610
+ cursor: grabbing;
4611
+ }
4612
+ .kpt-order-list__item--dragover {
4613
+ box-shadow: inset 0 2px 0 0 var(--kpt-color-primary);
4614
+ }
4615
+ .kpt-order-list__item.is-dragging {
4616
+ opacity: 0.5;
4617
+ }
4387
4618
  }
4388
4619
 
4389
4620
  /* src/lib/paginator/paginator.component.scss */
@@ -4570,6 +4801,36 @@
4570
4801
  }
4571
4802
  }
4572
4803
 
4804
+ /* src/lib/peek/peek.component.scss */
4805
+ @charset "UTF-8";
4806
+ @layer kpt.components {
4807
+ /* Host i zaczepienie są `inline`, nie `inline-flex`: karta wisi najczęściej na wzmiance w środku
4808
+ akapitu, a element flex nie łamie się razem z wierszem tekstu. */
4809
+ .kpt-peek-host,
4810
+ .kpt-peek__origin {
4811
+ display: inline;
4812
+ }
4813
+ .kpt-peek__panel {
4814
+ max-inline-size: 22rem;
4815
+ padding: var(--kpt-space-4, 1rem);
4816
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
4817
+ border-radius: var(--kpt-radius-md, 0.5rem);
4818
+ background: var(--kpt-color-surface-raised);
4819
+ color: var(--kpt-color-on-surface);
4820
+ font-size: var(--kpt-font-size-sm, 0.875rem);
4821
+ box-shadow: var(--kpt-elevation-3);
4822
+ /* Karta otwarta hoverem nie pokazuje obwódki fokusu — dostaje ją dopiero przypięta, bo wtedy
4823
+ naprawdę trzyma fokus i użytkownik musi wiedzieć, gdzie jest. */
4824
+ }
4825
+ .kpt-peek__panel:focus {
4826
+ outline: none;
4827
+ }
4828
+ .kpt-peek__panel[data-pinned]:focus-visible {
4829
+ outline: 2px solid var(--kpt-color-focus-ring);
4830
+ outline-offset: 2px;
4831
+ }
4832
+ }
4833
+
4573
4834
  /* src/lib/pick-list/pick-list.component.scss */
4574
4835
  @layer kpt.components {
4575
4836
  .kpt-pick-list-host {
@@ -4605,6 +4866,9 @@
4605
4866
  overflow-y: auto;
4606
4867
  flex: 1;
4607
4868
  }
4869
+ .kpt-pick-list__items.is-dragover {
4870
+ background: var(--kpt-color-muted);
4871
+ }
4608
4872
  .kpt-pick-list__item {
4609
4873
  padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
4610
4874
  border-radius: var(--kpt-radius-sm, 0.25rem);
@@ -4618,6 +4882,19 @@
4618
4882
  background: color-mix(in oklch, var(--kpt-color-primary) 16%, transparent);
4619
4883
  color: var(--kpt-color-primary);
4620
4884
  }
4885
+ .kpt-pick-list__item--draggable {
4886
+ cursor: grab;
4887
+ user-select: none;
4888
+ }
4889
+ .kpt-pick-list__item--draggable:active {
4890
+ cursor: grabbing;
4891
+ }
4892
+ .kpt-pick-list__item--dragover {
4893
+ box-shadow: inset 0 2px 0 0 var(--kpt-color-primary);
4894
+ }
4895
+ .kpt-pick-list__item.is-dragging {
4896
+ opacity: 0.5;
4897
+ }
4621
4898
  .kpt-pick-list__controls {
4622
4899
  display: flex;
4623
4900
  flex-direction: column;
@@ -4669,66 +4946,179 @@
4669
4946
  /* src/lib/progress/progress.component.scss */
4670
4947
  @layer kpt.components {
4671
4948
  .kpt-progress-host {
4672
- display: block;
4949
+ display: flex;
4950
+ flex-wrap: wrap;
4951
+ align-items: center;
4952
+ column-gap: var(--kpt-progress-gap, 0.5rem);
4953
+ row-gap: 0.25rem;
4673
4954
  --_color: var(--kpt-color-primary);
4955
+ --_thickness: var(--kpt-progress-thickness-md, 0.5rem);
4956
+ --_stroke: 4;
4674
4957
  }
4675
- .kpt-progress__linear[data-variant=success],
4676
- .kpt-progress__circular[data-variant=success] {
4958
+ .kpt-progress-host[data-variant=success] {
4677
4959
  --_color: var(--kpt-color-success);
4678
4960
  }
4679
- .kpt-progress__linear[data-variant=danger],
4680
- .kpt-progress__circular[data-variant=danger] {
4961
+ .kpt-progress-host[data-variant=danger] {
4681
4962
  --_color: var(--kpt-color-danger);
4682
4963
  }
4683
- .kpt-progress__linear[data-variant=warning],
4684
- .kpt-progress__circular[data-variant=warning] {
4964
+ .kpt-progress-host[data-variant=warning] {
4685
4965
  --_color: var(--kpt-color-warning);
4686
4966
  }
4687
- .kpt-progress__linear[data-variant=info],
4688
- .kpt-progress__circular[data-variant=info] {
4967
+ .kpt-progress-host[data-variant=info] {
4689
4968
  --_color: var(--kpt-color-info);
4690
4969
  }
4970
+ .kpt-progress-host[data-size=sm] {
4971
+ --_thickness: var(--kpt-progress-thickness-sm, 0.25rem);
4972
+ --_stroke: 3;
4973
+ }
4974
+ .kpt-progress-host[data-size=lg] {
4975
+ --_thickness: var(--kpt-progress-thickness-lg, 0.75rem);
4976
+ --_stroke: 6;
4977
+ }
4978
+ .kpt-progress-host[data-type=linear][data-label-position=top] .kpt-progress__label {
4979
+ order: 1;
4980
+ }
4981
+ .kpt-progress-host[data-type=linear][data-label-position=top] .kpt-progress__value {
4982
+ order: 2;
4983
+ margin-inline-start: auto;
4984
+ }
4985
+ .kpt-progress-host[data-type=linear][data-label-position=top] .kpt-progress__linear {
4986
+ order: 3;
4987
+ flex: 1 1 100%;
4988
+ }
4989
+ .kpt-progress-host[data-type=linear][data-label-position=bottom] .kpt-progress__linear {
4990
+ order: 1;
4991
+ flex: 1 1 100%;
4992
+ }
4993
+ .kpt-progress-host[data-type=linear][data-label-position=bottom] .kpt-progress__label {
4994
+ order: 2;
4995
+ }
4996
+ .kpt-progress-host[data-type=linear][data-label-position=bottom] .kpt-progress__value {
4997
+ order: 3;
4998
+ margin-inline-start: auto;
4999
+ }
5000
+ .kpt-progress-host[data-type=linear][data-label-position=end] .kpt-progress__label {
5001
+ order: 1;
5002
+ flex: 1 1 100%;
5003
+ }
5004
+ .kpt-progress-host[data-type=linear][data-label-position=end] .kpt-progress__linear {
5005
+ order: 2;
5006
+ flex: 1 1 0;
5007
+ }
5008
+ .kpt-progress-host[data-type=linear][data-label-position=end] .kpt-progress__value {
5009
+ order: 3;
5010
+ flex: 0 0 auto;
5011
+ }
5012
+ .kpt-progress__label {
5013
+ color: var(--kpt-progress-label-fg, var(--kpt-color-on-surface-muted));
5014
+ font-size: var(--kpt-font-size-sm);
5015
+ }
5016
+ .kpt-progress__value {
5017
+ color: var(--kpt-progress-value-fg, var(--kpt-color-on-surface));
5018
+ font-size: var(--kpt-font-size-sm);
5019
+ font-variant-numeric: tabular-nums;
5020
+ text-align: center;
5021
+ line-height: 1.2;
5022
+ }
4691
5023
  .kpt-progress__linear {
4692
- height: 0.5rem;
5024
+ position: relative;
5025
+ height: var(--_thickness);
4693
5026
  width: 100%;
4694
5027
  border-radius: var(--kpt-radius-full, 9999px);
4695
- background: var(--kpt-color-surface-variant);
5028
+ background: var(--kpt-progress-track-bg, var(--kpt-color-surface-variant));
4696
5029
  overflow: hidden;
4697
5030
  }
5031
+ .kpt-progress__linear .kpt-progress__buffer,
4698
5032
  .kpt-progress__linear .kpt-progress__bar {
4699
- height: 100%;
4700
- background: var(--_color);
5033
+ position: absolute;
5034
+ inset-block: 0;
5035
+ inset-inline-start: 0;
4701
5036
  border-radius: inherit;
5037
+ background: var(--_color);
4702
5038
  transition: width var(--kpt-motion-duration-base, 200ms) var(--kpt-motion-easing-standard);
4703
5039
  }
4704
- .kpt-progress__linear .kpt-progress--indeterminate {
4705
- width: 40% !important;
4706
- animation: kpt-progress-slide 1.2s ease-in-out infinite;
5040
+ .kpt-progress__linear .kpt-progress__buffer {
5041
+ opacity: var(--kpt-progress-buffer-opacity, 0.32);
5042
+ }
5043
+ .kpt-progress__linear .kpt-progress__bar.kpt-progress--indeterminate {
5044
+ width: 40%;
5045
+ transition: none;
5046
+ animation: kpt-progress-slide 1.6s var(--kpt-motion-easing-standard, ease-in-out) infinite;
5047
+ }
5048
+ .kpt-progress-host[data-type=circular] {
5049
+ display: inline-grid;
5050
+ place-items: center;
5051
+ column-gap: 0;
5052
+ row-gap: 0;
5053
+ }
5054
+ .kpt-progress-host[data-type=circular] > * {
5055
+ grid-area: 1/1;
4707
5056
  }
4708
5057
  .kpt-progress__circular {
4709
5058
  transform: rotate(-90deg);
4710
5059
  }
4711
5060
  .kpt-progress__circular .kpt-progress__track {
4712
5061
  fill: none;
4713
- stroke: var(--kpt-color-surface-variant);
4714
- stroke-width: 4;
5062
+ stroke: var(--kpt-progress-track-bg, var(--kpt-color-surface-variant));
5063
+ stroke-width: var(--_stroke);
4715
5064
  }
4716
5065
  .kpt-progress__circular .kpt-progress__bar {
4717
5066
  fill: none;
4718
5067
  stroke: var(--_color);
4719
- stroke-width: 4;
5068
+ stroke-width: var(--_stroke);
4720
5069
  stroke-linecap: round;
4721
5070
  transition: stroke-dashoffset var(--kpt-motion-duration-base, 200ms) var(--kpt-motion-easing-standard);
4722
5071
  }
4723
5072
  .kpt-progress__circular.kpt-progress--indeterminate {
4724
- animation: kpt-spinner-rotate 1s linear infinite;
5073
+ animation: kpt-progress-rotate 1.6s linear infinite;
5074
+ }
5075
+ .kpt-progress__circular.kpt-progress--indeterminate .kpt-progress__bar {
5076
+ transition: none;
5077
+ animation: kpt-progress-dash 1.4s ease-in-out infinite;
4725
5078
  }
4726
5079
  @keyframes kpt-progress-slide {
4727
5080
  0% {
4728
- transform: translateX(-100%);
5081
+ transform: translateX(-110%) scaleX(0.6);
5082
+ }
5083
+ 50% {
5084
+ transform: translateX(90%) scaleX(1.6);
5085
+ }
5086
+ 100% {
5087
+ transform: translateX(280%) scaleX(0.6);
5088
+ }
5089
+ }
5090
+ @keyframes kpt-progress-rotate {
5091
+ from {
5092
+ transform: rotate(-90deg);
5093
+ }
5094
+ to {
5095
+ transform: rotate(270deg);
5096
+ }
5097
+ }
5098
+ @keyframes kpt-progress-dash {
5099
+ 0% {
5100
+ stroke-dasharray: 1 100.5;
5101
+ stroke-dashoffset: 0;
5102
+ }
5103
+ 50% {
5104
+ stroke-dasharray: 70 100.5;
5105
+ stroke-dashoffset: -15;
4729
5106
  }
4730
5107
  100% {
4731
- transform: translateX(350%);
5108
+ stroke-dasharray: 1 100.5;
5109
+ stroke-dashoffset: -99;
5110
+ }
5111
+ }
5112
+ @media (prefers-reduced-motion: reduce) {
5113
+ .kpt-progress__linear .kpt-progress__bar.kpt-progress--indeterminate,
5114
+ .kpt-progress__circular.kpt-progress--indeterminate,
5115
+ .kpt-progress__circular.kpt-progress--indeterminate .kpt-progress__bar {
5116
+ animation-duration: 3.2s;
5117
+ }
5118
+ .kpt-progress__linear .kpt-progress__buffer,
5119
+ .kpt-progress__linear .kpt-progress__bar,
5120
+ .kpt-progress__circular .kpt-progress__bar {
5121
+ transition: none;
4732
5122
  }
4733
5123
  }
4734
5124
  }
@@ -4824,6 +5214,7 @@
4824
5214
  }
4825
5215
 
4826
5216
  /* src/lib/rich-text/rich-text.component.scss */
5217
+ @charset "UTF-8";
4827
5218
  @layer kpt.components {
4828
5219
  .kpt-rich-text-host {
4829
5220
  display: block;
@@ -5173,6 +5564,38 @@
5173
5564
  font-size: var(--kpt-font-size-xs, 0.75rem);
5174
5565
  color: var(--kpt-color-on-surface-muted);
5175
5566
  }
5567
+ /* Lista podpowiedzi wzmianek — ten sam wygląd co menu kontekstowe tabeli, bo obie wychodzą
5568
+ spod kursora w środku treści i mają się czytać jak jedna rodzina. */
5569
+ .kpt-rt-mentions {
5570
+ min-inline-size: 12rem;
5571
+ max-inline-size: 20rem;
5572
+ margin: 0;
5573
+ padding: 0.25rem;
5574
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
5575
+ border-radius: var(--kpt-radius-md, 0.5rem);
5576
+ background: var(--kpt-color-surface-raised);
5577
+ list-style: none;
5578
+ box-shadow: var(--kpt-elevation-3);
5579
+ }
5580
+ .kpt-rt-mentions__item {
5581
+ display: flex;
5582
+ align-items: baseline;
5583
+ gap: var(--kpt-space-2, 0.5rem);
5584
+ padding: var(--kpt-space-1, 0.25rem) var(--kpt-space-2, 0.5rem);
5585
+ border-radius: var(--kpt-radius-sm, 0.25rem);
5586
+ font-size: var(--kpt-font-size-sm, 0.875rem);
5587
+ cursor: pointer;
5588
+ /* Podświetlenie idzie z klawiatury i z kursora tym samym atrybutem: lista ma jedną
5589
+ aktywną pozycję, więc Enter zawsze wstawia to, co widać jako wybrane. */
5590
+ }
5591
+ .kpt-rt-mentions__item[data-active] {
5592
+ background: var(--kpt-color-surface-hover);
5593
+ }
5594
+ .kpt-rt-mentions__kind {
5595
+ margin-inline-start: auto;
5596
+ color: var(--kpt-color-text-muted);
5597
+ font-size: var(--kpt-font-size-xs, 0.75rem);
5598
+ }
5176
5599
  .kpt-rt-menu {
5177
5600
  min-width: 12rem;
5178
5601
  padding: 0.25rem;
@@ -5538,7 +5961,7 @@
5538
5961
  }
5539
5962
  .kpt-skeleton {
5540
5963
  display: block;
5541
- background: linear-gradient(90deg, var(--kpt-color-surface-variant) 25%, color-mix(in oklch, var(--kpt-color-surface-variant) 60%, var(--kpt-color-surface)) 50%, var(--kpt-color-surface-variant) 75%);
5964
+ background: linear-gradient(90deg, var(--kpt-color-surface-variant) 25%, color-mix(in oklab, var(--kpt-color-surface-variant) 60%, var(--kpt-color-surface)) 50%, var(--kpt-color-surface-variant) 75%);
5542
5965
  background-size: 200% 100%;
5543
5966
  border-radius: var(--kpt-radius-md, 0.5rem);
5544
5967
  animation: kpt-skeleton-shimmer 1.4s ease-in-out infinite;
@@ -6869,6 +7292,810 @@
6869
7292
  }
6870
7293
  }
6871
7294
 
7295
+ /* chat/src/lib/chat-composer.component.scss */
7296
+ @charset "UTF-8";
7297
+ @layer kpt.components {
7298
+ .kpt-chat-composer-host {
7299
+ display: block;
7300
+ border-block-start: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7301
+ background: var(--kpt-color-surface);
7302
+ }
7303
+ .kpt-chat-composer {
7304
+ display: flex;
7305
+ align-items: flex-end;
7306
+ gap: var(--kpt-space-2, 0.5rem);
7307
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7308
+ }
7309
+ .kpt-chat-composer__field {
7310
+ --_max-rows: 6;
7311
+ --_line: 1.5em;
7312
+ flex: 1 1 auto;
7313
+ min-width: 0;
7314
+ /* Wysokość ustawia skrypt od treści; ten limit zamienia dalszy wzrost na przewijanie. */
7315
+ max-block-size: calc(var(--_max-rows) * var(--_line) + var(--kpt-space-2, 0.5rem));
7316
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7317
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7318
+ border-radius: var(--kpt-radius-lg, 0.75rem);
7319
+ background: var(--kpt-color-surface-variant);
7320
+ color: var(--kpt-color-text);
7321
+ font: inherit;
7322
+ font-size: var(--kpt-font-size-sm, 0.875rem);
7323
+ line-height: 1.5;
7324
+ /* Zmiana rozmiaru myszą kłóciłaby się z autorośnięciem — wysokość liczy komponent. */
7325
+ resize: none;
7326
+ overflow-y: auto;
7327
+ }
7328
+ .kpt-chat-composer__field::placeholder {
7329
+ color: var(--kpt-color-text-subtle);
7330
+ }
7331
+ .kpt-chat-composer__field:focus-visible {
7332
+ outline: 2px solid var(--kpt-color-focus-ring);
7333
+ outline-offset: -1px;
7334
+ }
7335
+ .kpt-chat-composer__field:disabled {
7336
+ opacity: 0.6;
7337
+ cursor: not-allowed;
7338
+ }
7339
+ /* Pole pliku jest sterowane przyciskiem; natywny widget nie da się ostylować. */
7340
+ .kpt-chat-composer__picker {
7341
+ display: none;
7342
+ }
7343
+ .kpt-chat-composer__reply {
7344
+ display: flex;
7345
+ align-items: center;
7346
+ gap: var(--kpt-space-2, 0.5rem);
7347
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7348
+ border-inline-start: 2px solid var(--kpt-color-primary);
7349
+ background: var(--kpt-color-surface-variant);
7350
+ }
7351
+ .kpt-chat-composer__reply-body {
7352
+ display: flex;
7353
+ flex: 1 1 auto;
7354
+ flex-direction: column;
7355
+ min-width: 0;
7356
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7357
+ }
7358
+ .kpt-chat-composer__reply-author {
7359
+ color: var(--kpt-color-primary);
7360
+ font-weight: var(--kpt-font-weight-semibold, 600);
7361
+ }
7362
+ .kpt-chat-composer__reply-text {
7363
+ color: var(--kpt-color-text-muted);
7364
+ overflow: hidden;
7365
+ text-overflow: ellipsis;
7366
+ white-space: nowrap;
7367
+ }
7368
+ .kpt-chat-composer__files {
7369
+ display: flex;
7370
+ flex-wrap: wrap;
7371
+ gap: var(--kpt-space-2, 0.5rem);
7372
+ margin: 0;
7373
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem) 0;
7374
+ list-style: none;
7375
+ }
7376
+ .kpt-chat-composer__file {
7377
+ display: inline-flex;
7378
+ align-items: center;
7379
+ gap: 0.25rem;
7380
+ max-inline-size: 14rem;
7381
+ padding-inline-start: var(--kpt-space-2, 0.5rem);
7382
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7383
+ border-radius: var(--kpt-radius-full, 9999px);
7384
+ background: var(--kpt-color-surface-variant);
7385
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7386
+ }
7387
+ .kpt-chat-composer__file-name {
7388
+ overflow: hidden;
7389
+ text-overflow: ellipsis;
7390
+ white-space: nowrap;
7391
+ }
7392
+ }
7393
+
7394
+ /* chat/src/lib/chat-dock.component.scss */
7395
+ @charset "UTF-8";
7396
+ @layer kpt.components {
7397
+ /* Host nie może tworzyć własnego pudełka: FAB i panel są `fixed`, a host wstawiony
7398
+ w środek layoutu aplikacji zajmowałby miejsce w przepływie. */
7399
+ .kpt-chat-dock-host {
7400
+ display: contents;
7401
+ }
7402
+ .kpt-chat-dock__trigger {
7403
+ --_offset: var(--kpt-space-6, 1.5rem);
7404
+ position: fixed;
7405
+ inset-block-end: var(--_offset);
7406
+ z-index: var(--kpt-z-overlay, 1000);
7407
+ }
7408
+ .kpt-chat-dock__panel {
7409
+ --_width: 22.5rem;
7410
+ --_offset: var(--kpt-space-6, 1.5rem);
7411
+ /* Opóźnienie ukrycia: przy zamykaniu panel ma najpierw zjechać, przy otwieraniu
7412
+ pojawia się natychmiast. Ta sama sztuczka co w kpt-drawer. */
7413
+ --_vis-delay: var(--kpt-motion-duration-base, 200ms);
7414
+ position: fixed;
7415
+ /* 4,5rem to wysokość FAB-a plus prześwit — panel stoi nad przyciskiem. */
7416
+ inset-block-end: calc(var(--_offset) + 4.5rem);
7417
+ z-index: var(--kpt-z-overlay, 1000);
7418
+ display: flex;
7419
+ flex-direction: column;
7420
+ inline-size: var(--_width);
7421
+ max-inline-size: calc(100vw - 2 * var(--_offset));
7422
+ block-size: min(32rem, 100dvh - 9rem);
7423
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7424
+ border-radius: var(--kpt-radius-lg, 0.75rem);
7425
+ background: var(--kpt-color-surface);
7426
+ color: var(--kpt-color-text);
7427
+ overflow: hidden;
7428
+ box-shadow: var(--kpt-elevation-3);
7429
+ /* Stan zamknięty siedzi w regule bazowej, nie pod `[data-position]`: Angular ustawia
7430
+ atrybuty o klatkę za późno, więc panel mrugnąłby otwarty przy pierwszym renderze.
7431
+ `visibility` zamiast `display: none` — przejścia nie da się zanimować z `none`. */
7432
+ visibility: hidden;
7433
+ opacity: 0;
7434
+ transform: translateY(var(--kpt-space-4, 1rem)) scale(0.98);
7435
+ transform-origin: bottom center;
7436
+ 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);
7437
+ }
7438
+ .kpt-chat-dock__panel.is-open {
7439
+ visibility: visible;
7440
+ opacity: 1;
7441
+ transform: none;
7442
+ transition-delay: 0s;
7443
+ }
7444
+ .kpt-chat-dock__header {
7445
+ display: flex;
7446
+ flex: 0 0 auto;
7447
+ align-items: center;
7448
+ gap: var(--kpt-space-2, 0.5rem);
7449
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-2, 0.5rem) var(--kpt-space-2, 0.5rem) var(--kpt-space-4, 1rem);
7450
+ border-block-end: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7451
+ }
7452
+ .kpt-chat-dock__title {
7453
+ flex: 1 1 auto;
7454
+ font-weight: var(--kpt-font-weight-semibold, 600);
7455
+ }
7456
+ .kpt-chat-dock__content {
7457
+ display: flex;
7458
+ flex: 1 1 auto;
7459
+ flex-direction: column;
7460
+ min-height: 0;
7461
+ }
7462
+ /* Pozycja pozioma — logiczne własności, więc RTL odwraca róg bez osobnej reguły.
7463
+ Odsunięcie od rogu jest wspólne dla obu osi i steruje nim input `offset`: aplikacja
7464
+ z drugim widgetem w rogu (np. kpt-scroll-top) odsuwa dock, zamiast walczyć ze stylami. */
7465
+ .kpt-chat-dock__trigger,
7466
+ .kpt-chat-dock__panel {
7467
+ inset-inline-end: var(--_offset);
7468
+ }
7469
+ .kpt-chat-dock__trigger[data-position=bottom-start],
7470
+ .kpt-chat-dock__panel[data-position=bottom-start] {
7471
+ inset-inline: var(--_offset) auto;
7472
+ }
7473
+ /*
7474
+ * Współdzielony róg. `kpt-scroll-top` przykleja się dokładnie w to samo miejsce, z tym samym
7475
+ * `--kpt-z-overlay`, więc bez tej reguły FAB czatu znika pod nim w całości — wystaje sama
7476
+ * plakietka nieprzeczytanych, a przycisku nie da się kliknąć.
7477
+ *
7478
+ * Ustępuje przycisk powrotu na górę, bo czat jest akcją główną i większym celem. Odsuwamy go
7479
+ * W BOK, nie do góry: nad FAB-em stoi panel rozmowy, więc przesunięcie w pionie zamieniłoby
7480
+ * jedną kolizję na drugą, gorszą — przycisk lądowałby na treści wiadomości.
7481
+ *
7482
+ * `:has()` na korzeniu dokumentu jest tu jedyną drogą: oba komponenty są `position: fixed`
7483
+ * i nic ich nie łączy w drzewie DOM. Reguła stoi w arkuszu czatu, nie scroll-topu, bo zależność
7484
+ * ma iść w stronę grafu paczek — subpath zna rdzeń, rdzeń nie zna subpathu.
7485
+ *
7486
+ * Dotyczy wyłącznie `bottom-end`: dock po lewej i tak nie koliduje. Gdy aplikacja przestawi
7487
+ * `offset`, sama odpowiada też za ustawienie scroll-topu — ta reguła liczy od wartości domyślnej.
7488
+ */
7489
+ :root:has(.kpt-chat-dock__trigger[data-position=bottom-end]) .kpt-scroll-top {
7490
+ /* 3,5rem to szerokość FAB-a, 0,75rem prześwit między przyciskami. */
7491
+ right: calc(var(--kpt-space-6, 1.5rem) + 4.25rem);
7492
+ }
7493
+ /* Na telefonie panel wchodzi na pełny ekran: 22,5 rem w oknie szerokim na 20 rem
7494
+ to nie panel, tylko szpara. FAB zostaje na wierzchu jako przycisk zamknięcia. */
7495
+ @media (width < 640px) {
7496
+ .kpt-chat-dock__panel {
7497
+ inset: 0;
7498
+ inline-size: auto;
7499
+ max-inline-size: none;
7500
+ block-size: auto;
7501
+ border: 0;
7502
+ border-radius: 0;
7503
+ transform: translateY(var(--kpt-space-6, 1.5rem));
7504
+ }
7505
+ }
7506
+ @media (prefers-reduced-motion: reduce) {
7507
+ .kpt-chat-dock__panel {
7508
+ transition: opacity var(--kpt-motion-duration-fast, 120ms) linear, visibility 0s linear var(--_vis-delay);
7509
+ transform: none;
7510
+ }
7511
+ }
7512
+ }
7513
+
7514
+ /* chat/src/lib/chat-message.component.scss */
7515
+ @charset "UTF-8";
7516
+ @layer kpt.components {
7517
+ .kpt-chat-message-host {
7518
+ display: block;
7519
+ }
7520
+ /* Komunikat systemowy nie jest wypowiedzią — nie dostaje bąbla ani kolumny awatara. */
7521
+ .kpt-chat-message__system {
7522
+ margin: var(--kpt-space-2, 0.5rem) 0;
7523
+ text-align: center;
7524
+ color: var(--kpt-color-text-muted);
7525
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7526
+ }
7527
+ .kpt-chat-message__row {
7528
+ display: flex;
7529
+ align-items: flex-end;
7530
+ gap: var(--kpt-space-2, 0.5rem);
7531
+ /* Własna wiadomość: odwrócony kierunek dosuwa stos do prawej krawędzi, a kolumnę
7532
+ akcji (strzałka ponowienia) wypycha na zewnątrz bąbla — po stronie treści. */
7533
+ }
7534
+ .kpt-chat-message__row[data-align=end] {
7535
+ flex-direction: row-reverse;
7536
+ }
7537
+ /* Stała szerokość także wtedy, gdy awatara nie ma: bąble w grupie nie skaczą w poziomie. */
7538
+ .kpt-chat-message__avatar {
7539
+ flex: 0 0 2rem;
7540
+ align-self: flex-end;
7541
+ }
7542
+ .kpt-chat-message__stack {
7543
+ display: flex;
7544
+ flex-direction: column;
7545
+ min-width: 0;
7546
+ max-inline-size: min(42rem, 78%);
7547
+ }
7548
+ .kpt-chat-message__author {
7549
+ margin-inline-start: var(--kpt-space-3, 0.75rem);
7550
+ margin-block-end: var(--kpt-space-1, 0.25rem);
7551
+ color: var(--kpt-color-text-muted);
7552
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7553
+ font-weight: var(--kpt-font-weight-medium, 500);
7554
+ }
7555
+ .kpt-chat-message__bubble {
7556
+ /* Motyw jest monochromatyczny — różnicę między bąblami robi jasność, nie odcień.
7557
+ Wariant przychodzący siada na `muted`, bo `surface-variant` dzieli od tła listy
7558
+ 1,5% jasności i w jasnym motywie bąbel po prostu znika. */
7559
+ --_bg: var(--kpt-color-muted);
7560
+ --_fg: var(--kpt-color-on-muted);
7561
+ --_radius: var(--kpt-radius-lg, 0.75rem);
7562
+ --_radius-tight: var(--kpt-radius-sm, 0.25rem);
7563
+ position: relative;
7564
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7565
+ border-radius: var(--_radius);
7566
+ background: var(--_bg);
7567
+ color: var(--_fg);
7568
+ font-size: var(--kpt-font-size-sm, 0.875rem);
7569
+ line-height: var(--kpt-font-lineHeight-normal, 1.5);
7570
+ /* Cichszy wariant własnej wiadomości — dla rozmów, w których pełny kolor po prawej
7571
+ stronie jest za mocny. */
7572
+ }
7573
+ .kpt-chat-message__bubble[data-variant=primary] {
7574
+ --_bg: var(--kpt-color-primary-subtle);
7575
+ --_fg: var(--kpt-color-text);
7576
+ }
7577
+ .kpt-chat-message__bubble[data-variant=solid] {
7578
+ --_bg: var(--kpt-color-primary);
7579
+ --_fg: var(--kpt-color-on-primary);
7580
+ }
7581
+ .kpt-chat-message__bubble[data-variant=ghost] {
7582
+ --_bg: transparent;
7583
+ --_fg: var(--kpt-color-text);
7584
+ padding-inline: 0;
7585
+ }
7586
+ .kpt-chat-message__bubble {
7587
+ /* Nieudana wysyłka. Obwódka wewnętrznym cieniem, nie `border` — ramka przesunęłaby
7588
+ treść o piksel w chwili, w której status się zmienia. */
7589
+ }
7590
+ .kpt-chat-message__bubble[data-status=failed] {
7591
+ --_bg: var(--kpt-color-danger-subtle);
7592
+ /* Kolor tekstu też musi wrócić: własna wiadomość jest domyślnie `solid`, więc bez
7593
+ tego biały napis zostałby na bladoróżowym tle błędu. */
7594
+ --_fg: var(--kpt-color-text);
7595
+ box-shadow: inset 0 0 0 var(--kpt-border-width-thin, 1px) var(--kpt-color-danger-border);
7596
+ }
7597
+ .kpt-chat-message__bubble {
7598
+ /* Zapadnięty narożnik od strony nadawcy scala bąble w jedną kolumnę wypowiedzi. */
7599
+ }
7600
+ .kpt-chat-message__bubble[data-align=end][data-position=first] {
7601
+ border-end-end-radius: var(--_radius-tight);
7602
+ }
7603
+ .kpt-chat-message__bubble[data-align=end][data-position=middle] {
7604
+ border-start-end-radius: var(--_radius-tight);
7605
+ border-end-end-radius: var(--_radius-tight);
7606
+ }
7607
+ .kpt-chat-message__bubble[data-align=end][data-position=last] {
7608
+ border-start-end-radius: var(--_radius-tight);
7609
+ }
7610
+ .kpt-chat-message__bubble[data-align=start][data-position=first] {
7611
+ border-end-start-radius: var(--_radius-tight);
7612
+ }
7613
+ .kpt-chat-message__bubble[data-align=start][data-position=middle] {
7614
+ border-start-start-radius: var(--_radius-tight);
7615
+ border-end-start-radius: var(--_radius-tight);
7616
+ }
7617
+ .kpt-chat-message__bubble[data-align=start][data-position=last] {
7618
+ border-start-start-radius: var(--_radius-tight);
7619
+ }
7620
+ .kpt-chat-message__bubble {
7621
+ /* Narożnik pod ogonkiem jest prosty. Zaokrąglony odsuwał się od ogonka łukiem i między
7622
+ nimi prześwitywało tło listy — ogonek czytał się jak osobna strzałka doklejona obok
7623
+ bąbla, a nie jak jego przedłużenie. Bramka na `[data-tail]`: bąble bez ogonka (środek
7624
+ grupy, `ghost`, `system`, `[tail]="false"`) mają zostać zaokrąglone. */
7625
+ }
7626
+ .kpt-chat-message__bubble[data-tail][data-align=end] {
7627
+ border-end-end-radius: 0;
7628
+ }
7629
+ .kpt-chat-message__bubble[data-tail][data-align=start] {
7630
+ border-end-start-radius: 0;
7631
+ }
7632
+ .kpt-chat-message__bubble {
7633
+ /* Ogonek wycięty maską, nie obróconym kwadratem: obrót zostawia widoczny szew
7634
+ na półprzezroczystym tle i nie daje wklęsłego łuku. */
7635
+ }
7636
+ .kpt-chat-message__bubble[data-tail]::after {
7637
+ content: "";
7638
+ position: absolute;
7639
+ inset-block-end: 0;
7640
+ inline-size: 0.75rem;
7641
+ block-size: 0.75rem;
7642
+ background: var(--_bg);
7643
+ mask: radial-gradient(circle at 100% 0, transparent 0 0.75rem, #000 0.75rem);
7644
+ }
7645
+ .kpt-chat-message__bubble[data-tail][data-align=end]::after {
7646
+ inset-inline-end: -0.5rem;
7647
+ }
7648
+ .kpt-chat-message__bubble {
7649
+ /* Odbicie zamiast drugiej maski — obraca się razem z `inset-inline`, więc RTL
7650
+ nie wymaga osobnej reguły. */
7651
+ }
7652
+ .kpt-chat-message__bubble[data-tail][data-align=start]::after {
7653
+ inset-inline-start: -0.5rem;
7654
+ transform: scaleX(-1);
7655
+ }
7656
+ .kpt-chat-message__bubble {
7657
+ /* Reakcje wychodzą poza dolną krawędź, więc bąbel musi im zostawić miejsce. */
7658
+ }
7659
+ .kpt-chat-message__bubble:has(.kpt-chat-message__reactions) {
7660
+ margin-block-end: 0.875rem;
7661
+ }
7662
+ .kpt-chat-message__quote {
7663
+ display: flex;
7664
+ flex-direction: column;
7665
+ gap: 0.125rem;
7666
+ width: 100%;
7667
+ margin-block-end: var(--kpt-space-1, 0.25rem);
7668
+ padding: var(--kpt-space-1, 0.25rem) var(--kpt-space-2, 0.5rem);
7669
+ border: 0;
7670
+ border-inline-start: 2px solid var(--kpt-color-primary);
7671
+ border-radius: var(--kpt-radius-sm, 0.25rem);
7672
+ background: color-mix(in oklch, currentColor 8%, transparent);
7673
+ color: inherit;
7674
+ font: inherit;
7675
+ text-align: start;
7676
+ cursor: pointer;
7677
+ }
7678
+ .kpt-chat-message__quote-author {
7679
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7680
+ font-weight: var(--kpt-font-weight-semibold, 600);
7681
+ }
7682
+ .kpt-chat-message__quote-text {
7683
+ display: -webkit-box;
7684
+ -webkit-box-orient: vertical;
7685
+ -webkit-line-clamp: 2;
7686
+ overflow: hidden;
7687
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7688
+ opacity: 0.8;
7689
+ }
7690
+ .kpt-chat-message__text {
7691
+ margin: 0;
7692
+ /* Kompozytor łamie linie na Shift+Enter — bez `pre-wrap` zlepiłyby się w akapit. */
7693
+ white-space: pre-wrap;
7694
+ overflow-wrap: anywhere;
7695
+ }
7696
+ .kpt-chat-message__files {
7697
+ display: flex;
7698
+ flex-direction: column;
7699
+ gap: var(--kpt-space-1, 0.25rem);
7700
+ margin: var(--kpt-space-2, 0.5rem) 0 0;
7701
+ padding: 0;
7702
+ list-style: none;
7703
+ }
7704
+ .kpt-chat-message__file {
7705
+ display: flex;
7706
+ align-items: center;
7707
+ gap: var(--kpt-space-2, 0.5rem);
7708
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7709
+ }
7710
+ .kpt-chat-message__file-thumb {
7711
+ width: 2rem;
7712
+ height: 2rem;
7713
+ border-radius: var(--kpt-radius-sm, 0.25rem);
7714
+ object-fit: cover;
7715
+ }
7716
+ .kpt-chat-message__file-name {
7717
+ color: inherit;
7718
+ overflow: hidden;
7719
+ text-overflow: ellipsis;
7720
+ white-space: nowrap;
7721
+ }
7722
+ .kpt-chat-message__file-size {
7723
+ color: inherit;
7724
+ opacity: 0.6;
7725
+ }
7726
+ .kpt-chat-message__meta {
7727
+ display: flex;
7728
+ align-items: center;
7729
+ justify-content: flex-end;
7730
+ gap: 0.25rem;
7731
+ margin-block-start: 0.125rem;
7732
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7733
+ opacity: 0.7;
7734
+ }
7735
+ .kpt-chat-message__status {
7736
+ /* Ikona ma 1em, a wiersz meta jest już zmniejszony — bez tego status ginie. */
7737
+ font-size: 0.875rem;
7738
+ }
7739
+ /* Kolor statusu schodzi z `data-status` bąbla, a nie z drugiego takiego atrybutu na ikonie:
7740
+ jeden stan w DOM znaczy, że oba porty nie mają czego rozjechać. */
7741
+ .kpt-chat-message__bubble[data-status=read] .kpt-chat-message__status {
7742
+ color: var(--kpt-color-primary);
7743
+ opacity: 1;
7744
+ }
7745
+ /* Na bąblu `solid` akcent jest kolorem TŁA, więc „przeczytano" zniknęłoby w nim
7746
+ bez śladu. Tam wyróżnia je pełne krycie własnego koloru tekstu. */
7747
+ .kpt-chat-message__bubble[data-variant=solid][data-status=read] .kpt-chat-message__status {
7748
+ color: currentcolor;
7749
+ }
7750
+ .kpt-chat-message__bubble[data-status=failed] .kpt-chat-message__status {
7751
+ color: var(--kpt-color-danger);
7752
+ opacity: 1;
7753
+ }
7754
+ .kpt-chat-message__reactions {
7755
+ position: absolute;
7756
+ inset-block-end: -0.75rem;
7757
+ display: flex;
7758
+ gap: 0.25rem;
7759
+ margin: 0;
7760
+ padding: 0;
7761
+ list-style: none;
7762
+ }
7763
+ .kpt-chat-message__reactions[data-align=end] {
7764
+ inset-inline-end: var(--kpt-space-2, 0.5rem);
7765
+ }
7766
+ .kpt-chat-message__reactions[data-align=start] {
7767
+ inset-inline-start: var(--kpt-space-2, 0.5rem);
7768
+ }
7769
+ .kpt-chat-message__reaction {
7770
+ display: inline-flex;
7771
+ align-items: center;
7772
+ gap: 0.125rem;
7773
+ padding: 0.0625rem 0.375rem;
7774
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7775
+ border-radius: var(--kpt-radius-full, 9999px);
7776
+ background: var(--kpt-color-surface-raised);
7777
+ color: var(--kpt-color-text);
7778
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7779
+ line-height: 1.25;
7780
+ box-shadow: var(--kpt-elevation-1);
7781
+ }
7782
+ .kpt-chat-message__reaction[data-reacted] {
7783
+ border-color: var(--kpt-color-primary-border);
7784
+ background: var(--kpt-color-primary-subtle);
7785
+ }
7786
+ .kpt-chat-message__reaction-count {
7787
+ opacity: 0.7;
7788
+ }
7789
+ .kpt-chat-message__actions {
7790
+ display: flex;
7791
+ flex: 0 0 auto;
7792
+ align-items: center;
7793
+ gap: 0.125rem;
7794
+ /* Odpowiedz i zareaguj wchodzą pod kursorem oraz przy fokusie — inaczej lista
7795
+ wyglądałaby jak panel sterowania. Ponowienie jest wyjątkiem: musi być widoczne
7796
+ zawsze, bo bez niego nieudana wiadomość nie ma czym o sobie powiedzieć. */
7797
+ }
7798
+ .kpt-chat-message__actions .kpt-icon-button-host {
7799
+ opacity: 0;
7800
+ transition: opacity var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard, ease);
7801
+ }
7802
+ .kpt-chat-message__actions .kpt-chat-message__retry {
7803
+ opacity: 1;
7804
+ color: var(--kpt-color-danger);
7805
+ }
7806
+ .kpt-chat-message__row:hover .kpt-chat-message__actions .kpt-icon-button-host,
7807
+ .kpt-chat-message__row:focus-within .kpt-chat-message__actions .kpt-icon-button-host,
7808
+ .kpt-chat-message__actions[data-open] .kpt-icon-button-host {
7809
+ opacity: 1;
7810
+ }
7811
+ /* Kotwica wybieraka — pozycjonuje go względem przycisku „zareaguj". */
7812
+ .kpt-chat-message__react {
7813
+ position: relative;
7814
+ display: inline-flex;
7815
+ }
7816
+ /* Wybierak reakcji: rząd emoji nad przyciskiem. Rozwija się w stronę bąbla, nie od niego:
7817
+ akcje stoją przy zewnętrznej krawędzi listy, więc wybierak wyśrodkowany na przycisku
7818
+ wychodziłby poza viewport (a ten ma `overflow-y: auto`, czyli przycina też w poziomie)
7819
+ — w wąskim docku ucięłoby połowę reakcji. */
7820
+ .kpt-chat-message__picker {
7821
+ position: absolute;
7822
+ inset-block-end: calc(100% + var(--kpt-space-1, 0.25rem));
7823
+ inset-inline-end: 0;
7824
+ z-index: var(--kpt-z-raised, 1);
7825
+ display: flex;
7826
+ gap: 0.125rem;
7827
+ padding: 0.1875rem;
7828
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
7829
+ border-radius: var(--kpt-radius-full, 9999px);
7830
+ background: var(--kpt-color-surface-raised);
7831
+ box-shadow: var(--kpt-elevation-2);
7832
+ }
7833
+ /* Własna wiadomość ma odwrócony wiersz — akcje lądują z lewej, więc wybierak idzie w prawo. */
7834
+ .kpt-chat-message__row[data-align=end] .kpt-chat-message__picker {
7835
+ inset-inline: 0 auto;
7836
+ }
7837
+ .kpt-chat-message__pick {
7838
+ display: inline-flex;
7839
+ align-items: center;
7840
+ justify-content: center;
7841
+ inline-size: 1.75rem;
7842
+ block-size: 1.75rem;
7843
+ padding: 0;
7844
+ border: 0;
7845
+ border-radius: var(--kpt-radius-full, 9999px);
7846
+ background: transparent;
7847
+ font: inherit;
7848
+ font-size: var(--kpt-font-size-md, 1rem);
7849
+ line-height: 1;
7850
+ cursor: pointer;
7851
+ transition: transform var(--kpt-motion-duration-fast, 120ms) var(--kpt-motion-easing-standard, ease);
7852
+ }
7853
+ .kpt-chat-message__pick:hover {
7854
+ background: var(--kpt-color-surface-hover);
7855
+ transform: scale(1.15);
7856
+ }
7857
+ .kpt-chat-message__pick:focus-visible {
7858
+ outline: 2px solid var(--kpt-color-focus-ring);
7859
+ outline-offset: 1px;
7860
+ }
7861
+ .kpt-chat-message__pick {
7862
+ /* Reakcja już dana: ta sama plakietka co pod bąblem, żeby było widać, co się zdejmie. */
7863
+ }
7864
+ .kpt-chat-message__pick[aria-pressed=true] {
7865
+ background: var(--kpt-color-primary-subtle);
7866
+ }
7867
+ @media (prefers-reduced-motion: reduce) {
7868
+ .kpt-chat-message__actions .kpt-icon-button-host,
7869
+ .kpt-chat-message__pick {
7870
+ transition: none;
7871
+ }
7872
+ .kpt-chat-message__pick:hover {
7873
+ transform: none;
7874
+ }
7875
+ }
7876
+ }
7877
+
7878
+ /* chat/src/lib/chat-typing.component.scss */
7879
+ @charset "UTF-8";
7880
+ @layer kpt.components {
7881
+ .kpt-chat-typing-host {
7882
+ display: block;
7883
+ }
7884
+ .kpt-chat-typing {
7885
+ display: flex;
7886
+ align-items: center;
7887
+ gap: var(--kpt-space-2, 0.5rem);
7888
+ /* Wcięcie równe kolumnie awatara — wskaźnik staje tam, gdzie stanie bąbel. */
7889
+ padding-inline-start: calc(2rem + var(--kpt-space-2, 0.5rem));
7890
+ }
7891
+ .kpt-chat-typing__bubble {
7892
+ display: inline-flex;
7893
+ align-items: center;
7894
+ gap: 0.1875rem;
7895
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7896
+ border-radius: var(--kpt-radius-lg, 0.75rem);
7897
+ border-end-start-radius: var(--kpt-radius-sm, 0.25rem);
7898
+ background: var(--kpt-color-surface-variant);
7899
+ }
7900
+ .kpt-chat-typing__dot {
7901
+ width: 0.375rem;
7902
+ height: 0.375rem;
7903
+ border-radius: var(--kpt-radius-full, 9999px);
7904
+ background: var(--kpt-color-text-muted);
7905
+ animation: kpt-chat-typing-bounce 1.2s infinite ease-in-out;
7906
+ }
7907
+ .kpt-chat-typing__dot:nth-child(2) {
7908
+ animation-delay: 150ms;
7909
+ }
7910
+ .kpt-chat-typing__dot:nth-child(3) {
7911
+ animation-delay: 300ms;
7912
+ }
7913
+ .kpt-chat-typing__label {
7914
+ color: var(--kpt-color-text-muted);
7915
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7916
+ }
7917
+ @keyframes kpt-chat-typing-bounce {
7918
+ 0%, 60%, 100% {
7919
+ transform: translateY(0);
7920
+ opacity: 0.5;
7921
+ }
7922
+ 30% {
7923
+ transform: translateY(-0.1875rem);
7924
+ opacity: 1;
7925
+ }
7926
+ }
7927
+ @keyframes kpt-chat-typing-pulse {
7928
+ 0%, 100% {
7929
+ opacity: 0.35;
7930
+ }
7931
+ 50% {
7932
+ opacity: 1;
7933
+ }
7934
+ }
7935
+ /* Skok zamieniamy na pulsowanie, a nie wyłączamy animację: kropki są jedynym
7936
+ nośnikiem informacji „ktoś pisze" i nieruchome nie odróżniają się od ozdoby. */
7937
+ @media (prefers-reduced-motion: reduce) {
7938
+ .kpt-chat-typing__dot {
7939
+ animation-name: kpt-chat-typing-pulse;
7940
+ animation-duration: 1.8s;
7941
+ }
7942
+ }
7943
+ }
7944
+
7945
+ /* chat/src/lib/chat.component.scss */
7946
+ @charset "UTF-8";
7947
+ @layer kpt.components {
7948
+ .kpt-chat-host {
7949
+ --_gap: var(--kpt-space-1, 0.25rem);
7950
+ --_group-gap: var(--kpt-space-4, 1rem);
7951
+ display: flex;
7952
+ flex-direction: column;
7953
+ min-height: 0;
7954
+ background: var(--kpt-color-surface);
7955
+ color: var(--kpt-color-text);
7956
+ }
7957
+ /* Pasek offline. `role="status"` bez `aria-live="assertive"` — utrata sieci ma być
7958
+ zauważona, ale nie przerywać czytania wiadomości w połowie zdania. */
7959
+ .kpt-chat__offline {
7960
+ display: flex;
7961
+ align-items: center;
7962
+ gap: var(--kpt-space-2, 0.5rem);
7963
+ margin: 0;
7964
+ padding: var(--kpt-space-2, 0.5rem) var(--kpt-space-3, 0.75rem);
7965
+ border-block-end: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-warning-border);
7966
+ background: var(--kpt-color-warning-subtle);
7967
+ color: var(--kpt-color-text);
7968
+ font-size: var(--kpt-font-size-sm, 0.875rem);
7969
+ }
7970
+ .kpt-chat__offline-count {
7971
+ margin-inline-start: auto;
7972
+ color: var(--kpt-color-text-muted);
7973
+ font-size: var(--kpt-font-size-xs, 0.75rem);
7974
+ }
7975
+ /* Kontener pozycjonujący dla pigułki „nowe wiadomości" — pigułka ma pływać nad listą,
7976
+ a nie nad całą stroną, więc nie jest `fixed` jak kpt-scroll-top. */
7977
+ .kpt-chat__body {
7978
+ position: relative;
7979
+ display: flex;
7980
+ flex: 1 1 auto;
7981
+ min-height: 0;
7982
+ }
7983
+ .kpt-chat__viewport {
7984
+ display: flex;
7985
+ flex: 1 1 auto;
7986
+ flex-direction: column;
7987
+ gap: var(--_gap);
7988
+ min-height: 0;
7989
+ padding: var(--kpt-space-4, 1rem);
7990
+ overflow-y: auto;
7991
+ overscroll-behavior: contain;
7992
+ }
7993
+ /* Przerwa między grupami: wiadomość otwierająca grupę odsuwa się od poprzedniej.
7994
+ Wewnątrz grupy zostaje ciasny `--_gap`, dzięki czemu bąble czytają się jako jedna
7995
+ wypowiedź, a zapadnięte narożniki mają się do czego przykleić. */
7996
+ .kpt-chat__viewport > .kpt-chat-message-host:has(.kpt-chat-message__bubble[data-position=first]),
7997
+ .kpt-chat__viewport > .kpt-chat-message-host:has(.kpt-chat-message__bubble[data-position=single]) {
7998
+ margin-block-start: var(--_group-gap);
7999
+ }
8000
+ .kpt-chat__viewport > .kpt-chat-message-host:first-child {
8001
+ margin-block-start: 0;
8002
+ }
8003
+ .kpt-chat__empty {
8004
+ margin: auto;
8005
+ padding: var(--kpt-space-8, 2rem);
8006
+ color: var(--kpt-color-text-muted);
8007
+ font-size: var(--kpt-font-size-sm, 0.875rem);
8008
+ text-align: center;
8009
+ }
8010
+ /* Separator dnia w liście i pływająca pigułka to ta sama etykieta w dwóch rolach, więc
8011
+ wygląd mają wspólny. */
8012
+ .kpt-chat__date,
8013
+ .kpt-chat__day {
8014
+ padding: 0.125rem var(--kpt-space-3, 0.75rem);
8015
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-border);
8016
+ border-radius: var(--kpt-radius-full, 9999px);
8017
+ background: var(--kpt-color-surface-raised);
8018
+ color: var(--kpt-color-text-muted);
8019
+ font-size: var(--kpt-font-size-xs, 0.75rem);
8020
+ box-shadow: var(--kpt-elevation-1);
8021
+ }
8022
+ /* Separator jedzie z treścią i nie jest `sticky`: przyklejony zostawał na górze, więc przy
8023
+ dłuższej rozmowie rósł tam stos ramek, a że pigułki różnią się szerokością, starsze
8024
+ wystawały zza najnowszej. Rolę „w jakim dniu jestem" przejęła jedna pływająca pigułka. */
8025
+ .kpt-chat__date {
8026
+ align-self: center;
8027
+ margin: var(--kpt-space-4, 1rem) 0 var(--kpt-space-2, 0.5rem);
8028
+ }
8029
+ /* Pływająca pigułka dnia: jedna na całą listę, z tekstem podmienianym przy przewijaniu.
8030
+ Wisi nad listą, a nie w niej, więc `absolute` względem `.kpt-chat__body` — tak samo jak
8031
+ pigułka „nowe wiadomości" przy dolnej krawędzi. Gaśnie przez `visibility`, nie
8032
+ `display: none`, bo skrypt musi móc zmierzyć jej pas także wtedy, gdy jej nie widać. */
8033
+ .kpt-chat__day {
8034
+ position: absolute;
8035
+ inset-block-start: var(--kpt-space-2, 0.5rem);
8036
+ /* Prawy odstęp kompensuje pasek przewijania: lista oddaje mu kawałek swojej szerokości,
8037
+ więc pigułka wyśrodkowana względem całego pola stałaby odrobinę za bardzo w prawo
8038
+ względem samego tekstu. Logicznie, nie przez `right` — w RTL pasek jest po drugiej
8039
+ stronie i przesunięcie musi iść za nim. */
8040
+ inset-inline: 0 var(--kpt-space-3, 0.75rem);
8041
+ z-index: var(--kpt-z-raised, 1);
8042
+ width: fit-content;
8043
+ /* Margines bloku na zero: to `<p>`, a domyślny margines UA zepchnąłby pigułkę spod
8044
+ swojego `inset-block-start`. */
8045
+ margin: 0 auto;
8046
+ pointer-events: none;
8047
+ /* Pusta pigułka musi mieć tę samą wysokość co pełna: skrypt mierzy jej pas również wtedy,
8048
+ gdy jest zgaszona, a zapadnięty wiersz tekstu dałby za wąski pas i pigułka zapaliłaby się
8049
+ nad etykietą, która właśnie pod nią wjeżdża. Spacja zerowej szerokości trzyma linię. */
8050
+ }
8051
+ .kpt-chat__day::before {
8052
+ content: "​";
8053
+ }
8054
+ .kpt-chat__day[data-hidden] {
8055
+ visibility: hidden;
8056
+ }
8057
+ .kpt-chat__unread {
8058
+ display: flex;
8059
+ align-items: center;
8060
+ gap: var(--kpt-space-3, 0.75rem);
8061
+ margin: var(--kpt-space-4, 1rem) 0 var(--kpt-space-2, 0.5rem);
8062
+ color: var(--kpt-color-primary);
8063
+ font-size: var(--kpt-font-size-xs, 0.75rem);
8064
+ }
8065
+ .kpt-chat__unread::before, .kpt-chat__unread::after {
8066
+ content: "";
8067
+ flex: 1 1 auto;
8068
+ block-size: var(--kpt-border-width-thin, 1px);
8069
+ background: var(--kpt-color-primary-border);
8070
+ }
8071
+ .kpt-chat__jump {
8072
+ position: absolute;
8073
+ inset-block-end: var(--kpt-space-3, 0.75rem);
8074
+ inset-inline: 0;
8075
+ display: inline-flex;
8076
+ align-items: center;
8077
+ gap: var(--kpt-space-1, 0.25rem);
8078
+ width: fit-content;
8079
+ margin-inline: auto;
8080
+ padding: var(--kpt-space-1, 0.25rem) var(--kpt-space-3, 0.75rem);
8081
+ border: var(--kpt-border-width-thin, 1px) solid var(--kpt-color-primary-border);
8082
+ border-radius: var(--kpt-radius-full, 9999px);
8083
+ background: var(--kpt-color-surface-raised);
8084
+ color: var(--kpt-color-primary);
8085
+ font: inherit;
8086
+ font-size: var(--kpt-font-size-xs, 0.75rem);
8087
+ cursor: pointer;
8088
+ box-shadow: var(--kpt-elevation-2);
8089
+ }
8090
+ .kpt-chat__jump:hover {
8091
+ background: var(--kpt-color-surface-hover);
8092
+ }
8093
+ .kpt-chat__jump:focus-visible {
8094
+ outline: 2px solid var(--kpt-color-focus-ring);
8095
+ outline-offset: 2px;
8096
+ }
8097
+ }
8098
+
6872
8099
  /* roadmap/src/lib/roadmap.component.scss */
6873
8100
  @layer kpt.components {
6874
8101
  .kpt-roadmap {
@@ -7077,7 +8304,7 @@
7077
8304
  background: color-mix(in oklch, var(--kpt-color-primary) 10%, transparent);
7078
8305
  }
7079
8306
  .kpt-roadmap__row:hover .kpt-roadmap__label {
7080
- background: color-mix(in oklch, var(--kpt-color-primary) 10%, var(--kpt-color-surface));
8307
+ background: color-mix(in oklab, var(--kpt-color-primary) 10%, var(--kpt-color-surface));
7081
8308
  }
7082
8309
  .kpt-roadmap__row--group {
7083
8310
  background: var(--kpt-color-surface-variant, var(--kpt-color-surface));
@@ -7086,7 +8313,7 @@
7086
8313
  background: var(--kpt-color-surface-variant, var(--kpt-color-surface));
7087
8314
  }
7088
8315
  .kpt-roadmap__row--group:hover, .kpt-roadmap__row--group:hover .kpt-roadmap__label {
7089
- background: color-mix(in oklch, var(--kpt-color-primary) 10%, var(--kpt-color-surface-variant, var(--kpt-color-surface)));
8316
+ background: color-mix(in oklab, var(--kpt-color-primary) 10%, var(--kpt-color-surface-variant, var(--kpt-color-surface)));
7090
8317
  }
7091
8318
  .kpt-roadmap__label {
7092
8319
  position: sticky;