@xapp/stentor-chat-widget 1.95.2 → 1.97.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.
@@ -0,0 +1,12 @@
1
+ type WidgetTheme = any;
2
+ export declare function parseRgb(colorString: string): {
3
+ r: number;
4
+ g: number;
5
+ b: number;
6
+ a?: number;
7
+ } | null;
8
+ export declare function invertColorRgb(rgb: string, bw?: boolean): string;
9
+ export declare function invertColorHEX(hex: string, bw?: boolean): string;
10
+ export declare function invertColor(color: string, bw?: boolean): string;
11
+ export declare function buildDefaultChatTheme(primaryColor: string, secondaryColor?: string): WidgetTheme;
12
+ export {};
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseRgb = parseRgb;
4
+ exports.invertColorRgb = invertColorRgb;
5
+ exports.invertColorHEX = invertColorHEX;
6
+ exports.invertColor = invertColor;
7
+ exports.buildDefaultChatTheme = buildDefaultChatTheme;
8
+ function padZero(str, len = 2) { const zeros = new Array(len).join("0"); return (zeros + str).slice(-len); }
9
+ function parseRgb(colorString) {
10
+ const rgbaRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)$/;
11
+ const rgbRegex = /^rgb?\((\d+),\s*(\d+),\s*(\d+)\)$/;
12
+ let match;
13
+ // eslint-disable-next-line no-cond-assign
14
+ if (match = colorString.match(rgbaRegex)) {
15
+ const r = parseInt(match[1]);
16
+ const g = parseInt(match[2]);
17
+ const b = parseInt(match[3]);
18
+ const a = parseFloat(match[4]);
19
+ return { r, g, b, a };
20
+ // eslint-disable-next-line no-cond-assign
21
+ }
22
+ else if (match = colorString.match(rgbRegex)) {
23
+ const r = parseInt(match[1]);
24
+ const g = parseInt(match[2]);
25
+ const b = parseInt(match[3]);
26
+ return { r, g, b };
27
+ }
28
+ else {
29
+ return null;
30
+ }
31
+ }
32
+ function invertColorRgb(rgb, bw) {
33
+ const rgba = parseRgb(rgb);
34
+ if (!rgba) {
35
+ return rgb;
36
+ }
37
+ const { r, g, b } = rgba;
38
+ if (bw) {
39
+ // http://stackoverflow.com/a/3943023/112731
40
+ return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? "rgba(0, 0, 0, 1)" : "rgba(255, 255, 255, 1)";
41
+ }
42
+ return `rgba(${255 - r}, ${255 - g}, ${255 - b}, 1)`;
43
+ }
44
+ function invertColorHEX(hex, bw) {
45
+ if (!hex) {
46
+ return undefined;
47
+ }
48
+ if (hex.indexOf("#") === 0) {
49
+ hex = hex.slice(1);
50
+ }
51
+ // convert 3-digit hex to 6-digits.
52
+ if (hex.length === 3) {
53
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
54
+ }
55
+ if (hex.length !== 6) {
56
+ throw new Error("Invalid HEX color.");
57
+ }
58
+ const r = parseInt(hex.slice(0, 2), 16);
59
+ const g = parseInt(hex.slice(2, 4), 16);
60
+ const b = parseInt(hex.slice(4, 6), 16);
61
+ if (bw) {
62
+ // http://stackoverflow.com/a/3943023/112731
63
+ return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? "#000000" : "#FFFFFF";
64
+ }
65
+ // pad each with zeros and return
66
+ return "#" + padZero((255 - r).toString(16)) + padZero((255 - g).toString(16)) + padZero((255 - b).toString(16));
67
+ }
68
+ function invertColor(color, bw) {
69
+ return color.startsWith("#") ? invertColorHEX(color, bw) : invertColorRgb(color, bw);
70
+ }
71
+ function buildDefaultChatTheme(primaryColor, secondaryColor) {
72
+ const fontFamily = "RobotoRegular";
73
+ if (!secondaryColor) {
74
+ secondaryColor = invertColor(primaryColor, true);
75
+ }
76
+ return {
77
+ zIndex: "10000",
78
+ border: {
79
+ color: primaryColor,
80
+ radius: "10px",
81
+ },
82
+ carousel: {
83
+ button: {
84
+ color: primaryColor,
85
+ },
86
+ subtitle: {
87
+ color: "#979797",
88
+ fontFamily,
89
+ fontSize: "12px",
90
+ fontStyle: "normal",
91
+ fontWeight: "normal",
92
+ },
93
+ title: {
94
+ color: "#000",
95
+ fontFamily,
96
+ fontSize: "14px",
97
+ fontStyle: "normal",
98
+ fontWeight: "bold",
99
+ },
100
+ },
101
+ chatButton: {
102
+ background: primaryColor,
103
+ margin: {
104
+ bottom: "10px",
105
+ left: "10px",
106
+ right: "10px",
107
+ top: "10px",
108
+ },
109
+ },
110
+ content: {
111
+ background: "#ffffff",
112
+ },
113
+ cta: {
114
+ background: "#e1e2e1",
115
+ text: {
116
+ // Use a dark color that contrasts well with the light grey background
117
+ // instead of primaryColor which may not have sufficient contrast
118
+ color: "#333333",
119
+ fontFamily,
120
+ fontSize: "14px",
121
+ },
122
+ },
123
+ footer: {
124
+ background: primaryColor,
125
+ border: {
126
+ color: primaryColor,
127
+ radius: "0px",
128
+ width: "0px",
129
+ },
130
+ branding: {
131
+ text: {
132
+ color: "#ffffff",
133
+ fontFamily,
134
+ fontSize: "14px",
135
+ fontWeight: "normal",
136
+ fontStyle: "normal",
137
+ },
138
+ },
139
+ },
140
+ header: {
141
+ background: primaryColor,
142
+ avatarBackground: "transparent",
143
+ border: {
144
+ color: primaryColor,
145
+ radius: "0px",
146
+ width: "0px",
147
+ },
148
+ text: {
149
+ color: secondaryColor,
150
+ fontFamily,
151
+ fontSize: "16px",
152
+ fontWeight: "normal",
153
+ fontStyle: "normal",
154
+ },
155
+ subtitle: {
156
+ color: secondaryColor,
157
+ fontFamily,
158
+ fontSize: "10px",
159
+ fontWeight: "normal",
160
+ fontStyle: "normal",
161
+ },
162
+ },
163
+ input: {
164
+ background: "#ffffff",
165
+ border: {
166
+ radius: "20px",
167
+ },
168
+ text: {
169
+ color: "#000000",
170
+ fontFamily,
171
+ fontSize: "14px",
172
+ },
173
+ placeholder: {
174
+ color: "#e1e2e1",
175
+ fontFamily,
176
+ fontSize: "14px",
177
+ fontWeight: "normal",
178
+ fontStyle: "normal",
179
+ },
180
+ },
181
+ textTypingStatus: {
182
+ text: {
183
+ color: "#bfbfbf",
184
+ fontFamily,
185
+ fontSize: "14px",
186
+ fontWeight: "normal",
187
+ fontStyle: "normal",
188
+ },
189
+ },
190
+ menu: {
191
+ item: {
192
+ height: "24px",
193
+ text: {
194
+ fontSize: "14px",
195
+ },
196
+ },
197
+ },
198
+ menuButton: {
199
+ color: "#ffffff",
200
+ },
201
+ messages: {
202
+ maxWidth: "400px",
203
+ mine: {
204
+ bubbleColor: "#f2f2f2",
205
+ text: {
206
+ color: "#454545",
207
+ fontFamily,
208
+ fontSize: "18px",
209
+ fontStyle: "normal",
210
+ fontWeight: "normal",
211
+ },
212
+ },
213
+ others: {
214
+ bubbleColor: "#e1e2e1",
215
+ text: {
216
+ color: "#262726",
217
+ fontFamily,
218
+ fontSize: "18px",
219
+ fontStyle: "normal",
220
+ fontWeight: "normal",
221
+ },
222
+ },
223
+ padding: {
224
+ bottom: "0px",
225
+ left: "20px",
226
+ right: "16px",
227
+ top: "16px",
228
+ },
229
+ },
230
+ refreshButton: {
231
+ color: secondaryColor,
232
+ },
233
+ minimizeButton: {
234
+ color: secondaryColor,
235
+ },
236
+ cancelButton: {
237
+ color: secondaryColor,
238
+ },
239
+ primaryColor: primaryColor,
240
+ sendButton: {
241
+ color: "#505050",
242
+ width: "20px",
243
+ },
244
+ size: {
245
+ width: "500px",
246
+ height: "700px",
247
+ },
248
+ };
249
+ }
250
+ //# sourceMappingURL=__clientTheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"__clientTheme.js","sourceRoot":"","sources":["../src/__clientTheme.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const buildChatWidgetChannel_1 = require("../buildChatWidgetChannel");
4
+ /**
5
+ * The chat widget a new app starts with.
6
+ *
7
+ * Built here rather than in stentor-client so Studio can create the same widget
8
+ * server-side for a get.xapp.ai signup. Two builders would mean onboarding and
9
+ * the funnel producing subtly different widgets from the same inputs.
10
+ */
11
+ const BASE = {
12
+ appId: "buehler-air",
13
+ apiUrl: "https://api.xapp.ai",
14
+ mcpUrl: "https://agent.xapp.ai"
15
+ };
16
+ describe("buildChatWidgetChannel", () => {
17
+ it("connects over MCP by default", () => {
18
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(BASE);
19
+ expect(channel.connection.type).toEqual("mcp");
20
+ expect(channel.connection.serverUrl).toEqual("https://agent.xapp.ai/buehler-air");
21
+ });
22
+ it("uses homeowner mode, which needs no auth token", () => {
23
+ var _a;
24
+ // The widget runs on a public page. business_owner mode requires a
25
+ // token, and a token on a public page is not a token.
26
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(BASE);
27
+ expect((_a = channel.connection.mcp) === null || _a === void 0 ? void 0 : _a.mode).toEqual("homeowner");
28
+ expect(JSON.stringify(channel)).not.toContain("authToken");
29
+ });
30
+ it("turns the action bar on", () => {
31
+ var _a;
32
+ // Chat is not on the default tier, but the action bar is how people
33
+ // reach booking and the phone, so it ships enabled.
34
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(BASE);
35
+ expect((_a = channel.actionBar) === null || _a === void 0 ? void 0 : _a.enabled).toEqual(true);
36
+ });
37
+ it("offers booking first, ahead of calling and chat", () => {
38
+ // Booking is what the business is paying for. Calling is the fallback
39
+ // for people who will not use a form, and chat is last because it is
40
+ // the least likely to be enabled on their tier.
41
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(Object.assign(Object.assign({}, BASE), { formWidgetKey: "fw-1", phoneNumber: "+19042338831" }));
42
+ expect(channel.actionBar.buttons.map((b) => b.type)).toEqual(["schedule", "phone", "chat"]);
43
+ expect(channel.actionBar.buttons[0].label).toBeDefined();
44
+ });
45
+ it("carries the form widget's key so booking opens our form", () => {
46
+ var _a, _b, _c, _d;
47
+ // The button opens the form widget, which is a separate channel with
48
+ // its own key. Without it the button has nothing to open.
49
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(Object.assign(Object.assign({}, BASE), { formWidgetKey: "fw-key-123" }));
50
+ const book = channel.actionBar.buttons.find((b) => b.type === "schedule");
51
+ expect((_b = (_a = book.formConfig) === null || _a === void 0 ? void 0 : _a.api) === null || _b === void 0 ? void 0 : _b.key).toEqual("fw-key-123");
52
+ expect((_d = (_c = book.formConfig) === null || _c === void 0 ? void 0 : _c.api) === null || _d === void 0 ? void 0 : _d.url).toEqual("https://api.xapp.ai");
53
+ });
54
+ it("omits the booking button until the form widget exists", () => {
55
+ // The form widget is created first and its key read back. A booking
56
+ // button with no form behind it is a button that does nothing.
57
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(BASE);
58
+ expect(channel.actionBar.buttons.some((b) => b.type === "schedule")).toEqual(false);
59
+ });
60
+ it("adds click-to-call when the crawl found a number", () => {
61
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(Object.assign(Object.assign({}, BASE), { phoneNumber: "+19042338831" }));
62
+ const phone = channel.actionBar.buttons.find((b) => b.type === "phone");
63
+ expect(phone.phoneNumber).toEqual("+19042338831");
64
+ });
65
+ it("leaves the phone button out when there is no number", () => {
66
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(BASE);
67
+ expect(channel.actionBar.buttons.some((b) => b.type === "phone")).toEqual(false);
68
+ });
69
+ it("always offers chat itself", () => {
70
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(BASE);
71
+ expect(channel.actionBar.buttons.some((b) => b.type === "chat")).toEqual(true);
72
+ });
73
+ it("themes from the business's own colour", () => {
74
+ var _a, _b;
75
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(Object.assign(Object.assign({}, BASE), { primaryColor: "#f47c4c" }));
76
+ expect((_b = (_a = channel.theme) === null || _a === void 0 ? void 0 : _a.chatButton) === null || _b === void 0 ? void 0 : _b.background).toEqual("#f47c4c");
77
+ });
78
+ it("picks header text that reads on that colour rather than assuming dark", () => {
79
+ var _a, _b, _c;
80
+ // #f47c4c scores 154 for perceived brightness against a threshold of
81
+ // 186, so it takes white text. Assuming dark here is how a widget ships
82
+ // unreadable. The chat button carries no text of its own — it is an
83
+ // icon on a background — so the header is where this shows.
84
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(Object.assign(Object.assign({}, BASE), { primaryColor: "#f47c4c" }));
85
+ expect((_c = (_b = (_a = channel.theme) === null || _a === void 0 ? void 0 : _a.header) === null || _b === void 0 ? void 0 : _b.text) === null || _c === void 0 ? void 0 : _c.color).toEqual("#FFFFFF");
86
+ });
87
+ it("ignores a colour whose contrast cannot be computed", () => {
88
+ var _a, _b, _c, _d, _e;
89
+ // A scraped `orange` is valid CSS and unusable here: we cannot work out
90
+ // what text reads on it. Theming the header with it and guessing the
91
+ // text colour is how you ship invisible text, so the neutral default is
92
+ // used for both instead — background and foreground always a computed
93
+ // pair.
94
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(Object.assign(Object.assign({}, BASE), { primaryColor: "orange" }));
95
+ expect((_b = (_a = channel.theme) === null || _a === void 0 ? void 0 : _a.header) === null || _b === void 0 ? void 0 : _b.background).not.toEqual("orange");
96
+ expect(["#000000", "#FFFFFF"]).toContain((_e = (_d = (_c = channel.theme) === null || _c === void 0 ? void 0 : _c.header) === null || _d === void 0 ? void 0 : _d.text) === null || _e === void 0 ? void 0 : _e.color);
97
+ });
98
+ it("keeps a colour it can compute against", () => {
99
+ var _a, _b, _c, _d, _e;
100
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(Object.assign(Object.assign({}, BASE), { primaryColor: "rgb(20, 52, 204)" }));
101
+ expect((_b = (_a = channel.theme) === null || _a === void 0 ? void 0 : _a.header) === null || _b === void 0 ? void 0 : _b.background).toEqual("rgb(20, 52, 204)");
102
+ expect((_e = (_d = (_c = channel.theme) === null || _c === void 0 ? void 0 : _c.header) === null || _d === void 0 ? void 0 : _d.text) === null || _e === void 0 ? void 0 : _e.color).toEqual("#FFFFFF");
103
+ });
104
+ it("still builds a widget when the crawl found no colour", () => {
105
+ const channel = (0, buildChatWidgetChannel_1.buildChatWidgetChannel)(BASE);
106
+ expect(channel.theme).toBeDefined();
107
+ expect(channel.type).toEqual("chat-widget");
108
+ });
109
+ it("does not invent an id", () => {
110
+ // The id is assigned when the channel is written. A builder that made
111
+ // one up would be creating a second source of truth for it.
112
+ expect((0, buildChatWidgetChannel_1.buildChatWidgetChannel)(BASE).id).toBeUndefined();
113
+ });
114
+ });
115
+ //# sourceMappingURL=buildChatWidgetChannel.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildChatWidgetChannel.test.js","sourceRoot":"","sources":["../../src/__tests__/buildChatWidgetChannel.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const color_1 = require("../color");
4
+ /**
5
+ * These decide whether a widget's text is legible on the colour we scraped off
6
+ * a customer's website, so the contrast rule matters more than the inversion.
7
+ */
8
+ describe("invertColorHEX", () => {
9
+ it("inverts a colour", () => {
10
+ expect((0, color_1.invertColorHEX)("#ffffff")).toEqual("#000000");
11
+ expect((0, color_1.invertColorHEX)("#000000")).toEqual("#ffffff");
12
+ });
13
+ it("expands three-digit hex", () => {
14
+ expect((0, color_1.invertColorHEX)("#fff")).toEqual((0, color_1.invertColorHEX)("#ffffff"));
15
+ });
16
+ it("accepts a colour with no leading hash", () => {
17
+ expect((0, color_1.invertColorHEX)("ffffff")).toEqual("#000000");
18
+ });
19
+ it("returns black or white when asked for contrast rather than inversion", () => {
20
+ // A mid-tone inverts to another mid-tone, which is unreadable behind
21
+ // text. bw picks whichever of black or white actually reads.
22
+ //
23
+ // Buehler's orange looks light and is not: weighted for perceived
24
+ // brightness it scores 154 against a threshold of 186, so it takes
25
+ // white text. Eyeballing this is how you ship an unreadable widget.
26
+ expect((0, color_1.invertColorHEX)("#f47c4c", true)).toEqual("#FFFFFF");
27
+ expect((0, color_1.invertColorHEX)("#0b83b3", true)).toEqual("#FFFFFF");
28
+ expect((0, color_1.invertColorHEX)("#c1bdb2", true)).toEqual("#000000");
29
+ });
30
+ it("has no answer for a missing colour", () => {
31
+ expect((0, color_1.invertColorHEX)(undefined)).toBeUndefined();
32
+ expect((0, color_1.invertColorHEX)("")).toBeUndefined();
33
+ });
34
+ it("refuses a value that is not a colour", () => {
35
+ expect(() => (0, color_1.invertColorHEX)("#12345")).toThrow(/Invalid HEX/);
36
+ });
37
+ });
38
+ describe("readableTextOn", () => {
39
+ it("picks the text colour that reads on the background", () => {
40
+ // From real crawls: Buehler's orange (154), Affordable Pest's grey
41
+ // (189) and XAPP's blue (60), against the 186 threshold.
42
+ expect((0, color_1.readableTextOn)("#f47c4c")).toEqual("#FFFFFF");
43
+ expect((0, color_1.readableTextOn)("#c1bdb2")).toEqual("#000000");
44
+ expect((0, color_1.readableTextOn)("#1434cc")).toEqual("#FFFFFF");
45
+ });
46
+ it("falls back to dark text when there is no background colour", () => {
47
+ // Better a readable default than a widget with white-on-white text
48
+ // because a crawl came back without a colour.
49
+ expect((0, color_1.readableTextOn)(undefined)).toEqual("#000000");
50
+ });
51
+ });
52
+ describe("invertColor", () => {
53
+ it("handles rgb as well as hex", () => {
54
+ // rgb input answers in rgb notation, as it always has. readableTextOn
55
+ // is the one that normalises, because a theme wants one shape.
56
+ expect((0, color_1.invertColor)("#ffffff", true)).toEqual("#000000");
57
+ expect((0, color_1.invertColor)("rgb(255, 255, 255)", true)).toEqual("rgba(0, 0, 0, 1)");
58
+ });
59
+ it("normalises rgb through readableTextOn", () => {
60
+ expect((0, color_1.readableTextOn)("rgb(255, 255, 255)")).toEqual("#000000");
61
+ expect((0, color_1.readableTextOn)("rgb(20, 52, 204)")).toEqual("#FFFFFF");
62
+ });
63
+ });
64
+ describe("readableTextOn with colours we cannot parse", () => {
65
+ it("never answers with the background it was given", () => {
66
+ // A CSS named colour is entirely plausible from a scrape — hand-written
67
+ // stylesheets are full of `orange`, `tomato`, `steelblue`. Returning it
68
+ // unchanged put the header text at exactly the header background.
69
+ expect((0, color_1.readableTextOn)("orange")).not.toEqual("orange");
70
+ expect((0, color_1.readableTextOn)("navy")).not.toEqual("navy");
71
+ });
72
+ it("only ever answers black or white", () => {
73
+ const answers = ["orange", "navy", "rgb( 255, 0, 0)", "RGB(255,0,0)", "hsl(9, 100%, 64%)", "#f47c4c", "not a colour"]
74
+ .map(color_1.readableTextOn);
75
+ answers.forEach((answer) => {
76
+ expect(["#000000", "#FFFFFF"]).toContain(answer);
77
+ });
78
+ });
79
+ });
80
+ describe("isSupportedColor", () => {
81
+ it("recognises the notations we can compute contrast for", () => {
82
+ expect((0, color_1.isSupportedColor)("#f47c4c")).toEqual(true);
83
+ expect((0, color_1.isSupportedColor)("#fff")).toEqual(true);
84
+ expect((0, color_1.isSupportedColor)("rgb(255, 0, 0)")).toEqual(true);
85
+ expect((0, color_1.isSupportedColor)("rgba(255, 0, 0, 0.5)")).toEqual(true);
86
+ });
87
+ it("rejects everything else, including named colours", () => {
88
+ // Not "these are invalid" — they are perfectly good CSS. We just
89
+ // cannot work out what text reads on them, and a background whose
90
+ // contrast we cannot compute is one we must not use.
91
+ expect((0, color_1.isSupportedColor)("orange")).toEqual(false);
92
+ expect((0, color_1.isSupportedColor)("hsl(9, 100%, 64%)")).toEqual(false);
93
+ expect((0, color_1.isSupportedColor)("#12345")).toEqual(false);
94
+ expect((0, color_1.isSupportedColor)(undefined)).toEqual(false);
95
+ expect((0, color_1.isSupportedColor)("")).toEqual(false);
96
+ });
97
+ });
98
+ describe("usableColorOr", () => {
99
+ it("keeps a colour we can compute contrast against", () => {
100
+ expect((0, color_1.usableColorOr)("#f47c4c", "#1AA7DC")).toEqual("#f47c4c");
101
+ expect((0, color_1.usableColorOr)("rgb(20, 52, 204)", "#1AA7DC")).toEqual("rgb(20, 52, 204)");
102
+ });
103
+ it("substitutes the fallback for anything else", () => {
104
+ // Including colours that are perfectly valid CSS. If we cannot work out
105
+ // what text reads on it, we must not use it as a background — the text
106
+ // colour would be a guess paired with an unknown.
107
+ expect((0, color_1.usableColorOr)("orange", "#1AA7DC")).toEqual("#1AA7DC");
108
+ expect((0, color_1.usableColorOr)("hsl(9, 100%, 64%)", "#1AA7DC")).toEqual("#1AA7DC");
109
+ expect((0, color_1.usableColorOr)(undefined, "#1AA7DC")).toEqual("#1AA7DC");
110
+ expect((0, color_1.usableColorOr)("", "#1AA7DC")).toEqual("#1AA7DC");
111
+ });
112
+ });
113
+ describe("darkenColor", () => {
114
+ it("darkens hex", () => {
115
+ expect((0, color_1.darkenColor)("#1434cc", 20)).toEqual("#0020b8");
116
+ });
117
+ it("darkens rgb without an alpha", () => {
118
+ expect((0, color_1.darkenColor)("rgb(20, 52, 204)", 20)).toEqual("#0020b8");
119
+ });
120
+ it("has no opinion about a missing colour", () => {
121
+ expect((0, color_1.darkenColor)(undefined, 20)).toBeUndefined();
122
+ });
123
+ it("returns unsupported input unchanged", () => {
124
+ expect((0, color_1.darkenColor)("orange", 20)).toEqual("orange");
125
+ });
126
+ /**
127
+ * Fixed here, and therefore the one place this package deliberately does
128
+ * not match stentor-client.
129
+ *
130
+ * The alpha branch computed luminance on the 0-255 scale the channels use
131
+ * and then clamped it to 0-1, so it collapsed to 1 for every real colour
132
+ * and the ratio crushed the result to near-black: rgba(20, 52, 204, 0.8)
133
+ * darkened by 20% came back rgba(0, 1, 3, 1). Clamping on the right scale
134
+ * makes it do what it says — scale each channel by 1 - amount/100.
135
+ *
136
+ * The client still has the old behaviour, so a form themed from an rgba
137
+ * colour will change appearance when it migrates onto this. That is the
138
+ * intended outcome rather than a regression: near-black was never what
139
+ * anyone asked for, and consolidating these builders is what makes one fix
140
+ * enough.
141
+ *
142
+ * Neither version is reachable from the funnel — Arachne reports
143
+ * site.primaryColor as hex.
144
+ */
145
+ it("darkens rgba proportionally rather than crushing it", () => {
146
+ // 80% of each channel, which is what "20% darker" should mean.
147
+ expect((0, color_1.darkenColor)("rgba(20, 52, 204, 0.8)", 20)).toEqual("rgba(16, 42, 163, 1)");
148
+ expect((0, color_1.darkenColor)("rgba(255, 0, 0, 0.5)", 20)).toEqual("rgba(204, 0, 0, 0.7)");
149
+ });
150
+ it("leaves the hex and rgb paths exactly as they were", () => {
151
+ // Those branches were correct and are untouched, so the parity tests
152
+ // over hex colours still hold.
153
+ expect((0, color_1.darkenColor)("#1434cc", 20)).toEqual("#0020b8");
154
+ expect((0, color_1.darkenColor)("rgb(20, 52, 204)", 20)).toEqual("#0020b8");
155
+ });
156
+ });
157
+ //# sourceMappingURL=color.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.test.js","sourceRoot":"","sources":["../../src/__tests__/color.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const __clientTheme_1 = require("../__clientTheme");
4
+ const buildChatWidgetChannel_1 = require("../buildChatWidgetChannel");
5
+ /**
6
+ * The port has to be faithful, or migrating stentor-client to it restyles every
7
+ * widget created afterwards. This compares the two directly rather than
8
+ * trusting that it looks right.
9
+ *
10
+ * `__clientTheme.ts` is stentor-client's function, extracted verbatim with its
11
+ * colour helpers inlined so it can run here. It goes away once the client is
12
+ * migrated and there is only one of these left.
13
+ */
14
+ describe("parity with stentor-client's buildDefaultChatTheme", () => {
15
+ // Hex is what a crawl actually yields — Arachne reports site.primaryColor
16
+ // as hex — so these are the cases that matter, and they are byte-identical.
17
+ const HEX = ["#f47c4c", "#c1bdb2", "#1434cc", "#1AA7DC", "#000000", "#ffffff"];
18
+ HEX.forEach((primaryColor) => {
19
+ it(`produces the same theme for ${primaryColor}`, () => {
20
+ expect((0, buildChatWidgetChannel_1.buildChatTheme)(primaryColor)).toEqual((0, __clientTheme_1.buildDefaultChatTheme)(primaryColor));
21
+ });
22
+ });
23
+ it("differs from the client only in notation for rgb input", () => {
24
+ // The one intentional difference. Given rgb, the client's contrast
25
+ // helper answers "rgba(255, 255, 255, 1)" while readableTextOn
26
+ // normalises to "#FFFFFF". Same colour, one shape instead of two — and
27
+ // not reachable from a crawl, which reports hex.
28
+ const mine = JSON.stringify((0, buildChatWidgetChannel_1.buildChatTheme)("rgb(20, 52, 204)"));
29
+ const theirs = JSON.stringify((0, __clientTheme_1.buildDefaultChatTheme)("rgb(20, 52, 204)"));
30
+ expect(mine.replace(/#FFFFFF/g, "rgba(255, 255, 255, 1)")).toEqual(theirs);
31
+ });
32
+ it("produces the same theme when a secondary colour is supplied", () => {
33
+ expect((0, buildChatWidgetChannel_1.buildChatTheme)("#f47c4c", "#ffffff")).toEqual((0, __clientTheme_1.buildDefaultChatTheme)("#f47c4c", "#ffffff"));
34
+ });
35
+ });
36
+ //# sourceMappingURL=themeParity.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themeParity.test.js","sourceRoot":"","sources":["../../src/__tests__/themeParity.test.ts"],"names":[],"mappings":";;AAAA,oDAAyD;AACzD,sEAA2D;AAE3D;;;;;;;;GAQG;AACH,QAAQ,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAEhE,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAM,GAAG,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAE/E,GAAG,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;QACzB,EAAE,CAAC,+BAA+B,YAAY,EAAE,EAAE,GAAG,EAAE;YACnD,MAAM,CAAC,IAAA,uCAAc,EAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,IAAA,qCAAqB,EAAC,YAAY,CAAC,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAC9D,mEAAmE;QACnE,+DAA+D;QAC/D,uEAAuE;QACvE,iDAAiD;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAA,uCAAc,EAAC,kBAAkB,CAAC,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAA,qCAAqB,EAAC,kBAAkB,CAAC,CAAC,CAAC;QAEzE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,IAAA,uCAAc,EAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAA,qCAAqB,EAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IACtG,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}