@linktr.ee/messaging-react 3.31.1 → 3.31.2-rc-1785926578
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Card-EWN07XIl.cjs → Card-CF1TZXD5.cjs} +2 -2
- package/dist/{Card-EWN07XIl.cjs.map → Card-CF1TZXD5.cjs.map} +1 -1
- package/dist/{Card-CmlaSw7i.js → Card-CqYFVF6m.js} +2 -2
- package/dist/{Card-CmlaSw7i.js.map → Card-CqYFVF6m.js.map} +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/index-Ct_Jgy8P.cjs +2 -0
- package/dist/index-Ct_Jgy8P.cjs.map +1 -0
- package/dist/index-ilHx7pIO.js +5672 -0
- package/dist/index-ilHx7pIO.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +1 -1
- package/package.json +3 -5
- package/src/components/LinkAttachment/LinkAttachment.stories.tsx +42 -27
- package/src/components/LinkAttachment/LinkAttachment.test.tsx +46 -39
- package/src/components/LinkAttachment/components/Composer/Card.tsx +1 -1
- package/src/components/LinkAttachment/components/Received/Card.tsx +2 -2
- package/src/components/LinkAttachment/components/Sent/Card.tsx +2 -2
- package/src/components/LinkAttachment/components/_shared/BubbleTail.tsx +16 -6
- package/src/components/LinkAttachment/components/_shared/CardBody.tsx +4 -5
- package/src/components/LinkAttachment/components/_shared/CardShell.tsx +27 -19
- package/src/components/LinkAttachment/components/_shared/hex.ts +10 -12
- package/src/components/LinkAttachment/components/_shared/useChinPalette.ts +45 -48
- package/src/components/LinkAttachment/types.ts +6 -5
- package/src/styles.css +41 -4
- package/dist/index-CN3errpB.js +0 -7828
- package/dist/index-CN3errpB.js.map +0 -1
- package/dist/index-CcitzVoW.cjs +0 -5
- package/dist/index-CcitzVoW.cjs.map +0 -1
- package/src/components/LinkAttachment/components/_shared/accentSurface.test.ts +0 -135
- package/src/components/LinkAttachment/components/_shared/accentSurface.ts +0 -95
- package/src/components/LinkAttachment/components/_shared/chinContrast.test.ts +0 -95
- package/src/components/LinkAttachment/components/_shared/chinContrast.ts +0 -79
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The hex contract
|
|
2
|
+
* The hex contract for the accent colour `useChinPalette` publishes.
|
|
3
3
|
*
|
|
4
|
-
* `
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The extracted `accent_color` (from `getDominantColorFromImageUrl` via a Stream
|
|
5
|
+
* attachment field) is always a hex, but CSS `oklch(from …)` would resolve any
|
|
6
|
+
* colour name — including `rebeccapurple` — so the input is narrowed to a hex
|
|
7
|
+
* before it is published as `--accent`, rather than widening that contract by
|
|
8
|
+
* accident.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* `#abc`, `#aabbcc`, or either without the leading `#`.
|
|
13
13
|
*
|
|
14
|
-
* The
|
|
15
|
-
* that rejecting them would
|
|
16
|
-
* exists — `
|
|
17
|
-
* how a card once rendered unaccented while every calculation behind it had
|
|
18
|
-
* succeeded (MES-1349).
|
|
14
|
+
* The optional hash is deliberate: the extractor returns bare hexes often
|
|
15
|
+
* enough that rejecting them would drop the colour. It is also why `toCssHex`
|
|
16
|
+
* exists — `738883` without the `#` is not a valid CSS colour.
|
|
19
17
|
*/
|
|
20
18
|
export const HEX_PATTERN = /^#?(?:[0-9a-f]{3}|[0-9a-f]{6})$/i
|
|
21
19
|
|
|
22
|
-
/** Normalizes a hex for CSS
|
|
20
|
+
/** Normalizes a hex for CSS, adding the `#` the extractor omits. */
|
|
23
21
|
export function toCssHex(hex: string): string {
|
|
24
22
|
const trimmed = hex.trim()
|
|
25
23
|
return trimmed.startsWith('#') ? trimmed : `#${trimmed}`
|
|
@@ -5,10 +5,19 @@ import React from 'react'
|
|
|
5
5
|
|
|
6
6
|
import type { LinkAttachmentLayout, LinkAttachmentStatus } from '../../types'
|
|
7
7
|
|
|
8
|
-
import { accentSurface } from './accentSurface'
|
|
9
8
|
import type { LinkAttachmentVariant } from './CardShell'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { isPlayableAudio } from './CardThumbnail'
|
|
10
|
+
import { HEX_PATTERN, toCssHex } from './hex'
|
|
11
|
+
|
|
12
|
+
/** White on the dark stop, near-black on the light stop and the audio strip. */
|
|
13
|
+
const INK_ON_DARK = '#FFFFFF'
|
|
14
|
+
/** The near-black used across the Figma frames. */
|
|
15
|
+
const INK_ON_LIGHT = '#040403'
|
|
16
|
+
|
|
17
|
+
const INK_BY_VARIANT: Record<LinkAttachmentVariant, string> = {
|
|
18
|
+
dark: INK_ON_DARK,
|
|
19
|
+
light: INK_ON_LIGHT,
|
|
20
|
+
}
|
|
12
21
|
|
|
13
22
|
export interface ChinPaletteInput {
|
|
14
23
|
variant: LinkAttachmentVariant
|
|
@@ -31,21 +40,17 @@ export interface ChinPalette {
|
|
|
31
40
|
*/
|
|
32
41
|
thumbnailUrl?: string
|
|
33
42
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
accentStyle?: React.CSSProperties
|
|
38
|
-
/**
|
|
39
|
-
* Ink for both chin lines, chosen against the surface the card actually
|
|
40
|
-
* paints — the derived accent, the audio strip, or the fallback fill. Always
|
|
41
|
-
* defined for a parseable surface.
|
|
43
|
+
* Raw extracted colour to publish as the `--accent` custom property on the
|
|
44
|
+
* shell and the tail, or `undefined` when there is no paintable accent. The
|
|
45
|
+
* card keeps its hue and swaps the lightness in CSS (`styles.css`).
|
|
42
46
|
*/
|
|
43
|
-
|
|
47
|
+
accentHex?: string
|
|
44
48
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
49
|
+
* Ink for both chin lines: white on the sender's dark stop, near-black on the
|
|
50
|
+
* receiver's pale stop and on the audio strip. Fixed per surface — the stops
|
|
51
|
+
* clear AA at every hue.
|
|
47
52
|
*/
|
|
48
|
-
|
|
53
|
+
chinTextColor: string
|
|
49
54
|
/** True while the card renders as a skeleton. */
|
|
50
55
|
isLoading: boolean
|
|
51
56
|
/** Hand to `CardThumbnail` so a broken hero drops the accent with it. */
|
|
@@ -53,33 +58,25 @@ export interface ChinPalette {
|
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
/**
|
|
56
|
-
* Resolves a link card
|
|
61
|
+
* Resolves the accent a link card publishes and tracks hero-image failure.
|
|
57
62
|
*
|
|
58
63
|
* The accent is a claim about an image the viewer can see, so it only paints
|
|
59
|
-
* when `CardThumbnail` actually mounts that image. Mirroring its branches
|
|
60
|
-
*
|
|
64
|
+
* when `CardThumbnail` actually mounts that image. Mirroring its branches, the
|
|
65
|
+
* hero is *not* the accent's source when:
|
|
61
66
|
*
|
|
62
67
|
* - `layout: 'classic'` — there is no hero region at all;
|
|
63
|
-
* - `status` is not `'ready'` — the hero is the pulsing skeleton
|
|
64
|
-
* no image yet;
|
|
68
|
+
* - `status` is not `'ready'` — the hero is the pulsing skeleton, no image yet;
|
|
65
69
|
* - `thumbnailUrl` is absent — the hero is the type-icon placeholder, or for a
|
|
66
70
|
* playable video a bare `bg-black` box with no poster;
|
|
67
71
|
* - the attachment is playable audio — the hero collapses to the native
|
|
68
|
-
* `<audio>` player and the `<img>` never mounts
|
|
69
|
-
*
|
|
70
|
-
* artwork-backed audio link preview shipped a coloured card with no visible
|
|
71
|
-
* source image, and the accent beat `AUDIO_BG_CLASS` on Sent / Received
|
|
72
|
-
* because `CardShell` applies it as an inline `style`, which outranks any
|
|
73
|
-
* `bg-*` class. `ComposerCard` already dodged that by returning early before
|
|
74
|
-
* it passes `accentStyle`; this is that same rule, stated once instead of
|
|
75
|
-
* three times (MES-1349);
|
|
72
|
+
* `<audio>` player and the `<img>` never mounts even though `thumbnailUrl` is
|
|
73
|
+
* set, so `onImageError` can never rescue it and the gate has to be up front;
|
|
76
74
|
* - the image 404s — an accent extracted from something the viewer cannot see.
|
|
77
75
|
* `onImageError` covers both hero shapes: the `<img>` reports directly, and a
|
|
78
76
|
* playable video's `poster` (which raises no event of its own) is verified by
|
|
79
77
|
* the hidden probe image in `CardThumbnail`.
|
|
80
78
|
*
|
|
81
|
-
* Every case
|
|
82
|
-
* the variant's own text colours.
|
|
79
|
+
* Every excluded case publishes no accent and keeps the variant's own fill.
|
|
83
80
|
*/
|
|
84
81
|
export function useChinPalette({
|
|
85
82
|
variant,
|
|
@@ -105,32 +102,32 @@ export function useChinPalette({
|
|
|
105
102
|
// Truthiness rather than `!= null` so this tracks `CardThumbnail`'s own
|
|
106
103
|
// `thumbnailUrl ?` branch, which renders the placeholder for `''` too.
|
|
107
104
|
//
|
|
108
|
-
// Playable audio stays excluded
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// if the artwork 404'd. Colouring audio cards from their artwork is a change
|
|
113
|
-
// to the audio tickets (MES-1360 / MES-1361), not to this one.
|
|
105
|
+
// Playable audio stays excluded: its hero collapses to the native `<audio>`
|
|
106
|
+
// player, so the artwork never appears and an accent taken from it would claim
|
|
107
|
+
// an image the viewer cannot see. The `<img>` never mounts either, so
|
|
108
|
+
// `onImageError` could not withdraw the colour if the artwork 404'd.
|
|
114
109
|
const isAudio = isPlayableAudio(mimeType, sourceUrl)
|
|
115
110
|
const hasHero =
|
|
116
111
|
layout === 'featured' && !!thumbnailUrl && !isAudio && !heroFailed
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
const canDerive = hasHero && status === 'ready'
|
|
113
|
+
// Only a validated hex is published: the extracted `accent_color` is always a
|
|
114
|
+
// hex, and CSS would resolve any colour name, so narrow the input here. Bare
|
|
115
|
+
// hexes are accepted because the extractor omits the `#` often enough that
|
|
116
|
+
// rejecting them would silently lose the colour.
|
|
117
|
+
const accentHex =
|
|
118
|
+
canDerive && accentColor != null && HEX_PATTERN.test(accentColor.trim())
|
|
119
|
+
? toCssHex(accentColor)
|
|
120
120
|
: undefined
|
|
121
121
|
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
const painted =
|
|
127
|
-
surface ?? (isAudio ? AUDIO_BG : BUBBLE_BG_BY_VARIANT[variant])
|
|
122
|
+
// The accent surface and the sender's dark fallback both take white ink; the
|
|
123
|
+
// pale receiver fallback and the imposed audio strip both take near-black — so
|
|
124
|
+
// the ink is fixed per surface.
|
|
125
|
+
const chinTextColor = isAudio ? INK_ON_LIGHT : INK_BY_VARIANT[variant]
|
|
128
126
|
|
|
129
127
|
return {
|
|
130
128
|
thumbnailUrl: heroFailed ? undefined : thumbnailUrl,
|
|
131
|
-
|
|
132
|
-
chinTextColor
|
|
133
|
-
tailColor: painted,
|
|
129
|
+
accentHex,
|
|
130
|
+
chinTextColor,
|
|
134
131
|
isLoading: status === 'loading',
|
|
135
132
|
onImageError,
|
|
136
133
|
}
|
|
@@ -77,16 +77,17 @@ export interface LinkAttachmentBaseProps {
|
|
|
77
77
|
/**
|
|
78
78
|
* Dominant colour extracted from `thumbnailUrl`, as a hex string
|
|
79
79
|
* (`#RRGGBB` or `#RGB`). Paints the card and — when the card is the last
|
|
80
|
-
* bubble in a same-author run — its tail.
|
|
81
|
-
*
|
|
80
|
+
* bubble in a same-author run — its tail. Its hue is kept while the lightness
|
|
81
|
+
* is swapped to a fixed per-side stop (`oklch(from …)` in `styles.css`); the
|
|
82
|
+
* stops clear WCAG AA at every hue by construction, so callers never pass a
|
|
82
83
|
* text colour.
|
|
83
84
|
*
|
|
84
85
|
* Only painted while the hero image it was extracted from is actually on
|
|
85
86
|
* screen. Omitted, unparseable, `layout: 'classic'`, no `thumbnailUrl`,
|
|
86
87
|
* `status` other than `'ready'`, a playable-audio `mimeType` + `sourceUrl`
|
|
87
|
-
* (whose hero is the native player rather than an image), or a hero that
|
|
88
|
-
*
|
|
89
|
-
*
|
|
88
|
+
* (whose hero is the native player rather than an image), or a hero that fails
|
|
89
|
+
* to load => the card keeps the plain bubble fill. Browsers without
|
|
90
|
+
* relative-colour-syntax support also keep that fill (progressive enhancement).
|
|
90
91
|
*/
|
|
91
92
|
accentColor?: string
|
|
92
93
|
|
package/src/styles.css
CHANGED
|
@@ -1101,10 +1101,28 @@
|
|
|
1101
1101
|
}
|
|
1102
1102
|
}
|
|
1103
1103
|
|
|
1104
|
-
/*
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1104
|
+
/* Derived accent surface for `LinkAttachment` cards. `--accent` is the raw
|
|
1105
|
+
colour extracted from the attachment image; the card keeps its hue and swaps
|
|
1106
|
+
the lightness to this side's `--surface-l` stop. Only paints where the browser
|
|
1107
|
+
resolves relative colour syntax and an accent is set — otherwise the card's
|
|
1108
|
+
`bg-[…]` fallback fill shows through. The stops clear 4.5:1 against each side's
|
|
1109
|
+
ink at every hue. */
|
|
1110
|
+
.messaging-link-card[data-variant='dark'] {
|
|
1111
|
+
--surface-l: 0.3;
|
|
1112
|
+
}
|
|
1113
|
+
.messaging-link-card[data-variant='light'] {
|
|
1114
|
+
--surface-l: 0.9;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
@supports (background: oklch(from red l c h)) {
|
|
1118
|
+
.messaging-link-card[style*='--accent'] {
|
|
1119
|
+
background: oklch(from var(--accent) var(--surface-l) c h);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/* Bubble tail for `LinkAttachment` cards. Same 20x23 geometry and mask path as
|
|
1124
|
+
the text-bubble tails above; the fill is the card's accent surface, derived
|
|
1125
|
+
below from its own `--accent`.
|
|
1108
1126
|
|
|
1109
1127
|
Hand-written here rather than as a toolkit-only Tailwind utility on purpose:
|
|
1110
1128
|
`styles.css` ships as a built stylesheet that every consumer imports, whereas
|
|
@@ -1135,6 +1153,25 @@
|
|
|
1135
1153
|
transform-origin: center;
|
|
1136
1154
|
}
|
|
1137
1155
|
|
|
1156
|
+
/* The tail paints outside the shell's box, so it can't inherit the shell's
|
|
1157
|
+
computed background — it runs the same derivation itself off its own
|
|
1158
|
+
`--accent`, and falls back to this side's plain bubble fill. Keep these fills
|
|
1159
|
+
in sync with the shell's `bg-[…]` classes and the `--str-chat` bubble vars. */
|
|
1160
|
+
.messaging-link-card-tail[data-variant='dark'] {
|
|
1161
|
+
--surface-l: 0.3;
|
|
1162
|
+
background-color: #1e2330;
|
|
1163
|
+
}
|
|
1164
|
+
.messaging-link-card-tail[data-variant='light'] {
|
|
1165
|
+
--surface-l: 0.9;
|
|
1166
|
+
background-color: #f1f0ee;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
@supports (background: oklch(from red l c h)) {
|
|
1170
|
+
.messaging-link-card-tail[style*='--accent'] {
|
|
1171
|
+
background-color: oklch(from var(--accent) var(--surface-l) c h);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1138
1175
|
/* Locked attachment wrapper: stack card + text bubble in the correct direction */
|
|
1139
1176
|
.str-chat__li .str-chat__message--me .str-chat__message-bubble-wrapper {
|
|
1140
1177
|
display: flex;
|