@tomtom-org/maps-sdk-plugin-map-theme 0.1.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.
package/LICENSE.txt ADDED
@@ -0,0 +1,511 @@
1
+ TOMTOM MAPS SDK FOR JAVASCRIPT LICENSE AGREEMENT (“AGREEMENT”)
2
+
3
+ Last update: 29 May 2026
4
+
5
+
6
+
7
+ By downloading, accessing the “TomTom Maps SDK for JavaScript”, you are entering into a binding agreement with TomTom Global Content B.V. (“TomTom”). The 'TomTom Maps SDK for JavaScript' includes all software, binaries, and source code made available by TomTom, whether through the TomTom developer portal or public code repositories (e.g., GitHub)." Your agreement with TomTom includes the following terms and conditions and the Privacy Policy (Data Processing Schedule | TomTom). You should not use the TomTom Maps SDK for JavaScript if you cannot agree to this Agreement and the Privacy Policy.
8
+
9
+ You may not use the TomTom Maps SDK for JavaScript and may not accept the Agreement if you are a person barred from receiving the TomTom Maps SDK for JavaScript under the laws of the European Union or other countries, including the country in which you are resident or from which you use the TomTom Maps SDK for JavaScript. If you agree to be bound by the Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to the Agreement. If you do not have the requisite authority, you may not accept the Agreement or use the TomTom Maps SDK for JavaScript on behalf of your employer or other entity.
10
+
11
+
12
+
13
+ 1. GRANT OF LICENSE: Subject to and conditional upon your compliance at all times with this Agreement, TomTom grants to you a limited, non-exclusive, non-transferable and non-sublicensable, revocable, royalty-free license during the Term (as defined in Clause 9) to use the TomTom Maps SDK for JavaScript in connection with the development of web based applications or mobile applications to be used in connection with the TomTom APIs and, in addition, third party online APIs (“Permitted Applications”). For the avoidance of doubt, you must use the TomTom APIs in connection with the TomTom Maps SDK for JavaScript. Any such Permitted Application and use of the TomTom APIs shall be subject to TomTom’s standard terms of use applicable to the TomTom APIs as made available on the TomTom developer portal or any other specific written agreement that you and TomTom have executed regarding the use of the TomTom APIs.
14
+
15
+
16
+
17
+ 2. UPDATES AND CHANGES TO THE TOMTOM MAPS SDK FOR JAVASCRIPT AND THIS AGREEMENT: TomTom reserves the right, with or without notice and at its sole discretion, to discontinue, update, modify, upgrade and supplement any of the TomTom Maps SDK for JavaScript or any features within the TomTom Maps SDK for JavaScript and to modify this Agreement. You agree that future versions of the TomTom Maps SDK for JavaScript may be incompatible with applications that were developed on previous versions of the TomTom Maps SDK for JavaScript.
18
+
19
+
20
+
21
+ 3. YOUR FURTHER OBLIGATIONS:
22
+
23
+
24
+
25
+ 3.1. You agree to use the TomTom Maps SDK for JavaScript only for purposes that are permitted by (a) this Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).
26
+
27
+
28
+
29
+ 3.2. Other than as permitted by applicable legislation, you are prohibited from copying (except for backup purposes), modifying, adapting, (re)distributing, renting, lending, publicly presenting, decompiling, reverse engineering, disassembling, or creating derivative works of the TomTom Maps SDK for JavaScript or any part of the TomTom Maps SDK for JavaScript. Notwithstanding the foregoing, if the TomTom Maps SDK is hosted on a public repository (e.g. GitHub, which allows forking under its terms of service), you may: (i) fork the repository solely for the purpose of submitting a Pull Request to TomTom, provided that such fork remains hosted within the platform and may not be used as a basis for a separate or competing product or service; and (ii) download a local copy of the source code solely for the purpose of debugging your Permitted Application, provided that such copy is not shared or redistributed. The availability of the source code in a public repository, and any rights granted by that platform's terms of service with respect to viewing or forking within the platform, do not constitute — and shall not be construed as — a grant of rights to modify, fork, or redistribute.
30
+
31
+
32
+
33
+ 3.3. You may not use the TomTom Maps SDK for JavaScript in connection with any illegal, fraudulent, dishonest or unethical activity or behaviour, specially to create, develop, or help others create or develop a product, service, or library that competes, directly or indirectly, with the TomTom Maps SDK or the TomTom APIs.
34
+
35
+
36
+
37
+ 3.4. You shall be permitted to display TomTom APIs results (“Results”) on third party content or map data provided that nothing shall entitle you to add or incorporate Results into the OSM or any other open source database. You shall also be permitted to display Results from third party content or map data on a TomTom map as a separate layer.
38
+
39
+
40
+
41
+ 3.5. The TomTom Maps SDK for JavaScript may include data which TomTom licenses from third parties or open-source software. You shall comply with all requirements and restrictions which such third parties may require TomTom to impose on you (and as may be updated and amended by TomTom from time to time). It shall be your responsibility to check, review and comply with any updated or amended terms that would apply to the TomTom Maps SDK for JavaScript licensed to you under this Agreement. For the avoidance of doubt, any such software is made available to you under the terms and conditions of the applicable licenses. In the event of a conflict between this Agreement and an Open-Source license governing a specific dependency, the Open Source license shall prevail solely with respect to that dependency.
42
+
43
+
44
+
45
+ 4. PRIVACY: In order to improve the TomTom Developer Portal and TomTom Maps SDK for JavaScript, TomTom may collect certain usage related information from the software including but not limited to the general SDK and product identifiers, version numbers, and information on which tools and/or services in the TomTom Maps SDK for JavaScript are being used and how they are being used by you. In addition, TomTom may collect your unique identifier and associated IP address for legitimate business reasons (such as without limitation the processing of invoices). This data will be retained for a maximum period of 120 days. The information collected is maintained in accordance with our Privacy Policy.
46
+
47
+
48
+
49
+ 5. INTELLECTUAL PROPERTY RIGHTS AND COPYRIGHT NOTICES:
50
+
51
+
52
+
53
+ 5.1. All intellectual property rights related to the TomTom Maps SDK for JavaScript and to the TomTom APIs are owned by TomTom and/or its licensors and suppliers. However, TomTom agrees that it obtains no intellectual property rights under this Agreement in or to any Permitted Application that you develop using the TomTom Maps SDK for JavaScript and the TomTom APIs.
54
+
55
+
56
+
57
+ 5.2. Your use of any trade names, logos, trademarks, service marks and other marks of TomTom is subject to TomTom’s prior written approval. Any goodwill arising out of the use thereof shall inure to the benefit of TomTom. In addition, you agree not to remove, obscure, mask or change any logo and/or copyright notice placed on or automatically generated by the TomTom Maps SDK for JavaScript or the TomTom APIs.
58
+
59
+
60
+
61
+
62
+
63
+ 6. WARRANTIES:
64
+
65
+
66
+
67
+ 6.1. YOU ACKNOWLEDGE THAT THE TOMTOM MAPS SDK FOR JAVASCRIPT HAS NOT BEEN DEVELOPED TO MEET YOUR INDIVIDUAL REQUIREMENTS AND THAT IT IS THEREFORE YOUR RESPONSIBILITY TO ENSURE THAT THE FACILITIES AND FUNCTIONS OF THE TOMTOM MAPS SDK FOR JAVASCRIPT MEET YOUR REQUIREMENTS. TOMTOM DOES NOT AND CANNOT WARRANT THAT THE TOMTOM MAPS SDK FOR JAVASCRIPT OPERATES UNINTERRUPTED OR ERROR-FREE. TOMTOM DOES NOT WARRANT THAT THE TOMTOM MAPS SDK FOR JAVASCRIPT IS CAPABLE OF INTEROPERATING WITH ANY OTHER SYSTEM, DEVICE OR PRODUCT (EITHER SOFTWARE OR HARDWARE).
68
+
69
+
70
+
71
+ 6.2. YOUR USE OF THE TOMTOM MAPS SDK FOR JAVASCRIPT AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE TOMTOM MAPS SDK FOR JAVASCRIPT IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICES OR ANY LOSS OF DATA THAT RESULTS FROM SUCH USE.
72
+
73
+
74
+
75
+ 6.3. WITHOUT LIMITING CLAUSES 6.1 AND 6.2 ABOVE, THE TOMTOM MAPS SDK FOR JAVASCRIPT IS PROVIDED ON AN “AS IS” AND WITH “ALL FAULTS BASIS” AND TOMTOM EXPRESSLY DISCLAIMS ALL OTHER WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, SATISFACTORY QUALITY, ACCURACY, TITLE AND FITNESS FOR A PARTICULAR PURPOSE.
76
+
77
+
78
+
79
+ 6.4. THE PROVISIONS OF THIS CLAUSE DO NOT AFFECT ANY OF YOUR LEGAL RIGHTS UNDER APPLICABLE MANDATORY NATIONAL LEGISLATION, INCLUDING BUT NOT LIMITED TO ANY MANDATORY STATUTORY WARRANTIES. IF ANY PART OF THIS LIMITED WARRANTY IS HELD TO BE INVALID OR UNENFORCEABLE, THE REMAINDER OF THE LIMITED WARRANTY SHALL NONETHELESS REMAIN IN FULL FORCE AND EFFECT.
80
+
81
+
82
+
83
+
84
+
85
+ 7. LIMITATION OF LIABILITY:
86
+
87
+
88
+
89
+ 7.1. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, NEITHER TOMTOM NOR ITS LICENSORS, SUPPLIERS, OFFICERS, DIRECTORS OR EMPLOYEES SHALL BE LIABLE TO YOU OR TO ANY THIRD PARTY FOR ANY DAMAGES, WHETHER DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL (INCLUDING BUT NOT LIMITED TO DAMAGES FOR THE INABILITY TO USE OR ACCESS THE TOMTOM MAPS SDK FOR JAVASCRIPT OR TOMTOM APIS, LOSS OF DATA, LOSS OF BUSINESS, LOSS OF PROFITS, BUSINESS INTERRUPTION OR THE LIKE) ARISING OUT OF OR RELATED TO THE USE OF, THE INABILITY TO USE OR ACCESS, OR OTHERWISE THE PERFORMANCE OF THE TOMTOM MAPS SDK FOR JAVASCRIPT, EVEN IF TOMTOM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
90
+
91
+
92
+
93
+ 7.2. TO THE EXTENT TOMTOM IS NOT PERMITTED UNDER APPLICABLE LAW TO EXCLUDE ITS LIABILITY PURSUANT TO CLAUSE 7.1 ABOVE, TOMTOM’S TOTAL AGGREGATE LIABILITY TO YOU SHALL BE LIMITED TO THE AMOUNT OF EUR100.00.
94
+
95
+
96
+
97
+ 7.3. THE ABOVE LIMITATIONS OF LIABILITY UNDER THIS CLAUSE 7 DO NOT: (I) AFFECT YOUR RIGHTS UNDER ANY APPLICABLE MANDATORY NATIONAL LEGISLATION; (II) EXCLUDE OR LIMIT ANY LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
98
+
99
+
100
+
101
+
102
+
103
+ 8. IPR INDMENITY: To the maximum extent permitted by law, you agree to defend, indemnify and hold TomTom and its respective directors, officers, employees, affiliates, agents and suppliers harmless from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorney’s fees) arising out of or accruing from (a) your use of the TomTom Maps SDK for JavaScript, (b) any application you develop on the TomTom Maps SDK for JavaScript that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this Agreement.
104
+
105
+
106
+
107
+ 9. TERM AND TERMINATION AND DIRECT ENFORCEMENT:
108
+
109
+
110
+
111
+ 9.1. This Agreement begins on either (a) the date you demonstrate your acceptance of the terms and conditions of this Agreement or (b) when you first use the TomTom Maps SDK for JavaScript and shall continue until terminated (jointly the “Term”) by either TomTom or you subject to and in accordance with this Agreement. The provisions of this Agreement, which by their nature are intended to survive termination, will remain in effect after termination of this Agreement. Furthermore, you agree that TomTom may directly enforce these provisions against you.
112
+
113
+
114
+
115
+ 9.2. Without prejudice to any other rights, TomTom may immediately terminate this Agreement if (i) you fail to comply with any of its terms and conditions, or (ii) the agreement governing the use of TomTom APIs between you and TomTom you has terminated, or (iii) TomTom decides to no longer provide the TomTom Maps SDK for JavaScript or certain parts of the TomTom Maps SDK for JavaScript either entirely or in certain territories related to your use.
116
+
117
+
118
+
119
+ 9.3. You may terminate this Agreement by ceasing your use of the TomTom Maps SDK for JavaScript at any time.
120
+
121
+
122
+
123
+
124
+
125
+ 10. GOVERNING LAW AND DISPUTE RESOLUTION:
126
+
127
+
128
+
129
+ 10.1. If you, your employer or the entity on whose behalf you act is incorporated in the European Union then this Agreement and any dispute arising under or in connection with it, including any non-contractual claims and disputes, shall be governed by and construed in accordance with the laws of the Netherlands. The applicability of the United Nations Convention on Contracts for the International Sale of Goods is explicitly excluded. You and TomTom hereby irrevocably submit to the exclusive jurisdiction of the courts of Amsterdam, the Netherlands in respect of any dispute arising under or in connection with this Agreement, including any non-contractual claims or disputes.
130
+
131
+
132
+
133
+ 10.2. If you, your employer or the entity on whose behalf you act is incorporated outside of European Union, then this Agreement and any dispute arising under or in connection with it, including any non-contractual claims and disputes, shall be governed by and construed in accordance with the laws of the Netherlands. All disputes arising in connection with the Agreement, including any non-contractual claims or disputes, shall be settled in accordance with the Arbitration Rules of the Netherlands Arbitration Institute. The arbitral tribunal shall be composed of three (3) arbitrators. The place or arbitration shall be Amsterdam, and the language of the arbitration shall be English. Consolidation of the arbitral proceedings with other arbitral proceedings, as provided for in Article 1046 of the Dutch Code of Civil Procedure and Article 39 of the Arbitration Rules of the Netherlands Arbitration Institute, is excluded.
134
+
135
+
136
+
137
+ 10.3. Notwithstanding this, TomTom shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
138
+
139
+
140
+
141
+
142
+
143
+ 11. OTHER LEGAL TERMS:
144
+
145
+
146
+
147
+ 11.1. No failure or delay by TomTom to exercise any right or remedy provided under this Agreement or by law shall constitute a waiver of that or any other right or remedy, nor shall it preclude or restrict the further exercise of that or any other right or remedy. No single or partial exercise of such right or remedy shall preclude or restrict the further exercise of that or any other right or remedy.
148
+
149
+
150
+
151
+ 11.2. Should for any reason, or to any extent, any provision of this Agreement be held invalid or unenforceable, such invalidity or enforceability shall not affect or render invalid or unenforceable the remaining provisions of this Agreement and the application of that provision shall be enforced to the extent permitted by law.
152
+
153
+
154
+
155
+ 11.3. The rights granted to you in this Agreement may not be assigned or transferred by you without TomTom’s prior written approval.
156
+
157
+
158
+
159
+ 11.4. The TomTom APIs and TomTom Maps SDK for JavaScript are provided as “Commercial Computer Software” or “restricted computer software”. Use, duplication, or disclosure by the U.S. Government or U.S. Government subcontractor is subject to the restrictions set forth in 48.C.F.R. Section 12.212 or 48 C.F.R.227.2702, as applicable or successor provisions. In respect of the Map API and Search API, the manufacturer is TomTom Global Content B.V.
160
+
161
+
162
+
163
+ 11.5. The availability of the TomTom Maps SDK source code in a public repository does not constitute 'Open-Source' licensing. This Agreement does not grant you any rights under any Open-Source Initiative (OSI) approved license. Your rights are strictly limited to those expressly granted in Clause 1."
164
+
165
+
166
+
167
+ 11.6 By submitting any suggestion, bug fix, or code contribution (“Contribution”) to TomTom (e.g., via a GitHub Pull Request), you hereby grant TomTom a perpetual, irrevocable, worldwide, royalty-free, and non-exclusive license to use, reproduce, modify, and distribute the Contribution as part of the TomTom Maps SDK. You represent that you have the legal right to grant this license and that the Contribution does not infringe any third-party intellectual property rights.
168
+
169
+
170
+
171
+
172
+
173
+ Name: zod
174
+ Version: 4.1.12
175
+ License: MIT
176
+ Private: false
177
+ Description: TypeScript-first schema declaration and validation library with static type inference
178
+ Repository: git+https://github.com/colinhacks/zod.git
179
+ Homepage: https://zod.dev
180
+ Author: Colin McDonnell <zod@colinhacks.com>
181
+ License Copyright:
182
+ ===
183
+
184
+ MIT License
185
+
186
+ Copyright (c) 2025 Colin McDonnell
187
+
188
+ Permission is hereby granted, free of charge, to any person obtaining a copy
189
+ of this software and associated documentation files (the "Software"), to deal
190
+ in the Software without restriction, including without limitation the rights
191
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
192
+ copies of the Software, and to permit persons to whom the Software is
193
+ furnished to do so, subject to the following conditions:
194
+
195
+ The above copyright notice and this permission notice shall be included in all
196
+ copies or substantial portions of the Software.
197
+
198
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
199
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
200
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
201
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
202
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
203
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
204
+ SOFTWARE.
205
+
206
+ ---
207
+
208
+ Name: lodash-es
209
+ Version: 4.17.21
210
+ License: MIT
211
+ Private: false
212
+ Description: Lodash exported as ES modules.
213
+ Repository: undefined
214
+ Homepage: https://lodash.com/custom-builds
215
+ Author: John-David Dalton <john.david.dalton@gmail.com>
216
+ Contributors:
217
+ John-David Dalton <john.david.dalton@gmail.com>
218
+ Mathias Bynens <mathias@qiwi.be>
219
+ License Copyright:
220
+ ===
221
+
222
+ Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
223
+
224
+ Based on Underscore.js, copyright Jeremy Ashkenas,
225
+ DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
226
+
227
+ This software consists of voluntary contributions made by many
228
+ individuals. For exact contribution history, see the revision history
229
+ available at https://github.com/lodash/lodash
230
+
231
+ The following license applies to all parts of this software except as
232
+ documented below:
233
+
234
+ ====
235
+
236
+ Permission is hereby granted, free of charge, to any person obtaining
237
+ a copy of this software and associated documentation files (the
238
+ "Software"), to deal in the Software without restriction, including
239
+ without limitation the rights to use, copy, modify, merge, publish,
240
+ distribute, sublicense, and/or sell copies of the Software, and to
241
+ permit persons to whom the Software is furnished to do so, subject to
242
+ the following conditions:
243
+
244
+ The above copyright notice and this permission notice shall be
245
+ included in all copies or substantial portions of the Software.
246
+
247
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
248
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
249
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
250
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
251
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
252
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
253
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
254
+
255
+ ====
256
+
257
+ Copyright and related rights for sample code are waived via CC0. Sample
258
+ code is defined as all source code displayed within the prose of the
259
+ documentation.
260
+
261
+ CC0: http://creativecommons.org/publicdomain/zero/1.0/
262
+
263
+ ====
264
+
265
+ Files located in the node_modules and vendor directories are externally
266
+ maintained libraries used by this software which have their own
267
+ licenses; we recommend you read them, as their terms may differ from the
268
+ terms above.
269
+
270
+
271
+
272
+
273
+
274
+
275
+
276
+ Name: @turf/helpers
277
+ Version: 7.3.0
278
+ License: MIT
279
+ Private: false
280
+ Description: Provides helper functions to create GeoJSON features, like points, lines, or areas on a map.
281
+ Repository: git://github.com/Turfjs/turf.git
282
+ Homepage: https://github.com/Turfjs/turf
283
+ Author: Turf Authors
284
+ Contributors:
285
+ Tom MacWright <@tmcw>
286
+ Stefano Borghi <@stebogit>
287
+ Denis Carriere <@DenisCarriere>
288
+ William Nordmann <@wnordmann>
289
+ License Copyright:
290
+ ===
291
+
292
+ The MIT License (MIT)
293
+
294
+ Copyright (c) 2017 TurfJS
295
+
296
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
297
+ this software and associated documentation files (the "Software"), to deal in
298
+ the Software without restriction, including without limitation the rights to
299
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
300
+ the Software, and to permit persons to whom the Software is furnished to do so,
301
+ subject to the following conditions:
302
+
303
+ The above copyright notice and this permission notice shall be included in all
304
+ copies or substantial portions of the Software.
305
+
306
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
307
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
308
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
309
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
310
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
311
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
312
+
313
+ ---
314
+
315
+ Name: @turf/invariant
316
+ Version: 7.3.0
317
+ License: MIT
318
+ Private: false
319
+ Description: Lightweight utility for input validation and data extraction in Turf.js. Ensures GeoJSON inputs are in the correct format and extracts specific components like coordinates or geometries.
320
+ Repository: git://github.com/Turfjs/turf.git
321
+ Homepage: https://github.com/Turfjs/turf
322
+ Author: Turf Authors
323
+ Contributors:
324
+ Tom MacWright <@tmcw>
325
+ Denis Carriere <@DenisCarriere>
326
+ License Copyright:
327
+ ===
328
+
329
+ The MIT License (MIT)
330
+
331
+ Copyright (c) 2017 TurfJS
332
+
333
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
334
+ this software and associated documentation files (the "Software"), to deal in
335
+ the Software without restriction, including without limitation the rights to
336
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
337
+ the Software, and to permit persons to whom the Software is furnished to do so,
338
+ subject to the following conditions:
339
+
340
+ The above copyright notice and this permission notice shall be included in all
341
+ copies or substantial portions of the Software.
342
+
343
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
344
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
345
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
346
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
347
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
348
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
349
+
350
+ ---
351
+
352
+ Name: @turf/bearing
353
+ Version: 7.3.0
354
+ License: MIT
355
+ Private: false
356
+ Description: Takes two points and finds the geographic bearing between them.
357
+ Repository: git://github.com/Turfjs/turf.git
358
+ Homepage: https://github.com/Turfjs/turf
359
+ Author: Turf Authors
360
+ License Copyright:
361
+ ===
362
+
363
+ The MIT License (MIT)
364
+
365
+ Copyright (c) 2017 TurfJS
366
+
367
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
368
+ this software and associated documentation files (the "Software"), to deal in
369
+ the Software without restriction, including without limitation the rights to
370
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
371
+ the Software, and to permit persons to whom the Software is furnished to do so,
372
+ subject to the following conditions:
373
+
374
+ The above copyright notice and this permission notice shall be included in all
375
+ copies or substantial portions of the Software.
376
+
377
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
378
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
379
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
380
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
381
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
382
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
383
+
384
+ ---
385
+
386
+ Name: maplibre-gl
387
+ Version: 5.12.0
388
+ License: BSD-3-Clause
389
+ Private: false
390
+ Description: BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
391
+ Repository: git://github.com/maplibre/maplibre-gl-js.git
392
+ Homepage: https://maplibre.org/
393
+ License Copyright:
394
+ ===
395
+
396
+ Copyright (c) 2023, MapLibre contributors
397
+
398
+ All rights reserved.
399
+
400
+ Redistribution and use in source and binary forms, with or without modification,
401
+ are permitted provided that the following conditions are met:
402
+
403
+ * Redistributions of source code must retain the above copyright notice,
404
+ this list of conditions and the following disclaimer.
405
+ * Redistributions in binary form must reproduce the above copyright notice,
406
+ this list of conditions and the following disclaimer in the documentation
407
+ and/or other materials provided with the distribution.
408
+ * Neither the name of MapLibre GL JS nor the names of its contributors
409
+ may be used to endorse or promote products derived from this software
410
+ without specific prior written permission.
411
+
412
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
413
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
414
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
415
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
416
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
417
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
418
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
419
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
420
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
421
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
422
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
423
+
424
+
425
+ -------------------------------------------------------------------------------
426
+
427
+ Contains code from mapbox-gl-js v1.13 and earlier
428
+
429
+ Version v1.13 of mapbox-gl-js and earlier are licensed under a BSD-3-Clause license
430
+
431
+ Copyright (c) 2020, Mapbox
432
+ Redistribution and use in source and binary forms, with or without modification,
433
+ are permitted provided that the following conditions are met:
434
+
435
+ * Redistributions of source code must retain the above copyright notice,
436
+ this list of conditions and the following disclaimer.
437
+ * Redistributions in binary form must reproduce the above copyright notice,
438
+ this list of conditions and the following disclaimer in the documentation
439
+ and/or other materials provided with the distribution.
440
+ * Neither the name of Mapbox GL JS nor the names of its contributors
441
+ may be used to endorse or promote products derived from this software
442
+ without specific prior written permission.
443
+
444
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
445
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
446
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
447
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
448
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
449
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
450
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
451
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
452
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
453
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
454
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
455
+
456
+
457
+ -------------------------------------------------------------------------------
458
+
459
+ Contains code from glfx.js
460
+
461
+ Copyright (C) 2011 by Evan Wallace
462
+
463
+ Permission is hereby granted, free of charge, to any person obtaining a copy
464
+ of this software and associated documentation files (the "Software"), to deal
465
+ in the Software without restriction, including without limitation the rights
466
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
467
+ copies of the Software, and to permit persons to whom the Software is
468
+ furnished to do so, subject to the following conditions:
469
+
470
+ The above copyright notice and this permission notice shall be included in
471
+ all copies or substantial portions of the Software.
472
+
473
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
474
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
475
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
476
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
477
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
478
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
479
+ THE SOFTWARE.
480
+
481
+ --------------------------------------------------------------------------------
482
+
483
+ Contains a portion of d3-color https://github.com/d3/d3-color
484
+
485
+ Copyright 2010-2016 Mike Bostock
486
+ All rights reserved.
487
+
488
+ Redistribution and use in source and binary forms, with or without modification,
489
+ are permitted provided that the following conditions are met:
490
+
491
+ * Redistributions of source code must retain the above copyright notice, this
492
+ list of conditions and the following disclaimer.
493
+
494
+ * Redistributions in binary form must reproduce the above copyright notice,
495
+ this list of conditions and the following disclaimer in the documentation
496
+ and/or other materials provided with the distribution.
497
+
498
+ * Neither the name of the author nor the names of contributors may be used to
499
+ endorse or promote products derived from this software without specific prior
500
+ written permission.
501
+
502
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
503
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
504
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
505
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
506
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
507
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
508
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
509
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
510
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
511
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Map Theme Plugin
2
+
3
+ Plugin for the [TomTom Maps SDK for JavaScript](https://docs.tomtom.com/maps-sdk-js/introduction/overview) that themes a `TomTomMap` from wherever a look comes from: a brand's colours, a website's, a described mood, an artist's palette, or an image. It assigns them to the SDK's ten map colours; `StylingModule.setMapColors` applies them.
4
+
5
+ | Function | From | To |
6
+ | --- | --- | --- |
7
+ | `deriveMapColors` | A handful of CSS colours | The ten map colours |
8
+ | `deriveMapColorsFromCss` | A stylesheet's text — a website's CSS, design tokens, an uploaded `.css` file | The ten map colours, through the colours it paints with most |
9
+ | `cssColors` | A stylesheet's text | Hex colours, most used first |
10
+ | `deriveMapColorsFromImage` | An image — `File`, `Blob`, `<img>`, `ImageBitmap`, CORS-readable URL or raw pixels | The ten map colours, through its dominant colours |
11
+ | `dominantColors` | Raw pixels | Hex colours, largest area first |
12
+ | `mapColorsLightDark` | The ten map colours | `'light'` or `'dark'`: switch to a style of the same (`standardDark` for a dark theme) before applying them |
13
+
14
+ ## Docs & examples
15
+
16
+ - Developer guide: https://docs.tomtom.com/maps-sdk-js/guides/plugins/map-theme
17
+ - Example: https://docs.tomtom.com/maps-sdk-js/examples/map-theme
18
+
19
+ ## Quickstart
20
+
21
+ The plugin declares `@tomtom-org/maps-sdk` and `culori` as peer dependencies; npm 7+ and Yarn install them for you.
22
+
23
+ ```bash
24
+ npm install @tomtom-org/maps-sdk maplibre-gl @tomtom-org/maps-sdk-plugin-map-theme
25
+ ```
26
+
27
+ ```ts
28
+ import { StylingModule } from '@tomtom-org/maps-sdk/map';
29
+ import { deriveMapColors, deriveMapColorsFromCss, deriveMapColorsFromImage } from '@tomtom-org/maps-sdk-plugin-map-theme';
30
+
31
+ const styling = await StylingModule.get(map);
32
+ styling.setMapColors(deriveMapColors(['#1d3f8a', '#e63312', '#f5d000', '#111111', '#f4f1ea']));
33
+ styling.setMapColors(deriveMapColorsFromCss(await cssFileInput.files[0].text()));
34
+ styling.setMapColors(await deriveMapColorsFromImage(fileInput.files[0], { mode: 'dark' }));
35
+ ```
@@ -0,0 +1,47 @@
1
+ THIRD-PARTY SOFTWARE NOTICES FOR @tomtom-org/maps-sdk-plugin-map-theme
2
+
3
+ This file lists the third-party components used by this package, together with
4
+ their licenses. It is generated from the build output — do not edit it by hand.
5
+
6
+ Components marked "Bundled: false (peer dependency)" are not shipped inside it:
7
+ your package manager installs them alongside it and your application bundles them.
8
+ The licenses below apply to those copies, and each such component may in turn install
9
+ its own dependencies under their own licenses.
10
+
11
+ The software itself is licensed under the terms in LICENSE.txt, which is not affected
12
+ by the licenses reproduced here.
13
+
14
+ --------------------------------------------------------------------------------
15
+
16
+ Name: culori
17
+ Version: 4.0.2
18
+ License: MIT
19
+ Bundled: false (peer dependency)
20
+ Description: A general-purpose color library for JavaScript
21
+ Repository: git@github.com:Evercoder/culori.git
22
+ Author: Dan Burzo <dan@danburzo.ro>
23
+ License Text:
24
+ ===
25
+
26
+ MIT License
27
+
28
+ Copyright (c) 2018 Dan Burzo
29
+
30
+ Permission is hereby granted, free of charge, to any person obtaining a copy
31
+ of this software and associated documentation files (the "Software"), to deal
32
+ in the Software without restriction, including without limitation the rights
33
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34
+ copies of the Software, and to permit persons to whom the Software is
35
+ furnished to do so, subject to the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be included in all
38
+ copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
46
+ SOFTWARE.
47
+
@@ -0,0 +1,36 @@
1
+ The following third-party components are declared as peer dependencies. Their code is
2
+ NOT bundled into this package: your package manager installs them alongside it and
3
+ your application bundles them. The licenses below apply to those copies, and each of
4
+ these components may in turn install its own dependencies under their own licenses.
5
+
6
+ Name: culori
7
+ Version: 4.0.2
8
+ License: MIT
9
+ Bundled: false (peer dependency)
10
+ Description: A general-purpose color library for JavaScript
11
+ Repository: git@github.com:Evercoder/culori.git
12
+ Author: Dan Burzo <dan@danburzo.ro>
13
+ License Text:
14
+ ===
15
+
16
+ MIT License
17
+
18
+ Copyright (c) 2018 Dan Burzo
19
+
20
+ Permission is hereby granted, free of charge, to any person obtaining a copy
21
+ of this software and associated documentation files (the "Software"), to deal
22
+ in the Software without restriction, including without limitation the rights
23
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24
+ copies of the Software, and to permit persons to whom the Software is
25
+ furnished to do so, subject to the following conditions:
26
+
27
+ The above copyright notice and this permission notice shall be included in all
28
+ copies or substantial portions of the Software.
29
+
30
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36
+ SOFTWARE.
@@ -0,0 +1,159 @@
1
+ import { LightDark, MapColors } from "@tomtom-org/maps-sdk/map";
2
+ //#region src/types.d.ts
3
+ /**
4
+ * Every {@link MapThemeMode}, for validating one at runtime.
5
+ *
6
+ * @group Map Theme
7
+ */
8
+ export declare const mapThemeModes: readonly ["light", "dark", "auto"];
9
+ /**
10
+ * Whether a derived theme reads as a light or a dark map. `auto` (the default) picks dark when the
11
+ * source colours are mostly dark.
12
+ *
13
+ * @group Map Theme
14
+ */
15
+ type MapThemeMode = (typeof mapThemeModes)[number];
16
+ /**
17
+ * Options for {@link deriveMapColors}.
18
+ *
19
+ * @group Map Theme
20
+ */
21
+ type MapThemeOptions = {
22
+ /** Default `auto`. */
23
+ mode?: MapThemeMode;
24
+ };
25
+ /**
26
+ * Options for {@link deriveMapColorsFromImage} and {@link deriveMapColorsFromCss}, which extract a few
27
+ * colours from their source before assigning them roles.
28
+ *
29
+ * @group Map Theme
30
+ */
31
+ type MapThemeExtractionOptions = MapThemeOptions & {
32
+ /** The most colours to extract. Default 6. */
33
+ colorCount?: number;
34
+ };
35
+ /** @ignore */
36
+ declare const DEFAULT_COLOR_COUNT = 6;
37
+ /**
38
+ * Raw pixels in the shape of a DOM `ImageData`: RGBA bytes, row-major. Accepted by
39
+ * {@link deriveMapColorsFromImage} so an image decoded elsewhere (a worker, a test) can be themed
40
+ * without a canvas.
41
+ *
42
+ * @group Map Theme
43
+ */
44
+ type ImagePixels = {
45
+ data: Uint8ClampedArray | Uint8Array | number[];
46
+ width: number;
47
+ height: number;
48
+ };
49
+ /**
50
+ * What {@link deriveMapColorsFromImage} accepts: an image URL (must be CORS-readable), a `Blob`/`File`,
51
+ * a loaded `HTMLImageElement`, an `ImageBitmap`, or raw {@link ImagePixels}.
52
+ *
53
+ * @group Map Theme
54
+ */
55
+ type MapThemeImageSource = string | Blob | HTMLImageElement | ImageBitmap | ImagePixels;
56
+ //#endregion
57
+ //#region src/cssColors.d.ts
58
+ /**
59
+ * The colours a stylesheet paints with, most used first, as hex strings: `var()` references
60
+ * resolved, fills weighted over borders and shadows, translucent colours and a colour's shades left
61
+ * out, and at most two greys while colourful ones remain. Feed the result to {@link deriveMapColors};
62
+ * {@link deriveMapColorsFromCss} does both.
63
+ *
64
+ * Rules for one colour scheme (`prefers-color-scheme`, a `.dark` or `.light` class, a `data-*="dark"`
65
+ * attribute) are read only with that `mode`, and then override the defaults; `auto` reads neither.
66
+ * A `var()` resolves against its own rule or the page root, not a custom property set on another selector.
67
+ *
68
+ * @param cssText - The stylesheet's text, e.g. `await file.text()`; `@import`s are not followed.
69
+ *
70
+ * @group Map Theme
71
+ */
72
+ export declare const cssColors: (cssText: string, options?: MapThemeExtractionOptions) => string[];
73
+ //#endregion
74
+ //#region src/cssTheme.d.ts
75
+ /**
76
+ * Derives the ten semantic map colours from a stylesheet — a site's CSS, a design system's tokens —
77
+ * by reading the colours it paints with ({@link cssColors}) and assigning them roles with
78
+ * {@link deriveMapColors}.
79
+ *
80
+ * With `mode: 'auto'`, the default, the map is light or dark as the page is: its background on
81
+ * `:root`, `html` or `body`, or else the grey most rules fill with, and light when neither resolves.
82
+ * Needs no DOM.
83
+ *
84
+ * @param cssText - The stylesheet's text, e.g. `await file.text()`; `@import`s are not followed.
85
+ * @throws `RangeError` when the stylesheet paints with no opaque colour.
86
+ *
87
+ * @group Map Theme
88
+ */
89
+ export declare const deriveMapColorsFromCss: (cssText: string, options?: MapThemeExtractionOptions) => Required<MapColors>;
90
+ //#endregion
91
+ //#region src/dominantColors.d.ts
92
+ /**
93
+ * The dominant colours of an image, largest area first, as hex strings — a small k-means over a
94
+ * sample of opaque pixels, with near-identical clusters merged. Feed the result to
95
+ * {@link deriveMapColors}; {@link deriveMapColorsFromImage} does both.
96
+ *
97
+ * @param count - The most colours to return (default 6); merged clusters can leave fewer.
98
+ *
99
+ * @group Map Theme
100
+ */
101
+ export declare const dominantColors: (pixels: ImagePixels, count?: number) => string[];
102
+ //#endregion
103
+ //#region src/imageTheme.d.ts
104
+ /**
105
+ * Derives the ten semantic map colours from an image — a photo, a logo, a poster — by extracting its
106
+ * dominant colours and assigning them roles with {@link deriveMapColors}.
107
+ *
108
+ * @throws `RangeError` when the image has no opaque pixels, or an error when it cannot be read (not
109
+ * CORS-readable, no DOM to decode it).
110
+ *
111
+ * @group Map Theme
112
+ */
113
+ export declare const deriveMapColorsFromImage: (source: MapThemeImageSource, options?: MapThemeExtractionOptions) => Promise<Required<MapColors>>;
114
+ //#endregion
115
+ //#region src/roleAssignment.d.ts
116
+ /**
117
+ * Assigns a handful of colours — a brand's, a website's, an artist's, a mood's — to the ten semantic
118
+ * map colours, ready for {@link StylingModule.setMapColors}.
119
+ *
120
+ * @remarks
121
+ * Deterministic, and no two roles share a colour:
122
+ * - `land`: the lightest low-saturation colour (the darkest, on a dark theme), toned down if saturated.
123
+ * - `roadMajor`: the most colourful by OKLCH chroma, preferring one that is not a blue or a green.
124
+ * - `water`: a blue or cyan; `vegetation`: a green.
125
+ * - `label`: the one contrasting most with `land`, when it reaches WCAG 4.5:1.
126
+ *
127
+ * Picked colours come back as given; roles no colour fits are `hsl()` shades of `land`, so the
128
+ * result is complete. Apply it over a style of the same light or dark: {@link mapColorsLightDark}.
129
+ *
130
+ * @param colors - One to roughly eight CSS colours; order does not matter, near-duplicates count once.
131
+ * @throws `RangeError` when no colour can be parsed.
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * const colors = deriveMapColors(['#0b2a3a', '#0fa8c8', '#f6efe3', '#d9822b'], { mode: 'light' });
136
+ * styling.setMapColors(colors); // `colors` also tells a UI what became what
137
+ * ```
138
+ *
139
+ * @group Map Theme
140
+ */
141
+ export declare const deriveMapColors: (colors: string[], options?: MapThemeOptions) => Required<MapColors>;
142
+ /**
143
+ * Whether map colours make a light or a dark map (dark when labels are lighter than land), to apply
144
+ * them over a style of the same: {@link StylingModule.setMapColors} keeps the loaded style's shade
145
+ * offsets, which a dark land over `standardLight` clamps to black. Tells what `auto` chose.
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const colors = await deriveMapColorsFromImage(file);
150
+ * const lightDark = mapColorsLightDark(colors);
151
+ * if (lightDark !== map.styleLightDarkTheme) await map.setStyle(lightDark === 'dark' ? 'standardDark' : 'standardLight');
152
+ * styling.setMapColors(colors);
153
+ * ```
154
+ *
155
+ * @group Map Theme
156
+ */
157
+ export declare const mapColorsLightDark: ({ land, label }: Pick<Required<MapColors>, "land" | "label">) => LightDark;
158
+ //#endregion
159
+ export type { DEFAULT_COLOR_COUNT, ImagePixels, MapThemeExtractionOptions, MapThemeImageSource, MapThemeMode, MapThemeOptions };
@@ -0,0 +1,2 @@
1
+ import{clampRgb as e,differenceEuclidean as t,differenceHueNaive as o,formatHex as r,modeHsl as s,modeLrgb as a,modeOklab as n,modeOklch as l,modeRgb as i,parse as c,useMode as h,wcagContrast as d,wcagLuminance as p}from"culori/fn";import{formatStyleHsl as u,toHsl as m}from"@tomtom-org/maps-sdk/map";h(i),h(a),h(s),h(n);var f=h(l),g=t("oklab"),w=(e,t)=>g(e,t)<.035,k=([e,t,o])=>({mode:"rgb",r:e/255,g:t/255,b:o/255}),b=["light","dark","auto"],v=(e,t)=>new RegExp(String.raw`prefers-color-scheme\s*:\s*${e}|\.(?:theme-)?${e}(?:-(?:mode|theme))?(?![\w-])|\[data-[\w-]+=["']?${e}["']?\]`,t),M=["light","dark"],x={light:v("light","i"),dark:v("dark","i")},S=v("(?:light|dark)","gi"),y=/^(?:(?::root|html|body)\s*)+$/,C=/^background(-color)?$/,I=/url\((?:"[^"]*"|'[^']*'|[^)]*)\)|"[^"]*"|'[^']*'/gi,E=new RegExp([/#[\da-f]{3,8}\b/,/\b(?:rgba?|hsla?|oklab|oklch)\([^()]*\)/,/\b[a-z]+\b/].map(({source:e})=>e).join("|"),"i"),$=new RegExp(E,"gi"),O=/var\(\s*(--[\w-]+)\s*(?:,([^()]*(?:\([^()]*\)[^()]*)*))?\)/g,R=[[/^(background|fill|accent-color|caret-color)|^color$/,1],[/^(border|outline|stroke|column-rule|text-decoration)/,.3],[/shadow$/,.1]],z=/brand|primary|accent/i,F=e=>e.startsWith("--"),q=e=>""===e||e.split(",").some(e=>y.test(e.trim())),A=(e,t)=>`${q(e)?"":e}\0${t}`,H=e=>M.find(t=>e.some(e=>x[t].test(e))),L=(e,t,o)=>{const r=e[t];if(o.escaped)o.escaped=!1;else if("\\"===r)o.escaped=!0;else if(o.quote)r===o.quote&&(o.quote=void 0);else if('"'===r||"'"===r)o.quote=r;else if("("===r)o.parentheses++;else if(")"===r)o.parentheses=Math.max(0,o.parentheses-1);else if(o.parentheses||"{"!==r){if(!o.parentheses&&(";"===r||"}"===r)){const s=e.slice(o.start,t);return o.start=t+1,{text:s,closesRule:"}"===r}}}else o.preludes.push(e.slice(o.start,t).trim()),o.start=t+1},j=(e,t)=>{const o=e.indexOf(":"),r=t.at(-1);if(o<=0||void 0===r)return;const s=e.slice(0,o).trim();return{property:F(s)?s:s.toLowerCase(),value:e.slice(o+1).trim(),selector:r.replace(S,"").replace(/\s+/g," ").trim(),scheme:H(t)}},B=(e,t,o,r=0)=>r>8?e:e.replace(O,(e,s,a="")=>{const n=o.get(A(t,s))??o.get(A("",s));return B(n??a,t,o,r+1)}),D=(t,o,s)=>{for(const[a]of o.replace(I," ").matchAll($)){const o=c(a);if(!o||(o.alpha??1)<1)continue;const n=e(o),l=r(n),i=t.get(l)??{hex:l,weight:0,color:f(n)};i.weight+=s,t.set(l,i)}},N=(e,t)=>F(e)?E.exec(t)?.[0]!==t.trim()?0:z.test(e)?1:.5:R.find(([t])=>t.test(e))?.[1]??0,P=(e,t)=>{const o=(e=>{const t=e.replace(/\/\*[\s\S]*?\*\//g,""),o={preludes:[],start:0,parentheses:0,escaped:!1},r=[];for(let s=0;s<t.length;s++){const e=L(t,s,o);if(!e)continue;const a=j(e.text,o.preludes);a&&r.push(a),e.closesRule&&o.preludes.pop()}return r})(e),r=({selector:e,property:t})=>A(e,t),s=new Set(o.filter(({scheme:e})=>e===t).map(r));return o.filter(e=>e.scheme?e.scheme===t:!s.has(r(e)))},T=e=>e.c<.03,V=(e,t)=>t.weight-e.weight,W=e=>{const t=[];for(const r of[...e].toSorted(V)){const e=t.find(e=>{return t=e.color,s=r.color,T(t)||T(s)?T(t)&&T(s)&&Math.abs(t.l-s.l)<.06:Math.abs(o({h:t.h??0},{h:s.h??0}))<20&&Math.abs(t.l-s.l)<.2;var t,s});e?e.weight+=r.weight:t.push({...r})}return t.toSorted(V)},Y=(e,t)=>{const o=(e=>{const[t,...o]=e,r=o.find(e=>Math.abs(e.color.l-t.color.l)>=.4);return r?[t,r,...o.filter(e=>e!==r)]:e})(e.filter(e=>T(e.color))),r=e.filter(e=>!T(e.color)),s=Math.min(t,Math.max(Math.min(o.length,2),t-r.length));return[...o.slice(0,s),...r.slice(0,t-s)].toSorted(V).map(e=>e.hex)},_=(e,t)=>{const o=e.filter(({property:e})=>C.test(e)),r=o.findLast(({selector:e})=>q(e)),s=/* @__PURE__ */new Map;r&&D(s,B(r.value,r.selector,t),1);const a=/* @__PURE__ */new Map;for(const{value:l,selector:i}of o)D(a,B(l,i,t),1);const[n]=s.size?s.values():[...a.values()].filter(e=>T(e.color)).toSorted(V);return n&&n.color.l<.5?"dark":"light"},G=(e,{mode:t="auto",colorCount:o=6}={})=>{const r=P(e,t),s=(e=>new Map(e.filter(({property:e})=>F(e)).map(({selector:e,property:t,value:o})=>[A(e,t),o])))(r),a=/* @__PURE__ */new Map;for(const{property:n,value:l,selector:i}of r){const e=B(l,i,s),t=N(n,e);t&&D(a,e,t)}return{colors:Y(W(a.values()),o),mode:"auto"===t?_(r,s):t}},J=(e,t={})=>G(e,t).colors,K=[170,260],Q=[150,280],U=[70,170],X=(e,[t,o])=>void 0!==e.h&&e.h>=t&&e.h<=o,Z=(e,t,o)=>u({h:e,s:t,l:o}),ee=e=>{const t=m(e);return t&&{...t,css:e,chroma:f(t).c}},te=class{pool;dark;land;constructor(e,t){this.pool=[...e];const o=e.reduce((e,t)=>e+t.l,0)/e.length;var r;this.dark="dark"===t||"auto"===t&&o<.45,this.land=(r=this.take(this.pickLand())??e[0]).s<=.14?r:ee(Z(r.h,.14,r.l))??r}pickLand(){const e=this.pool.filter(e=>e.s<=.4);return(e.length?e:this.pool).toSorted((e,t)=>this.dark?e.l-t.l:t.l-e.l)[0]}take(e){if(e)return this.pool.splice(this.pool.indexOf(e),1),e}takeAccent(){const e=this.pool.filter(e=>e.s>=.2).toSorted((e,t)=>t.chroma-e.chroma),t=e.find(e=>!X(e,Q)&&!X(e,U));return this.take(t??e[0])}takeHue(e){const t=this.pool.filter(t=>t.s>=.12&&X(t,e));return this.take(t.toSorted((e,t)=>t.s-e.s)[0])}takeMostContrasting(){const e=e=>d(e,this.land),t=this.pool.toSorted((t,o)=>e(o)-e(t))[0];return t&&e(t)>=4.5?this.take(t):void 0}shade(e,t={}){return Z(t.h??this.land.h,t.s??this.land.s,this.land.l+(this.dark?1:-1)*e)}labelShade(e,t){const o=this.shade(e,{s:t});return d(o,this.land)>=4.5?o:this.shade(-e,{s:t})}},oe=(e,t={})=>{const o=e.map(ee).filter(e=>void 0!==e),r=o.filter((e,t)=>!o.slice(0,t).some(t=>w(t,e)));if(!r.length)throw new RangeError("deriveMapColors needs at least one parseable CSS colour.");const s=new te(r,t.mode??"auto"),{dark:a,land:n}=s,l=s.takeAccent()?.css??s.shade(a?.55:.45,{s:.7}),i=(s.takeHue(K)??s.takeHue(Q))?.css??s.shade(a?.12:.14,{h:205,s:Math.max(.35,n.s)}),c=s.takeHue(U)?.css??s.shade(a?.08:.09,{h:110,s:Math.max(.18,.6*n.s)}),h=s.takeMostContrasting()?.css??s.labelShade(a?.8:.78,Math.min(n.s,.2)),d=ee(c)??n;return{land:n.css,water:i,vegetation:c,park:Z(d.h,d.s,d.l+(a?.04:.05)),artificial:s.shade(a?.04:.03),roadMajor:l,road:s.shade(a?.1:-.08,{s:.5*n.s}),roadOutline:s.shade(a?.24:.22,{s:.6*n.s}),label:h,labelOutline:s.shade(-.03)}},re=({land:e,label:t})=>p(t)>p(e)?"dark":"light",se=(e,t={})=>{const{colors:o,mode:r}=G(e,t);if(!o.length)throw new RangeError("deriveMapColorsFromCss: the stylesheet paints with no opaque colour to take a theme from.");return oe(o,{mode:r})},ae=(e,t)=>(e[0]-t[0])**2+(e[1]-t[1])**2+(e[2]-t[2])**2,ne=(e,t)=>{let o=0,r=Number.POSITIVE_INFINITY;for(let s=0;s<t.length;s++){const a=ae(e,t[s]);a<r&&(r=a,o=s)}return o},le=(e,t=6)=>{const o=(({data:e})=>{const t=Math.floor(e.length/4),o=Math.max(1,Math.floor(t/4096)),r=[];for(let s=0;s<t;s+=o){const t=4*s;e[t+3]<128||r.push([e[t],e[t+1],e[t+2]])}return r})(e);if(!o.length)return[];const{centroids:s,sizes:a}=((e,t)=>{let o=t,r=[];for(let s=0;s<8;s++){const t=o.map(()=>[0,0,0]);r=o.map(()=>0);for(const s of e){const e=ne(s,o);r[e]++,t[e][0]+=s[0],t[e][1]+=s[1],t[e][2]+=s[2]}o=o.map((e,o)=>r[o]?t[o].map(e=>e/r[o]):e)}return{centroids:o,sizes:r}})(o,((e,t)=>{const o=e=>p(k(e)),r=[[...e.toSorted((e,t)=>o(e)-o(t))[Math.floor(e.length/2)]]];for(;r.length<t;){let t=e[0],o=-1;for(const s of e){const e=ae(s,r[ne(s,r)]);e>o&&(o=e,t=s)}if(0===o)break;r.push([...t])}return r})(o,Math.min(t,o.length)));return((e,t)=>{const o=[];for(const[r,s]of e.entries()){if(!t[r])continue;const e=o.find(e=>w(k(e.rgb),k(s)));e?e.size+=t[r]:o.push({rgb:s,size:t[r]})}return o})(s,a).toSorted((e,t)=>t.size-e.size).map(e=>r(k(e.rgb)))},ie=64,ce=async e=>{return"string"==typeof e?(t=e,new Promise((e,o)=>{const r=new Image;r.crossOrigin="anonymous",r.onload=()=>e(r),r.onerror=()=>o(/* @__PURE__ */new Error(`deriveMapColorsFromImage: could not load '${t}'.`)),r.src=t})):e instanceof Blob?createImageBitmap(e):e;var t},he=async(e,t={})=>{const o=(e=>"object"==typeof e&&"data"in e)(e)?e:await(async e=>{if("undefined"==typeof document)throw new TypeError("deriveMapColorsFromImage needs a DOM to decode images; pass ImagePixels instead.");const t=await ce(e),o=document.createElement("canvas");o.width=ie,o.height=ie;const r=o.getContext("2d");if(!r)throw new Error("deriveMapColorsFromImage: no 2D canvas context available.");r.drawImage(t,0,0,ie,ie);try{return r.getImageData(0,0,ie,ie)}catch{throw new Error("deriveMapColorsFromImage: the image is not CORS-readable (canvas tainted). Serve it with Access-Control-Allow-Origin or pass a Blob/File.")}})(e),r=le(o,t.colorCount);if(!r.length)throw new RangeError("deriveMapColorsFromImage: the image has no opaque pixels to take colours from.");return oe(r,t)};export{J as cssColors,oe as deriveMapColors,se as deriveMapColorsFromCss,he as deriveMapColorsFromImage,le as dominantColors,re as mapColorsLightDark,b as mapThemeModes};
2
+ //# sourceMappingURL=index.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.es.js","names":["registerColorSpace","clampRgb","differenceEuclidean","differenceHueNaive","formatHex","modeHsl","modeLrgb","modeOklab","modeOklch","modeRgb","parse","useMode","wcagContrast","wcagLuminance","formatStyleHsl","toHsl","toOklch","oklabDistance","isSameColor","a","b","rgbFromBytes","red","green","blue","mode","r","g","mapThemeModes","schemeMarker","scheme","flags","RegExp","String","raw","COLOR_SCHEMES","SCHEME_MARKERS","light","dark","ANY_SCHEME_MARKER","PAGE_ROOT","PAGE_BACKGROUND","NON_PAINTING_TEXT","COLOR_TOKEN","map","source","join","COLOR_TOKENS","VARIABLE_REFERENCE","USAGE_WEIGHTS","BRAND_NAME","isCustomProperty","property","startsWith","isPageRoot","selector","split","some","part","test","trim","scopeKey","schemeOf","preludes","find","prelude","scanCharacter","css","index","state","character","escaped","quote","parentheses","Math","max","text","slice","start","closesRule","push","declarationOf","colon","indexOf","at","toLowerCase","value","replace","resolveVariables","variables","depth","_reference","name","fallback","definition","get","tally","swatches","weight","token","matchAll","parsed","alpha","rgb","hex","swatch","color","set","weightOf","resolvedValue","exec","pattern","declarationsFor","cssText","declarations","length","scanned","declaration","pop","declarationsOf","key","overridden","Set","filter","has","isNeutral","c","byWeight","mergeShades","tallied","merged","toSorted","main","existing","isShadeOf","abs","l","h","pickSwatches","count","neutrals","first","rest","opposite","neutralsByRole","colorful","neutralCount","min","pageMode","backgrounds","rootBackground","findLast","rootColors","Map","fills","page","size","values","readStylesheet","colorCount","variablesOf","colors","cssColors","options","WATER_HUES","WIDER_WATER_HUES","VEGETATION_HUES","isInHues","from","to","hsl","s","parseColor","chroma","RoleAssigner","pool","land","constructor","this","meanLightness","reduce","sum","take","pickLand","splice","takeAccent","accents","unclaimed","takeHue","hues","inRange","takeMostContrasting","againstLand","best","shade","lightnessOffset","labelShade","towards","deriveMapColors","distinct","seen","RangeError","roles","roadMajor","water","vegetation","label","park","artificial","road","roadOutline","labelOutline","mapColorsLightDark","deriveMapColorsFromCss","squaredDistance","nearestIndex","sample","centroids","nearest","nearestDistance","Number","POSITIVE_INFINITY","distance","dominantColors","pixels","samples","data","pixelCount","floor","stride","offset","sampleOpaquePixels","sizes","seeds","iteration","sums","centroid","cluster","kMeans","luminanceOf","farthest","farthestDistance","seedCentroids","entries","twin","entry","mergeClusters","DECODE_SIDE","toDrawable","async","url","Promise","resolve","reject","image","Image","crossOrigin","onload","onerror","Error","src","Blob","createImageBitmap","deriveMapColorsFromImage","isImagePixels","document","TypeError","drawable","canvas","createElement","width","height","context","getContext","drawImage","getImageData","readPixels"],"sources":["../src/colorSpaces.ts","../src/types.ts","../src/cssColors.ts","../src/roleAssignment.ts","../src/cssTheme.ts","../src/dominantColors.ts","../src/imageTheme.ts"],"sourcesContent":["import type { Color, Rgb } from 'culori/fn';\nimport {\n differenceEuclidean,\n modeHsl,\n modeLrgb,\n modeOklab,\n modeOklch,\n modeRgb,\n useMode as registerColorSpace,\n} from 'culori/fn';\n\n// The colour spaces this plugin's own culori calls need: sRGB for hex, linear sRGB for luminance and\n// contrast, HSL and OKLab for the colours a stylesheet writes in them, OKLCH for chroma. The SDK\n// registers some of them too, but an app can resolve a second culori copy for the plugin.\nregisterColorSpace(modeRgb);\nregisterColorSpace(modeLrgb);\nregisterColorSpace(modeHsl);\nregisterColorSpace(modeOklab);\n\n/**\n * A colour in OKLCH: perceptual lightness, chroma (how colourful it looks) and hue.\n * @ignore\n */\nexport const toOklch = registerColorSpace(modeOklch);\n\n// Below this OKLab distance two colours read as one.\nconst SAME_COLOR_DISTANCE = 0.035;\nconst oklabDistance = differenceEuclidean('oklab');\n\n/**\n * Whether two colours read as one: an image's near-identical clusters, a palette's near-duplicates.\n * @ignore\n */\nexport const isSameColor = (a: Color, b: Color): boolean => oklabDistance(a, b) < SAME_COLOR_DISTANCE;\n\n/**\n * Red, green and blue bytes (0–255), as image pixels carry them, as a culori colour.\n * @ignore\n */\nexport const rgbFromBytes = ([red, green, blue]: readonly number[]): Rgb => ({\n mode: 'rgb',\n r: red / 255,\n g: green / 255,\n b: blue / 255,\n});\n","import type { LightDark } from '@tomtom-org/maps-sdk/map';\n\n/**\n * Every {@link MapThemeMode}, for validating one at runtime.\n *\n * @group Map Theme\n */\nexport const mapThemeModes = ['light', 'dark', 'auto'] as const satisfies readonly (LightDark | 'auto')[];\n\n/**\n * Whether a derived theme reads as a light or a dark map. `auto` (the default) picks dark when the\n * source colours are mostly dark.\n *\n * @group Map Theme\n */\nexport type MapThemeMode = (typeof mapThemeModes)[number];\n\n/**\n * Options for {@link deriveMapColors}.\n *\n * @group Map Theme\n */\nexport type MapThemeOptions = {\n /** Default `auto`. */\n mode?: MapThemeMode;\n};\n\n/**\n * Options for {@link deriveMapColorsFromImage} and {@link deriveMapColorsFromCss}, which extract a few\n * colours from their source before assigning them roles.\n *\n * @group Map Theme\n */\nexport type MapThemeExtractionOptions = MapThemeOptions & {\n /** The most colours to extract. Default 6. */\n colorCount?: number;\n};\n\n/** @ignore */\nexport const DEFAULT_COLOR_COUNT = 6;\n\n/**\n * Raw pixels in the shape of a DOM `ImageData`: RGBA bytes, row-major. Accepted by\n * {@link deriveMapColorsFromImage} so an image decoded elsewhere (a worker, a test) can be themed\n * without a canvas.\n *\n * @group Map Theme\n */\nexport type ImagePixels = { data: Uint8ClampedArray | Uint8Array | number[]; width: number; height: number };\n\n/**\n * What {@link deriveMapColorsFromImage} accepts: an image URL (must be CORS-readable), a `Blob`/`File`,\n * a loaded `HTMLImageElement`, an `ImageBitmap`, or raw {@link ImagePixels}.\n *\n * @group Map Theme\n */\nexport type MapThemeImageSource = string | Blob | HTMLImageElement | ImageBitmap | ImagePixels;\n","import type { LightDark } from '@tomtom-org/maps-sdk/map';\nimport { clampRgb, differenceHueNaive, formatHex, type Oklch, parse } from 'culori/fn';\nimport { toOklch } from './colorSpaces';\nimport { DEFAULT_COLOR_COUNT, type MapThemeExtractionOptions, type MapThemeMode } from './types';\n\n// `scheme` is set on a rule for one colour scheme only. `selector` has the class or attribute that\n// marks the scheme removed, so `.dark` and `html[data-theme=dark]` select the page root as `:root` does.\ntype Declaration = { property: string; value: string; selector: string; scheme?: LightDark };\n\ntype Swatch = { hex: string; weight: number; color: Oklch };\n\n// What marks a rule as one colour scheme's: the media query, or the class or `data-*` attribute a\n// site toggles (`.dark`, `.dark-mode`, `.theme-dark`, `[data-theme=\"dark\"]`) — not a utility class\n// such as `.bg-dark`.\nconst schemeMarker = (scheme: string, flags: string): RegExp =>\n new RegExp(\n String.raw`prefers-color-scheme\\s*:\\s*${scheme}|\\.(?:theme-)?${scheme}(?:-(?:mode|theme))?(?![\\w-])|\\[data-[\\w-]+=[\"']?${scheme}[\"']?\\]`,\n flags,\n );\nconst COLOR_SCHEMES = ['light', 'dark'] as const satisfies readonly LightDark[];\nconst SCHEME_MARKERS: Record<LightDark, RegExp> = {\n light: schemeMarker('light', 'i'),\n dark: schemeMarker('dark', 'i'),\n};\nconst ANY_SCHEME_MARKER = schemeMarker('(?:light|dark)', 'gi');\nconst PAGE_ROOT = /^(?:(?::root|html|body)\\s*)+$/;\nconst PAGE_BACKGROUND = /^background(-color)?$/;\n// Text in a value that paints nothing, though words in it read as colour names: `url(/img/tan.png)`.\nconst NON_PAINTING_TEXT = /url\\((?:\"[^\"]*\"|'[^']*'|[^)]*)\\)|\"[^\"]*\"|'[^']*'/gi;\nconst HEX_COLOR = /#[\\da-f]{3,8}\\b/;\nconst COLOR_FUNCTION = /\\b(?:rgba?|hsla?|oklab|oklch)\\([^()]*\\)/;\nconst COLOR_NAME = /\\b[a-z]+\\b/;\nconst COLOR_TOKEN = new RegExp([HEX_COLOR, COLOR_FUNCTION, COLOR_NAME].map(({ source }) => source).join('|'), 'i');\nconst COLOR_TOKENS = new RegExp(COLOR_TOKEN, 'gi');\nconst VARIABLE_REFERENCE = /var\\(\\s*(--[\\w-]+)\\s*(?:,([^()]*(?:\\([^()]*\\)[^()]*)*))?\\)/g;\nconst MAX_VARIABLE_DEPTH = 8;\n\n// What a colour counts for where it is used: what a page fills with dominates its look, lines and\n// shadows barely register, and a property that paints nothing (a font, a grid area) counts for 0.\nconst USAGE_WEIGHTS: [RegExp, number][] = [\n [/^(background|fill|accent-color|caret-color)|^color$/, 1],\n [/^(border|outline|stroke|column-rule|text-decoration)/, 0.3],\n [/shadow$/, 0.1],\n];\n// A custom property holding one colour counts once where it is defined, so a file of design tokens\n// alone still ranks them; one named as the brand's counts double.\nconst DEFINITION_WEIGHT = 0.5;\nconst BRAND_NAME = /brand|primary|accent/i;\n\n// Below this OKLCH chroma a colour reads as a grey. A stylesheet has many; a theme needs two of them\n// at most (a background and a text colour) while colourful ones remain.\nconst NEUTRAL_CHROMA = 0.03;\nconst MAX_NEUTRALS = 2;\n// OKLCH lightness apart that a background and a text colour are at least.\nconst OPPOSITE_LIGHTNESS = 0.4;\n\nconst isCustomProperty = (property: string): boolean => property.startsWith('--');\n\n// Whether a rule styles the whole page, so that its custom properties reach every other rule.\nconst isPageRoot = (selector: string): boolean =>\n selector === '' || selector.split(',').some((part) => PAGE_ROOT.test(part.trim()));\n\n// Declarations with one key are one setting: the same property, on the same selector or anywhere on\n// the page root.\nconst scopeKey = (selector: string, property: string): string =>\n `${isPageRoot(selector) ? '' : selector}\\u0000${property}`;\n\nconst schemeOf = (preludes: string[]): LightDark | undefined =>\n COLOR_SCHEMES.find((scheme) => preludes.some((prelude) => SCHEME_MARKERS[scheme].test(prelude)));\n\ntype ScanState = { preludes: string[]; start: number; parentheses: number; quote?: string; escaped: boolean };\n\ntype ScannedDeclaration = { text: string; closesRule: boolean };\n\n// Advances the scan over one character; returns a declaration's text when the character ends one.\n// Escaped characters, quoted strings and parentheses are stepped over, so neither the `;` in\n// `url(\"data:…;base64,…\")` nor the quotes in `content: \"\\\\\"` or `.content-\\[\\'x\\'\\]` end anything.\nconst scanCharacter = (css: string, index: number, state: ScanState): ScannedDeclaration | undefined => {\n const character = css[index];\n if (state.escaped) {\n state.escaped = false;\n } else if (character === '\\\\') {\n state.escaped = true;\n } else if (state.quote) {\n if (character === state.quote) state.quote = undefined;\n } else if (character === '\"' || character === \"'\") {\n state.quote = character;\n } else if (character === '(') {\n state.parentheses++;\n } else if (character === ')') {\n state.parentheses = Math.max(0, state.parentheses - 1);\n } else if (!state.parentheses && character === '{') {\n state.preludes.push(css.slice(state.start, index).trim());\n state.start = index + 1;\n } else if (!state.parentheses && (character === ';' || character === '}')) {\n const text = css.slice(state.start, index);\n state.start = index + 1;\n return { text, closesRule: character === '}' };\n }\n return undefined;\n};\n\n// Undefined for text that declares nothing: an empty rule, a stray `;`, text outside any rule.\nconst declarationOf = (text: string, preludes: string[]): Declaration | undefined => {\n const colon = text.indexOf(':');\n const selector = preludes.at(-1);\n if (colon <= 0 || selector === undefined) return undefined;\n\n const property = text.slice(0, colon).trim();\n return {\n property: isCustomProperty(property) ? property : property.toLowerCase(),\n value: text.slice(colon + 1).trim(),\n selector: selector.replace(ANY_SCHEME_MARKER, '').replace(/\\s+/g, ' ').trim(),\n scheme: schemeOf(preludes),\n };\n};\n\n// A stylesheet's declarations, each with the selector of the rule holding it.\nconst declarationsOf = (cssText: string): Declaration[] => {\n const css = cssText.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n const state: ScanState = { preludes: [], start: 0, parentheses: 0, escaped: false };\n const declarations: Declaration[] = [];\n for (let index = 0; index < css.length; index++) {\n const scanned = scanCharacter(css, index, state);\n if (!scanned) continue;\n\n const declaration = declarationOf(scanned.text, state.preludes);\n if (declaration) declarations.push(declaration);\n if (scanned.closesRule) state.preludes.pop();\n }\n return declarations;\n};\n\n// Custom properties by `scopeKey`. A `var()` reads the definition on its own rule, else the page\n// root's: which elements a definition on another selector (`.inverse { --bg: … }`) reaches is more\n// than a flat read of the rules can tell, so it resolves nothing outside its own rule.\ntype Variables = Map<string, string>;\n\nconst variablesOf = (declarations: Declaration[]): Variables =>\n new Map(\n declarations\n .filter(({ property }) => isCustomProperty(property))\n .map(({ selector, property, value }) => [scopeKey(selector, property), value]),\n );\n\nconst resolveVariables = (value: string, selector: string, variables: Variables, depth = 0): string =>\n depth > MAX_VARIABLE_DEPTH\n ? value\n : value.replace(VARIABLE_REFERENCE, (_reference, name: string, fallback = '') => {\n const definition = variables.get(scopeKey(selector, name)) ?? variables.get(scopeKey('', name));\n return resolveVariables(definition ?? fallback, selector, variables, depth + 1);\n });\n\n// Adds `weight` to every opaque colour in a value, keyed by hex, in the order the value writes them.\n// Translucent colours are overlays and shadows, not the palette.\nconst tally = (swatches: Map<string, Swatch>, value: string, weight: number): void => {\n for (const [token] of value.replace(NON_PAINTING_TEXT, ' ').matchAll(COLOR_TOKENS)) {\n const parsed = parse(token);\n if (!parsed || (parsed.alpha ?? 1) < 1) continue;\n\n const rgb = clampRgb(parsed);\n const hex = formatHex(rgb);\n const swatch = swatches.get(hex) ?? { hex, weight: 0, color: toOklch(rgb) };\n swatch.weight += weight;\n swatches.set(hex, swatch);\n }\n};\n\nconst weightOf = (property: string, resolvedValue: string): number => {\n if (!isCustomProperty(property)) return USAGE_WEIGHTS.find(([pattern]) => pattern.test(property))?.[1] ?? 0;\n\n if (COLOR_TOKEN.exec(resolvedValue)?.[0] !== resolvedValue.trim()) return 0;\n\n return BRAND_NAME.test(property) ? 2 * DEFINITION_WEIGHT : DEFINITION_WEIGHT;\n};\n\n// The declarations in effect for a mode: the defaults, and for `light` or `dark` that scheme's rules\n// too, each replacing the defaults with its `scopeKey`. `auto` reads the page as it is by default.\nconst declarationsFor = (cssText: string, mode: MapThemeMode): Declaration[] => {\n const declarations = declarationsOf(cssText);\n const key = ({ selector, property }: Declaration) => scopeKey(selector, property);\n const overridden = new Set(declarations.filter(({ scheme }) => scheme === mode).map(key));\n return declarations.filter((declaration) =>\n declaration.scheme ? declaration.scheme === mode : !overridden.has(key(declaration)),\n );\n};\n\nconst isNeutral = (color: Oklch): boolean => color.c < NEUTRAL_CHROMA;\n\n// Shades of one colour (a button and its hover, two greys a step apart) are one swatch.\nconst isShadeOf = (a: Oklch, b: Oklch): boolean => {\n if (isNeutral(a) || isNeutral(b)) return isNeutral(a) && isNeutral(b) && Math.abs(a.l - b.l) < 0.06;\n\n return Math.abs(differenceHueNaive({ h: a.h ?? 0 }, { h: b.h ?? 0 })) < 20 && Math.abs(a.l - b.l) < 0.2;\n};\n\nconst byWeight = (a: Swatch, b: Swatch): number => b.weight - a.weight;\n\n// Swatches heaviest first, each named by its most-used shade.\nconst mergeShades = (tallied: Iterable<Swatch>): Swatch[] => {\n const merged: Swatch[] = [];\n for (const swatch of [...tallied].toSorted(byWeight)) {\n const main = merged.find((existing) => isShadeOf(existing.color, swatch.color));\n if (main) {\n main.weight += swatch.weight;\n } else {\n merged.push({ ...swatch });\n }\n }\n return merged.toSorted(byWeight);\n};\n\n// The most-used grey, then the most-used one across the lightness scale from it (a background and a\n// text colour), then the rest by use.\nconst neutralsByRole = (neutrals: Swatch[]): Swatch[] => {\n const [first, ...rest] = neutrals;\n const opposite = rest.find((swatch) => Math.abs(swatch.color.l - first.color.l) >= OPPOSITE_LIGHTNESS);\n return opposite ? [first, opposite, ...rest.filter((swatch) => swatch !== opposite)] : neutrals;\n};\n\nconst pickSwatches = (swatches: Swatch[], count: number): string[] => {\n const neutrals = neutralsByRole(swatches.filter((swatch) => isNeutral(swatch.color)));\n const colorful = swatches.filter((swatch) => !isNeutral(swatch.color));\n const neutralCount = Math.min(count, Math.max(Math.min(neutrals.length, MAX_NEUTRALS), count - colorful.length));\n return [...neutrals.slice(0, neutralCount), ...colorful.slice(0, count - neutralCount)]\n .toSorted(byWeight)\n .map((swatch) => swatch.hex);\n};\n\n// Light or dark as the page itself is: the last background set on the root or the body, or else the\n// grey the stylesheet fills the most rules with; light when neither resolves to a colour.\nconst pageMode = (declarations: Declaration[], variables: Variables): LightDark => {\n const backgrounds = declarations.filter(({ property }) => PAGE_BACKGROUND.test(property));\n const rootBackground = backgrounds.findLast(({ selector }) => isPageRoot(selector));\n const rootColors = new Map<string, Swatch>();\n if (rootBackground) {\n tally(rootColors, resolveVariables(rootBackground.value, rootBackground.selector, variables), 1);\n }\n\n const fills = new Map<string, Swatch>();\n for (const { value, selector } of backgrounds) tally(fills, resolveVariables(value, selector, variables), 1);\n\n const [page] = rootColors.size\n ? rootColors.values()\n : [...fills.values()].filter((swatch) => isNeutral(swatch.color)).toSorted(byWeight);\n return page && page.color.l < 0.5 ? 'dark' : 'light';\n};\n\n/**\n * The colours a stylesheet paints with, and the mode given or, for `auto`, the one its page sets.\n * @ignore\n */\nexport const readStylesheet = (\n cssText: string,\n { mode = 'auto', colorCount = DEFAULT_COLOR_COUNT }: MapThemeExtractionOptions = {},\n): { colors: string[]; mode: LightDark } => {\n const declarations = declarationsFor(cssText, mode);\n const variables = variablesOf(declarations);\n const tallied = new Map<string, Swatch>();\n for (const { property, value, selector } of declarations) {\n const resolvedValue = resolveVariables(value, selector, variables);\n const weight = weightOf(property, resolvedValue);\n if (weight) tally(tallied, resolvedValue, weight);\n }\n return {\n colors: pickSwatches(mergeShades(tallied.values()), colorCount),\n mode: mode === 'auto' ? pageMode(declarations, variables) : mode,\n };\n};\n\n/**\n * The colours a stylesheet paints with, most used first, as hex strings: `var()` references\n * resolved, fills weighted over borders and shadows, translucent colours and a colour's shades left\n * out, and at most two greys while colourful ones remain. Feed the result to {@link deriveMapColors};\n * {@link deriveMapColorsFromCss} does both.\n *\n * Rules for one colour scheme (`prefers-color-scheme`, a `.dark` or `.light` class, a `data-*=\"dark\"`\n * attribute) are read only with that `mode`, and then override the defaults; `auto` reads neither.\n * A `var()` resolves against its own rule or the page root, not a custom property set on another selector.\n *\n * @param cssText - The stylesheet's text, e.g. `await file.text()`; `@import`s are not followed.\n *\n * @group Map Theme\n */\nexport const cssColors = (cssText: string, options: MapThemeExtractionOptions = {}): string[] =>\n readStylesheet(cssText, options).colors;\n","import { formatStyleHsl, type HslColor, type LightDark, type MapColors, toHsl } from '@tomtom-org/maps-sdk/map';\nimport { wcagContrast, wcagLuminance } from 'culori/fn';\nimport { isSameColor, toOklch } from './colorSpaces';\nimport type { MapThemeMode, MapThemeOptions } from './types';\n\n// A source colour in HSL (saturation and lightness 0–1), with the CSS it was given as and its OKLCH\n// chroma.\ntype ParsedColor = HslColor & { css: string; chroma: number };\n\nconst NEUTRAL_SATURATION = 0.4;\n// Land-use tints in the built-up colour's hue family are re-derived as its shades, saturation\n// included, and `artificial` is a shade of land; a saturated land makes them garish, so it is toned\n// down to a true neutral.\nconst MAX_LAND_SATURATION = 0.14;\nconst MIN_LABEL_CONTRAST = 4.5;\nconst MIN_ACCENT_SATURATION = 0.2;\n\n// Hue ranges, in degrees, water and vegetation take their colour from: water tries the first and\n// widens to the second.\ntype HueRange = readonly [from: number, to: number];\nconst WATER_HUES: HueRange = [170, 260];\nconst WIDER_WATER_HUES: HueRange = [150, 280];\nconst VEGETATION_HUES: HueRange = [70, 170];\n\nconst isInHues = (color: ParsedColor, [from, to]: HueRange): boolean =>\n color.h !== undefined && color.h >= from && color.h <= to;\n\nconst hsl = (h: number | undefined, s: number, l: number): string => formatStyleHsl({ h, s, l });\n\nconst parseColor = (css: string): ParsedColor | undefined => {\n const parsed = toHsl(css);\n return parsed && { ...parsed, css, chroma: toOklch(parsed).c };\n};\n\nconst asNeutral = (color: ParsedColor): ParsedColor => {\n if (color.s <= MAX_LAND_SATURATION) return color;\n\n return parseColor(hsl(color.h, MAX_LAND_SATURATION, color.l)) ?? color;\n};\n\n// Assigns the ten semantic roles from an unordered list of colours. Each pick removes the colour\n// from the pool so no two roles share one; a role with no fitting colour is derived from `land`\n// so the result is always a coherent, complete palette.\nclass RoleAssigner {\n private readonly pool: ParsedColor[];\n readonly dark: boolean;\n readonly land: ParsedColor;\n\n constructor(colors: ParsedColor[], mode: MapThemeMode) {\n this.pool = [...colors];\n const meanLightness = colors.reduce((sum, color) => sum + color.l, 0) / colors.length;\n this.dark = mode === 'dark' || (mode === 'auto' && meanLightness < 0.45);\n this.land = asNeutral(this.take(this.pickLand()) ?? colors[0]);\n }\n\n private pickLand(): ParsedColor | undefined {\n const neutrals = this.pool.filter((color) => color.s <= NEUTRAL_SATURATION);\n const candidates = neutrals.length ? neutrals : this.pool;\n // Darkest first on a dark theme, lightest first otherwise; the sort is stable, so equally\n // light candidates keep the caller's order.\n return candidates.toSorted((a, b) => (this.dark ? a.l - b.l : b.l - a.l))[0];\n }\n\n private take(color: ParsedColor | undefined): ParsedColor | undefined {\n if (!color) return undefined;\n\n this.pool.splice(this.pool.indexOf(color), 1);\n return color;\n }\n\n // The most colourful colour left that is saturated enough to read as an accent, ranked by chroma\n // rather than HSL saturation, in which a dark navy is as saturated as a vivid red. A colour\n // outside the water and vegetation hues goes first, so roads do not spend the only blue or green.\n takeAccent(): ParsedColor | undefined {\n const accents = this.pool\n .filter((color) => color.s >= MIN_ACCENT_SATURATION)\n .toSorted((a, b) => b.chroma - a.chroma);\n const unclaimed = accents.find(\n (color) => !isInHues(color, WIDER_WATER_HUES) && !isInHues(color, VEGETATION_HUES),\n );\n return this.take(unclaimed ?? accents[0]);\n }\n\n takeHue(hues: HueRange): ParsedColor | undefined {\n const inRange = this.pool.filter((color) => color.s >= 0.12 && isInHues(color, hues));\n return this.take(inRange.toSorted((a, b) => b.s - a.s)[0]);\n }\n\n // The colour that contrasts most with land; undefined when none reaches the readable minimum.\n takeMostContrasting(): ParsedColor | undefined {\n const againstLand = (color: ParsedColor) => wcagContrast(color, this.land);\n const best = this.pool.toSorted((a, b) => againstLand(b) - againstLand(a))[0];\n return best && againstLand(best) >= MIN_LABEL_CONTRAST ? this.take(best) : undefined;\n }\n\n // A shade of land, `lightnessOffset` towards the label side (lighter on a dark theme, darker on a\n // light one; negative goes the other way), optionally re-hued or re-saturated.\n shade(lightnessOffset: number, options: { h?: number; s?: number } = {}): string {\n return hsl(\n options.h ?? this.land.h,\n options.s ?? this.land.s,\n this.land.l + (this.dark ? 1 : -1) * lightnessOffset,\n );\n }\n\n // A label shade towards the label side, or away from it when land sits so near that end that the\n // shade clamps to a colour too close to land to read.\n labelShade(lightnessOffset: number, s: number): string {\n const towards = this.shade(lightnessOffset, { s });\n return wcagContrast(towards, this.land) >= MIN_LABEL_CONTRAST ? towards : this.shade(-lightnessOffset, { s });\n }\n}\n\n/**\n * Assigns a handful of colours — a brand's, a website's, an artist's, a mood's — to the ten semantic\n * map colours, ready for {@link StylingModule.setMapColors}.\n *\n * @remarks\n * Deterministic, and no two roles share a colour:\n * - `land`: the lightest low-saturation colour (the darkest, on a dark theme), toned down if saturated.\n * - `roadMajor`: the most colourful by OKLCH chroma, preferring one that is not a blue or a green.\n * - `water`: a blue or cyan; `vegetation`: a green.\n * - `label`: the one contrasting most with `land`, when it reaches WCAG 4.5:1.\n *\n * Picked colours come back as given; roles no colour fits are `hsl()` shades of `land`, so the\n * result is complete. Apply it over a style of the same light or dark: {@link mapColorsLightDark}.\n *\n * @param colors - One to roughly eight CSS colours; order does not matter, near-duplicates count once.\n * @throws `RangeError` when no colour can be parsed.\n *\n * @example\n * ```typescript\n * const colors = deriveMapColors(['#0b2a3a', '#0fa8c8', '#f6efe3', '#d9822b'], { mode: 'light' });\n * styling.setMapColors(colors); // `colors` also tells a UI what became what\n * ```\n *\n * @group Map Theme\n */\nexport const deriveMapColors = (colors: string[], options: MapThemeOptions = {}): Required<MapColors> => {\n const parsed = colors.map(parseColor).filter((color): color is ParsedColor => color !== undefined);\n const distinct = parsed.filter((color, index) => !parsed.slice(0, index).some((seen) => isSameColor(seen, color)));\n if (!distinct.length) {\n throw new RangeError('deriveMapColors needs at least one parseable CSS colour.');\n }\n\n const roles = new RoleAssigner(distinct, options.mode ?? 'auto');\n const { dark, land } = roles;\n\n const roadMajor = roles.takeAccent()?.css ?? roles.shade(dark ? 0.55 : 0.45, { s: 0.7 });\n const water =\n (roles.takeHue(WATER_HUES) ?? roles.takeHue(WIDER_WATER_HUES))?.css ??\n roles.shade(dark ? 0.12 : 0.14, { h: 205, s: Math.max(0.35, land.s) });\n const vegetation =\n roles.takeHue(VEGETATION_HUES)?.css ??\n roles.shade(dark ? 0.08 : 0.09, { h: 110, s: Math.max(0.18, land.s * 0.6) });\n const label = roles.takeMostContrasting()?.css ?? roles.labelShade(dark ? 0.8 : 0.78, Math.min(land.s, 0.2));\n const park = parseColor(vegetation) ?? land;\n\n return {\n land: land.css,\n water,\n vegetation,\n park: hsl(park.h, park.s, park.l + (dark ? 0.04 : 0.05)),\n artificial: roles.shade(dark ? 0.04 : 0.03),\n roadMajor,\n road: roles.shade(dark ? 0.1 : -0.08, { s: land.s * 0.5 }),\n roadOutline: roles.shade(dark ? 0.24 : 0.22, { s: land.s * 0.6 }),\n label,\n labelOutline: roles.shade(-0.03),\n };\n};\n\n/**\n * Whether map colours make a light or a dark map (dark when labels are lighter than land), to apply\n * them over a style of the same: {@link StylingModule.setMapColors} keeps the loaded style's shade\n * offsets, which a dark land over `standardLight` clamps to black. Tells what `auto` chose.\n *\n * @example\n * ```typescript\n * const colors = await deriveMapColorsFromImage(file);\n * const lightDark = mapColorsLightDark(colors);\n * if (lightDark !== map.styleLightDarkTheme) await map.setStyle(lightDark === 'dark' ? 'standardDark' : 'standardLight');\n * styling.setMapColors(colors);\n * ```\n *\n * @group Map Theme\n */\nexport const mapColorsLightDark = ({ land, label }: Pick<Required<MapColors>, 'land' | 'label'>): LightDark =>\n wcagLuminance(label) > wcagLuminance(land) ? 'dark' : 'light';\n","import type { MapColors } from '@tomtom-org/maps-sdk/map';\nimport { readStylesheet } from './cssColors';\nimport { deriveMapColors } from './roleAssignment';\nimport type { MapThemeExtractionOptions } from './types';\n\n/**\n * Derives the ten semantic map colours from a stylesheet — a site's CSS, a design system's tokens —\n * by reading the colours it paints with ({@link cssColors}) and assigning them roles with\n * {@link deriveMapColors}.\n *\n * With `mode: 'auto'`, the default, the map is light or dark as the page is: its background on\n * `:root`, `html` or `body`, or else the grey most rules fill with, and light when neither resolves.\n * Needs no DOM.\n *\n * @param cssText - The stylesheet's text, e.g. `await file.text()`; `@import`s are not followed.\n * @throws `RangeError` when the stylesheet paints with no opaque colour.\n *\n * @group Map Theme\n */\nexport const deriveMapColorsFromCss = (\n cssText: string,\n options: MapThemeExtractionOptions = {},\n): Required<MapColors> => {\n const { colors, mode } = readStylesheet(cssText, options);\n if (!colors.length) {\n throw new RangeError(\n 'deriveMapColorsFromCss: the stylesheet paints with no opaque colour to take a theme from.',\n );\n }\n\n return deriveMapColors(colors, { mode });\n};\n","import { formatHex, wcagLuminance } from 'culori/fn';\nimport { isSameColor, rgbFromBytes } from './colorSpaces';\nimport { DEFAULT_COLOR_COUNT, type ImagePixels } from './types';\n\nconst SAMPLE_BUDGET = 4096;\nconst KMEANS_ITERATIONS = 8;\n\n// In RGB bytes: cheap enough for the k-means loop, which only needs a nearest centroid.\nconst squaredDistance = (a: number[], b: number[]): number =>\n (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2;\n\n// Up to SAMPLE_BUDGET opaque pixels, evenly strided through the image.\nconst sampleOpaquePixels = ({ data }: ImagePixels): number[][] => {\n const pixelCount = Math.floor(data.length / 4);\n const stride = Math.max(1, Math.floor(pixelCount / SAMPLE_BUDGET));\n const samples: number[][] = [];\n for (let index = 0; index < pixelCount; index += stride) {\n const offset = index * 4;\n if (data[offset + 3] < 128) continue;\n\n samples.push([data[offset], data[offset + 1], data[offset + 2]]);\n }\n return samples;\n};\n\nconst nearestIndex = (sample: number[], centroids: number[][]): number => {\n let nearest = 0;\n let nearestDistance = Number.POSITIVE_INFINITY;\n for (let c = 0; c < centroids.length; c++) {\n const distance = squaredDistance(sample, centroids[c]);\n if (distance < nearestDistance) {\n nearestDistance = distance;\n nearest = c;\n }\n }\n return nearest;\n};\n\n// Farthest-point seeding: start from the median-luminance pixel, then keep adding the pixel farthest\n// from every seed so far, so distinct colours each get a cluster even when they are all dark.\nconst seedCentroids = (samples: number[][], count: number): number[][] => {\n const luminanceOf = (rgb: number[]) => wcagLuminance(rgbFromBytes(rgb));\n const byLuminance = samples.toSorted((a, b) => luminanceOf(a) - luminanceOf(b));\n const centroids = [[...byLuminance[Math.floor(samples.length / 2)]]];\n while (centroids.length < count) {\n let farthest = samples[0];\n let farthestDistance = -1;\n for (const sample of samples) {\n const distance = squaredDistance(sample, centroids[nearestIndex(sample, centroids)]);\n if (distance > farthestDistance) {\n farthestDistance = distance;\n farthest = sample;\n }\n }\n if (farthestDistance === 0) break;\n\n centroids.push([...farthest]);\n }\n return centroids;\n};\n\nconst kMeans = (samples: number[][], seeds: number[][]): { centroids: number[][]; sizes: number[] } => {\n let centroids = seeds;\n let sizes: number[] = [];\n for (let iteration = 0; iteration < KMEANS_ITERATIONS; iteration++) {\n const sums = centroids.map(() => [0, 0, 0]);\n sizes = centroids.map(() => 0);\n for (const sample of samples) {\n const nearest = nearestIndex(sample, centroids);\n sizes[nearest]++;\n sums[nearest][0] += sample[0];\n sums[nearest][1] += sample[1];\n sums[nearest][2] += sample[2];\n }\n centroids = centroids.map((centroid, cluster) =>\n sizes[cluster] ? sums[cluster].map((sum) => sum / sizes[cluster]) : centroid,\n );\n }\n return { centroids, sizes };\n};\n\ntype Cluster = { rgb: number[]; size: number };\n\nconst mergeClusters = (centroids: number[][], sizes: number[]): Cluster[] => {\n const merged: Cluster[] = [];\n for (const [cluster, rgb] of centroids.entries()) {\n if (!sizes[cluster]) continue;\n\n const twin = merged.find((entry) => isSameColor(rgbFromBytes(entry.rgb), rgbFromBytes(rgb)));\n if (twin) {\n twin.size += sizes[cluster];\n } else {\n merged.push({ rgb, size: sizes[cluster] });\n }\n }\n return merged;\n};\n\n/**\n * The dominant colours of an image, largest area first, as hex strings — a small k-means over a\n * sample of opaque pixels, with near-identical clusters merged. Feed the result to\n * {@link deriveMapColors}; {@link deriveMapColorsFromImage} does both.\n *\n * @param count - The most colours to return (default 6); merged clusters can leave fewer.\n *\n * @group Map Theme\n */\nexport const dominantColors = (pixels: ImagePixels, count = DEFAULT_COLOR_COUNT): string[] => {\n const samples = sampleOpaquePixels(pixels);\n if (!samples.length) return [];\n\n const { centroids, sizes } = kMeans(samples, seedCentroids(samples, Math.min(count, samples.length)));\n return mergeClusters(centroids, sizes)\n .toSorted((a, b) => b.size - a.size)\n .map((entry) => formatHex(rgbFromBytes(entry.rgb)));\n};\n","import type { MapColors } from '@tomtom-org/maps-sdk/map';\nimport { dominantColors } from './dominantColors';\nimport { deriveMapColors } from './roleAssignment';\nimport type { ImagePixels, MapThemeExtractionOptions, MapThemeImageSource } from './types';\n\n// The dominant colours do not need every pixel, so the image is drawn into a square this small.\nconst DECODE_SIDE = 64;\n\nconst isImagePixels = (source: MapThemeImageSource): source is ImagePixels =>\n typeof source === 'object' && 'data' in source;\n\nconst loadImageElement = (url: string): Promise<HTMLImageElement> =>\n new Promise((resolve, reject) => {\n const image = new Image();\n image.crossOrigin = 'anonymous';\n image.onload = () => resolve(image);\n image.onerror = () => reject(new Error(`deriveMapColorsFromImage: could not load '${url}'.`));\n image.src = url;\n });\n\nconst toDrawable = async (source: Exclude<MapThemeImageSource, ImagePixels>): Promise<CanvasImageSource> => {\n if (typeof source === 'string') return loadImageElement(source);\n\n if (source instanceof Blob) return createImageBitmap(source);\n\n return source;\n};\n\nconst readPixels = async (source: Exclude<MapThemeImageSource, ImagePixels>): Promise<ImagePixels> => {\n if (typeof document === 'undefined') {\n throw new TypeError('deriveMapColorsFromImage needs a DOM to decode images; pass ImagePixels instead.');\n }\n\n const drawable = await toDrawable(source);\n const canvas = document.createElement('canvas');\n canvas.width = DECODE_SIDE;\n canvas.height = DECODE_SIDE;\n const context = canvas.getContext('2d');\n if (!context) {\n throw new Error('deriveMapColorsFromImage: no 2D canvas context available.');\n }\n\n context.drawImage(drawable, 0, 0, DECODE_SIDE, DECODE_SIDE);\n try {\n return context.getImageData(0, 0, DECODE_SIDE, DECODE_SIDE);\n } catch {\n throw new Error(\n 'deriveMapColorsFromImage: the image is not CORS-readable (canvas tainted). Serve it with ' +\n 'Access-Control-Allow-Origin or pass a Blob/File.',\n );\n }\n};\n\n/**\n * Derives the ten semantic map colours from an image — a photo, a logo, a poster — by extracting its\n * dominant colours and assigning them roles with {@link deriveMapColors}.\n *\n * @throws `RangeError` when the image has no opaque pixels, or an error when it cannot be read (not\n * CORS-readable, no DOM to decode it).\n *\n * @group Map Theme\n */\nexport const deriveMapColorsFromImage = async (\n source: MapThemeImageSource,\n options: MapThemeExtractionOptions = {},\n): Promise<Required<MapColors>> => {\n const pixels = isImagePixels(source) ? source : await readPixels(source);\n const colors = dominantColors(pixels, options.colorCount);\n if (!colors.length) {\n throw new RangeError('deriveMapColorsFromImage: the image has no opaque pixels to take colours from.');\n }\n\n return deriveMapColors(colors, options);\n};\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ [
2
+ {
3
+ "name": "culori",
4
+ "version": "4.0.2",
5
+ "license": "MIT",
6
+ "bundled": false,
7
+ "description": "A general-purpose color library for JavaScript",
8
+ "repository": "git@github.com:Evercoder/culori.git",
9
+ "author": "Dan Burzo <dan@danburzo.ro>",
10
+ "licenseText": "MIT License\n\nCopyright (c) 2018 Dan Burzo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
11
+ }
12
+ ]
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@tomtom-org/maps-sdk-plugin-map-theme",
3
+ "author": "TomTom International B.V.",
4
+ "license": "See in LICENSE.txt",
5
+ "version": "0.1.0",
6
+ "type": "module",
7
+ "description": "Theme a TomTomMap from wherever a look comes from — a brand's colours, a website's, a described mood, an artist's palette, or an image — by assigning them to the SDK's ten map colours",
8
+ "types": "dist/index.d.ts",
9
+ "peerDependencies": {
10
+ "@tomtom-org/maps-sdk": ">=0.58.0 <1.0.0",
11
+ "culori": "^4.0.2"
12
+ },
13
+ "devDependencies": {
14
+ "@tsconfig/recommended": "^1.0.13",
15
+ "@types/culori": "^4.0.1",
16
+ "@types/node": "^26.6.2",
17
+ "@vitest/coverage-v8": "^4.1.11",
18
+ "rolldown": "^1.2.11",
19
+ "ts-node": "^10.9.2",
20
+ "typescript": "^6.0.3",
21
+ "vite": "^8.3.1",
22
+ "vitest": "^4.1.11",
23
+ "@tomtom-org/maps-sdk": "0.58.0"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "package.json",
28
+ "README.md",
29
+ "LICENSE.txt",
30
+ "THIRD_PARTY.txt"
31
+ ],
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.es.js"
36
+ }
37
+ },
38
+ "scripts": {
39
+ "build": "vite build --config ../plugin-vite-config.ts",
40
+ "build:watch": "vite build --watch --config ../plugin-vite-config.ts",
41
+ "test": "vitest run",
42
+ "test:coverage": "vitest run --coverage",
43
+ "type-check": "tsc --noEmit"
44
+ }
45
+ }