@react-x11/components 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/README.md +95 -34
  2. package/dist/charts/node.d.ts +1 -1
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +6 -2
  5. package/dist/charts/node.js.map +1 -1
  6. package/dist/embed/client.d.ts +48 -3
  7. package/dist/embed/client.d.ts.map +1 -1
  8. package/dist/embed/client.js +79 -5
  9. package/dist/embed/client.js.map +1 -1
  10. package/dist/embed/index.d.ts +1 -1
  11. package/dist/embed/index.d.ts.map +1 -1
  12. package/dist/embed/index.js +1 -1
  13. package/dist/embed/index.js.map +1 -1
  14. package/dist/html/node.d.ts +3 -1
  15. package/dist/html/node.d.ts.map +1 -1
  16. package/dist/html/node.js +7 -5
  17. package/dist/html/node.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/{markdown → internal/markdown}/ast.d.ts +12 -0
  23. package/dist/internal/markdown/ast.d.ts.map +1 -0
  24. package/dist/internal/markdown/ast.js +22 -0
  25. package/dist/internal/markdown/ast.js.map +1 -0
  26. package/dist/internal/markdown/parse.d.ts.map +1 -0
  27. package/dist/{markdown → internal/markdown}/parse.js +112 -22
  28. package/dist/internal/markdown/parse.js.map +1 -0
  29. package/dist/internal/markdown/stringify.d.ts +4 -0
  30. package/dist/internal/markdown/stringify.d.ts.map +1 -0
  31. package/dist/internal/markdown/stringify.js +684 -0
  32. package/dist/internal/markdown/stringify.js.map +1 -0
  33. package/dist/internal/markdown/tags.d.ts.map +1 -0
  34. package/dist/internal/markdown/tags.js.map +1 -0
  35. package/dist/internal/scroll.d.ts +3 -2
  36. package/dist/internal/scroll.d.ts.map +1 -1
  37. package/dist/internal/scroll.js +1 -0
  38. package/dist/internal/scroll.js.map +1 -1
  39. package/dist/maps/anchors.d.ts +98 -0
  40. package/dist/maps/anchors.d.ts.map +1 -0
  41. package/dist/maps/anchors.js +531 -0
  42. package/dist/maps/anchors.js.map +1 -0
  43. package/dist/maps/controller.d.ts +283 -0
  44. package/dist/maps/controller.d.ts.map +1 -0
  45. package/dist/maps/controller.js +743 -0
  46. package/dist/maps/controller.js.map +1 -0
  47. package/dist/maps/gl/buckets.d.ts +95 -0
  48. package/dist/maps/gl/buckets.d.ts.map +1 -0
  49. package/dist/maps/gl/buckets.js +362 -0
  50. package/dist/maps/gl/buckets.js.map +1 -0
  51. package/dist/maps/gl/build-worker.d.ts +13 -0
  52. package/dist/maps/gl/build-worker.d.ts.map +1 -0
  53. package/dist/maps/gl/build-worker.js +44 -0
  54. package/dist/maps/gl/build-worker.js.map +1 -0
  55. package/dist/maps/gl/color.d.ts +8 -0
  56. package/dist/maps/gl/color.d.ts.map +1 -0
  57. package/dist/maps/gl/color.js +57 -0
  58. package/dist/maps/gl/color.js.map +1 -0
  59. package/dist/maps/gl/cover.d.ts +51 -0
  60. package/dist/maps/gl/cover.d.ts.map +1 -0
  61. package/dist/maps/gl/cover.js +124 -0
  62. package/dist/maps/gl/cover.js.map +1 -0
  63. package/dist/maps/gl/index.d.ts +3 -0
  64. package/dist/maps/gl/index.d.ts.map +1 -0
  65. package/dist/maps/gl/index.js +20 -0
  66. package/dist/maps/gl/index.js.map +1 -0
  67. package/dist/maps/gl/markers.d.ts +28 -0
  68. package/dist/maps/gl/markers.d.ts.map +1 -0
  69. package/dist/maps/gl/markers.js +60 -0
  70. package/dist/maps/gl/markers.js.map +1 -0
  71. package/dist/maps/gl/overlays.d.ts +68 -0
  72. package/dist/maps/gl/overlays.d.ts.map +1 -0
  73. package/dist/maps/gl/overlays.js +300 -0
  74. package/dist/maps/gl/overlays.js.map +1 -0
  75. package/dist/maps/gl/pane.d.ts +12 -0
  76. package/dist/maps/gl/pane.d.ts.map +1 -0
  77. package/dist/maps/gl/pane.js +37 -0
  78. package/dist/maps/gl/pane.js.map +1 -0
  79. package/dist/maps/gl/placement.d.ts +111 -0
  80. package/dist/maps/gl/placement.d.ts.map +1 -0
  81. package/dist/maps/gl/placement.js +567 -0
  82. package/dist/maps/gl/placement.js.map +1 -0
  83. package/dist/maps/gl/renderer.d.ts +352 -0
  84. package/dist/maps/gl/renderer.d.ts.map +1 -0
  85. package/dist/maps/gl/renderer.js +1127 -0
  86. package/dist/maps/gl/renderer.js.map +1 -0
  87. package/dist/maps/gl/shaders.d.ts +120 -0
  88. package/dist/maps/gl/shaders.d.ts.map +1 -0
  89. package/dist/maps/gl/shaders.js +451 -0
  90. package/dist/maps/gl/shaders.js.map +1 -0
  91. package/dist/maps/gl/store.d.ts +115 -0
  92. package/dist/maps/gl/store.d.ts.map +1 -0
  93. package/dist/maps/gl/store.js +469 -0
  94. package/dist/maps/gl/store.js.map +1 -0
  95. package/dist/maps/gl/text.d.ts +154 -0
  96. package/dist/maps/gl/text.d.ts.map +1 -0
  97. package/dist/maps/gl/text.js +382 -0
  98. package/dist/maps/gl/text.js.map +1 -0
  99. package/dist/maps/gl/view.d.ts +54 -0
  100. package/dist/maps/gl/view.d.ts.map +1 -0
  101. package/dist/maps/gl/view.js +974 -0
  102. package/dist/maps/gl/view.js.map +1 -0
  103. package/dist/maps/index.d.ts +1 -1
  104. package/dist/maps/index.d.ts.map +1 -1
  105. package/dist/maps/index.js +216 -52
  106. package/dist/maps/index.js.map +1 -1
  107. package/dist/maps/labels.d.ts +37 -16
  108. package/dist/maps/labels.d.ts.map +1 -1
  109. package/dist/maps/labels.js +188 -119
  110. package/dist/maps/labels.js.map +1 -1
  111. package/dist/maps/node.d.ts +162 -56
  112. package/dist/maps/node.d.ts.map +1 -1
  113. package/dist/maps/node.js +607 -515
  114. package/dist/maps/node.js.map +1 -1
  115. package/dist/maps/overlay.d.ts +72 -9
  116. package/dist/maps/overlay.d.ts.map +1 -1
  117. package/dist/maps/overlay.js +108 -28
  118. package/dist/maps/overlay.js.map +1 -1
  119. package/dist/maps/paint.d.ts +3 -0
  120. package/dist/maps/paint.d.ts.map +1 -1
  121. package/dist/maps/paint.js.map +1 -1
  122. package/dist/maps/proj.d.ts +14 -0
  123. package/dist/maps/proj.d.ts.map +1 -1
  124. package/dist/maps/proj.js +28 -0
  125. package/dist/maps/proj.js.map +1 -1
  126. package/dist/maps/renderer.d.ts +69 -0
  127. package/dist/maps/renderer.d.ts.map +1 -0
  128. package/dist/maps/renderer.js +66 -0
  129. package/dist/maps/renderer.js.map +1 -0
  130. package/dist/maps/sources.d.ts +25 -3
  131. package/dist/maps/sources.d.ts.map +1 -1
  132. package/dist/maps/sources.js +17 -0
  133. package/dist/maps/sources.js.map +1 -1
  134. package/dist/maps/theme.d.ts +19 -0
  135. package/dist/maps/theme.d.ts.map +1 -0
  136. package/dist/maps/theme.js +44 -0
  137. package/dist/maps/theme.js.map +1 -0
  138. package/dist/maps/tiles.d.ts +117 -17
  139. package/dist/maps/tiles.d.ts.map +1 -1
  140. package/dist/maps/tiles.js +163 -30
  141. package/dist/maps/tiles.js.map +1 -1
  142. package/dist/maps/types.d.ts +188 -20
  143. package/dist/maps/types.d.ts.map +1 -1
  144. package/dist/markdown/expressions.d.ts +1 -1
  145. package/dist/markdown/expressions.d.ts.map +1 -1
  146. package/dist/markdown/expressions.js.map +1 -1
  147. package/dist/markdown/index.d.ts +7 -6
  148. package/dist/markdown/index.d.ts.map +1 -1
  149. package/dist/markdown/index.js +9 -7
  150. package/dist/markdown/index.js.map +1 -1
  151. package/dist/markdown/spans.d.ts +1 -1
  152. package/dist/markdown/spans.d.ts.map +1 -1
  153. package/dist/media-player/index.d.ts +11 -3
  154. package/dist/media-player/index.d.ts.map +1 -1
  155. package/dist/media-player/index.js +13 -2
  156. package/dist/media-player/index.js.map +1 -1
  157. package/dist/rich-text-editor/clipboard.d.ts +23 -0
  158. package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
  159. package/dist/rich-text-editor/clipboard.js +307 -0
  160. package/dist/rich-text-editor/clipboard.js.map +1 -0
  161. package/dist/rich-text-editor/collab.d.ts +32 -0
  162. package/dist/rich-text-editor/collab.d.ts.map +1 -0
  163. package/dist/rich-text-editor/collab.js +55 -0
  164. package/dist/rich-text-editor/collab.js.map +1 -0
  165. package/dist/rich-text-editor/commands.d.ts +50 -0
  166. package/dist/rich-text-editor/commands.d.ts.map +1 -0
  167. package/dist/rich-text-editor/commands.js +376 -0
  168. package/dist/rich-text-editor/commands.js.map +1 -0
  169. package/dist/rich-text-editor/drag.d.ts +68 -0
  170. package/dist/rich-text-editor/drag.d.ts.map +1 -0
  171. package/dist/rich-text-editor/drag.js +148 -0
  172. package/dist/rich-text-editor/drag.js.map +1 -0
  173. package/dist/rich-text-editor/html.d.ts +145 -0
  174. package/dist/rich-text-editor/html.d.ts.map +1 -0
  175. package/dist/rich-text-editor/html.js +516 -0
  176. package/dist/rich-text-editor/html.js.map +1 -0
  177. package/dist/rich-text-editor/index.d.ts +263 -0
  178. package/dist/rich-text-editor/index.d.ts.map +1 -0
  179. package/dist/rich-text-editor/index.js +822 -0
  180. package/dist/rich-text-editor/index.js.map +1 -0
  181. package/dist/rich-text-editor/inline.d.ts +94 -0
  182. package/dist/rich-text-editor/inline.d.ts.map +1 -0
  183. package/dist/rich-text-editor/inline.js +252 -0
  184. package/dist/rich-text-editor/inline.js.map +1 -0
  185. package/dist/rich-text-editor/keymap.d.ts +71 -0
  186. package/dist/rich-text-editor/keymap.d.ts.map +1 -0
  187. package/dist/rich-text-editor/keymap.js +393 -0
  188. package/dist/rich-text-editor/keymap.js.map +1 -0
  189. package/dist/rich-text-editor/keys.d.ts +29 -0
  190. package/dist/rich-text-editor/keys.d.ts.map +1 -0
  191. package/dist/rich-text-editor/keys.js +93 -0
  192. package/dist/rich-text-editor/keys.js.map +1 -0
  193. package/dist/rich-text-editor/look.d.ts +71 -0
  194. package/dist/rich-text-editor/look.d.ts.map +1 -0
  195. package/dist/rich-text-editor/look.js +342 -0
  196. package/dist/rich-text-editor/look.js.map +1 -0
  197. package/dist/rich-text-editor/markdown.d.ts +18 -0
  198. package/dist/rich-text-editor/markdown.d.ts.map +1 -0
  199. package/dist/rich-text-editor/markdown.js +496 -0
  200. package/dist/rich-text-editor/markdown.js.map +1 -0
  201. package/dist/rich-text-editor/nodes.d.ts +135 -0
  202. package/dist/rich-text-editor/nodes.d.ts.map +1 -0
  203. package/dist/rich-text-editor/nodes.js +397 -0
  204. package/dist/rich-text-editor/nodes.js.map +1 -0
  205. package/dist/rich-text-editor/render.d.ts +68 -0
  206. package/dist/rich-text-editor/render.d.ts.map +1 -0
  207. package/dist/rich-text-editor/render.js +500 -0
  208. package/dist/rich-text-editor/render.js.map +1 -0
  209. package/dist/rich-text-editor/schema.d.ts +16 -0
  210. package/dist/rich-text-editor/schema.d.ts.map +1 -0
  211. package/dist/rich-text-editor/schema.js +386 -0
  212. package/dist/rich-text-editor/schema.js.map +1 -0
  213. package/dist/rich-text-editor/store.d.ts +11 -0
  214. package/dist/rich-text-editor/store.d.ts.map +1 -0
  215. package/dist/rich-text-editor/store.js +43 -0
  216. package/dist/rich-text-editor/store.js.map +1 -0
  217. package/dist/rich-text-editor/suggest.d.ts +127 -0
  218. package/dist/rich-text-editor/suggest.d.ts.map +1 -0
  219. package/dist/rich-text-editor/suggest.js +0 -0
  220. package/dist/rich-text-editor/suggest.js.map +1 -0
  221. package/dist/rich-text-editor/tables.d.ts +41 -0
  222. package/dist/rich-text-editor/tables.d.ts.map +1 -0
  223. package/dist/rich-text-editor/tables.js +204 -0
  224. package/dist/rich-text-editor/tables.js.map +1 -0
  225. package/dist/rich-text-editor/toolbar.d.ts +58 -0
  226. package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
  227. package/dist/rich-text-editor/toolbar.js +548 -0
  228. package/dist/rich-text-editor/toolbar.js.map +1 -0
  229. package/dist/rich-text-editor/view.d.ts +368 -0
  230. package/dist/rich-text-editor/view.d.ts.map +1 -0
  231. package/dist/rich-text-editor/view.js +1968 -0
  232. package/dist/rich-text-editor/view.js.map +1 -0
  233. package/dist/rich-text-editor/virtual.d.ts +54 -0
  234. package/dist/rich-text-editor/virtual.d.ts.map +1 -0
  235. package/dist/rich-text-editor/virtual.js +262 -0
  236. package/dist/rich-text-editor/virtual.js.map +1 -0
  237. package/dist/richtext/node.d.ts +25 -2
  238. package/dist/richtext/node.d.ts.map +1 -1
  239. package/dist/richtext/node.js +46 -24
  240. package/dist/richtext/node.js.map +1 -1
  241. package/dist/terminal/backends.d.ts +4 -1
  242. package/dist/terminal/backends.d.ts.map +1 -1
  243. package/dist/terminal/backends.js.map +1 -1
  244. package/dist/terminal/index.d.ts +18 -7
  245. package/dist/terminal/index.d.ts.map +1 -1
  246. package/dist/terminal/index.js +28 -11
  247. package/dist/terminal/index.js.map +1 -1
  248. package/package.json +23 -4
  249. package/src/charts/node.ts +6 -2
  250. package/src/embed/client.ts +102 -8
  251. package/src/embed/index.ts +2 -0
  252. package/src/html/node.ts +7 -5
  253. package/src/index.ts +2 -0
  254. package/src/{markdown → internal/markdown}/ast.ts +24 -7
  255. package/src/{markdown → internal/markdown}/parse.ts +134 -24
  256. package/src/internal/markdown/stringify.ts +772 -0
  257. package/src/internal/scroll.ts +5 -3
  258. package/src/maps/anchors.ts +607 -0
  259. package/src/maps/controller.ts +999 -0
  260. package/src/maps/gl/buckets.ts +457 -0
  261. package/src/maps/gl/build-worker.ts +55 -0
  262. package/src/maps/gl/color.ts +62 -0
  263. package/src/maps/gl/cover.ts +170 -0
  264. package/src/maps/gl/index.ts +23 -0
  265. package/src/maps/gl/markers.ts +83 -0
  266. package/src/maps/gl/overlays.ts +379 -0
  267. package/src/maps/gl/pane.ts +42 -0
  268. package/src/maps/gl/placement.ts +816 -0
  269. package/src/maps/gl/renderer.ts +1576 -0
  270. package/src/maps/gl/shaders.ts +486 -0
  271. package/src/maps/gl/store.ts +566 -0
  272. package/src/maps/gl/text.ts +495 -0
  273. package/src/maps/gl/view.ts +1224 -0
  274. package/src/maps/index.ts +270 -81
  275. package/src/maps/labels.ts +214 -121
  276. package/src/maps/node.ts +763 -629
  277. package/src/maps/overlay.ts +148 -43
  278. package/src/maps/paint.ts +3 -0
  279. package/src/maps/proj.ts +31 -0
  280. package/src/maps/renderer.ts +155 -0
  281. package/src/maps/sources.ts +38 -3
  282. package/src/maps/theme.ts +51 -0
  283. package/src/maps/tiles.ts +191 -33
  284. package/src/maps/types.ts +188 -20
  285. package/src/markdown/expressions.ts +5 -1
  286. package/src/markdown/index.ts +14 -11
  287. package/src/markdown/spans.ts +1 -1
  288. package/src/media-player/index.ts +18 -4
  289. package/src/rich-text-editor/clipboard.ts +439 -0
  290. package/src/rich-text-editor/collab.ts +91 -0
  291. package/src/rich-text-editor/commands.ts +448 -0
  292. package/src/rich-text-editor/dom-globals.d.ts +23 -0
  293. package/src/rich-text-editor/drag.ts +211 -0
  294. package/src/rich-text-editor/html.ts +619 -0
  295. package/src/rich-text-editor/index.ts +1331 -0
  296. package/src/rich-text-editor/inline.ts +345 -0
  297. package/src/rich-text-editor/keymap.ts +510 -0
  298. package/src/rich-text-editor/keys.ts +119 -0
  299. package/src/rich-text-editor/look.ts +427 -0
  300. package/src/rich-text-editor/markdown.ts +653 -0
  301. package/src/rich-text-editor/nodes.ts +497 -0
  302. package/src/rich-text-editor/render.ts +800 -0
  303. package/src/rich-text-editor/schema.ts +431 -0
  304. package/src/rich-text-editor/store.ts +39 -0
  305. package/src/rich-text-editor/suggest.ts +597 -0
  306. package/src/rich-text-editor/tables.ts +272 -0
  307. package/src/rich-text-editor/toolbar.ts +724 -0
  308. package/src/rich-text-editor/view.ts +2287 -0
  309. package/src/rich-text-editor/virtual.ts +347 -0
  310. package/src/richtext/node.ts +59 -30
  311. package/src/terminal/backends.ts +4 -1
  312. package/src/terminal/index.ts +43 -15
  313. package/dist/markdown/ast.d.ts.map +0 -1
  314. package/dist/markdown/ast.js +0 -17
  315. package/dist/markdown/ast.js.map +0 -1
  316. package/dist/markdown/parse.d.ts.map +0 -1
  317. package/dist/markdown/parse.js.map +0 -1
  318. package/dist/markdown/tags.d.ts.map +0 -1
  319. package/dist/markdown/tags.js.map +0 -1
  320. /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
  321. /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
  322. /package/dist/{markdown → internal/markdown}/tags.js +0 -0
  323. /package/src/{markdown → internal/markdown}/tags.ts +0 -0
package/src/maps/index.ts CHANGED
@@ -14,8 +14,9 @@
14
14
  // until an application imports the component that needs it, so
15
15
  // `sideEffects: false` stays honest. Do not move it into `../index.ts`
16
16
  // (AGENTS.md, "Tree-shaking is a constraint").
17
- import React, { useImperativeHandle, useRef } from 'react';
18
- import type { ReactElement } from 'react';
17
+ import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
18
+ import type { ReactElement, ReactNode } from 'react';
19
+ import { useApp, useSupports, useTheme } from 'react-x11';
19
20
  import { registerElement, registeredElements } from 'react-x11/host';
20
21
  import { createStyles, flattenStyle } from 'react-x11/style';
21
22
  // Loads the module the JSX augmentation at the bottom targets: nothing in
@@ -24,16 +25,25 @@ import { createStyles, flattenStyle } from 'react-x11/style';
24
25
  // addition. Type-only, so it is erased.
25
26
  import type {} from 'react-x11/jsx-runtime';
26
27
 
28
+ import { DEFAULT_CAMERA, MapController } from './controller.js';
27
29
  import { ELEMENT, MapViewNode, SELF_DAMAGED_PROPS } from './node.js';
30
+ import {
31
+ chooseRenderer,
32
+ glRendererModule,
33
+ loadGlRenderer,
34
+ rendererFromEnv,
35
+ rendererRequest,
36
+ } from './renderer.js';
37
+ import type { GlRendererModule } from './renderer.js';
38
+ import { defaultStyleFor, isDarkTheme } from './theme.js';
28
39
  import type {
29
- FitBoundsOptions,
30
- MapCamera,
31
- MapHandle,
32
40
  MapProps,
41
+ MapRenderer,
42
+ MapRendererReason,
33
43
  MapViewProps,
34
44
  } from './types.js';
35
- import type { LngLat, LngLatBounds } from './proj.js';
36
- import type { MapMarker } from './overlay.js';
45
+
46
+ const h = React.createElement;
37
47
 
38
48
  if (!registeredElements().includes(ELEMENT)) {
39
49
  registerElement(ELEMENT, {
@@ -65,8 +75,6 @@ const styles = createStyles({
65
75
  fill: { flexGrow: 1 },
66
76
  });
67
77
 
68
- const DEFAULT_CAMERA: MapCamera = { center: { lon: 0, lat: 20 }, zoom: 2 };
69
-
70
78
  /**
71
79
  * A map.
72
80
  *
@@ -91,87 +99,263 @@ const DEFAULT_CAMERA: MapCamera = { center: { lon: 0, lat: 20 }, zoom: 2 };
91
99
  * controlled form, where the application owns where the map is looking.
92
100
  */
93
101
  export function Map(props: MapProps): ReactElement {
94
- const {
95
- camera,
96
- defaultCamera,
97
- onCameraChange,
98
- style,
99
- children,
100
- ref,
101
- ...rest
102
- } = props;
103
-
104
- const view = useRef<MapViewNode | null>(null);
105
- const controlled = camera !== undefined;
106
-
107
- useImperativeHandle(
108
- ref,
109
- (): MapHandle => ({
110
- // Every method reads the node at call time rather than closing over
111
- // it: the ref attaches after the commit that created the node, and a
112
- // handle built once must still work for the whole mount.
113
- getCamera: () => view.current?.getCamera() ?? DEFAULT_CAMERA,
114
- setCamera: (next: Partial<MapCamera>) => view.current?.setCamera(next),
115
- panBy: (dx: number, dy: number) => view.current?.panBy(dx, dy),
116
- zoomIn: (step?: number) => view.current?.zoomIn(step),
117
- zoomOut: (step?: number) => view.current?.zoomOut(step),
118
- zoomTo: (zoom: number) => view.current?.zoomTo(zoom),
119
- fitBounds: (bounds: LngLatBounds, options?: FitBoundsOptions) =>
120
- view.current?.fitBounds(bounds, options),
121
- fitMarkers: (ids?: readonly string[], options?: FitBoundsOptions) =>
122
- view.current?.fitMarkers(ids, options),
123
- getBounds: () =>
124
- view.current?.getBounds() ?? {
125
- west: -180,
126
- south: -85,
127
- east: 180,
128
- north: 85,
129
- },
130
- project: (position: LngLat) =>
131
- view.current?.project(position) ?? { x: 0, y: 0 },
132
- unproject: (x: number, y: number) =>
133
- view.current?.unproject(x, y) ?? { lon: 0, lat: 0 },
134
- markerAt: (x: number, y: number): MapMarker | null =>
135
- view.current?.markerAt(x, y) ?? null,
136
- refresh: () => view.current?.refresh(),
137
- stats: () => view.current?.stats() ?? null,
138
- }),
139
- [],
102
+ // One controller for the life of the mount: the camera, the gestures and
103
+ // the handle, which whichever renderer is drawing answers to (see
104
+ // `./controller.ts`). Seeded here, once — `defaultCamera` is read on the
105
+ // first render and never again, which is what makes it a *default*.
106
+ const [controller] = useState(
107
+ () => new MapController(props.defaultCamera ?? DEFAULT_CAMERA),
140
108
  );
109
+ // Read at event and frame time, so the latest props are always the ones
110
+ // that count — and the controlled/uncontrolled fork is simply whether
111
+ // `camera` is among them.
112
+ controller.setProps(props);
113
+ // The controller's own handle, made once: the same object for the whole
114
+ // mount, whatever draws the map — across a renderer switch too.
115
+ useImperativeHandle(props.ref, () => controller.handle, [controller]);
116
+ useEffect(() => () => controller.dispose(), [controller]);
117
+
118
+ // A GL failure is the map's for the rest of its mount: an `'auto'` map
119
+ // that met one draws through the retained renderer from then on.
120
+ const [failure, setFailure] = useState<Error | null>(null);
121
+ const latest = useRef(props);
122
+ latest.current = props;
141
123
 
142
- const flat = flattenStyle(style ?? null);
124
+ const flat = flattenStyle(props.style ?? null);
143
125
  const sized =
144
126
  flat.height !== undefined ||
145
127
  flat.flexGrow !== undefined ||
146
128
  flat.flexBasis !== undefined;
147
129
 
148
- // The pane and anything the application puts over it are siblings rather
149
- // than parent and children: a registered element's own drawing happens
150
- // *after* `super.paint` has painted its children, so anything mounted
151
- // inside would be painted over by the map. Beside it, and after it, a
152
- // legend or a control panel lands on top. `<Flow>` makes the same
153
- // arrangement for the same reason.
154
- return React.createElement(
130
+ const { asked, forced } = rendererRequest(props.renderer, rendererFromEnv());
131
+ const pane =
132
+ asked === 'auto'
133
+ ? h(AutoPane, {
134
+ key: 'auto',
135
+ map: props,
136
+ controller,
137
+ failed: failure !== null,
138
+ onFailure: setFailure,
139
+ })
140
+ : h(MapPane, {
141
+ key: asked,
142
+ renderer: asked,
143
+ reason: forced ? 'forced' : null,
144
+ map: props,
145
+ controller,
146
+ // Asked for by name: never a fallback, always the application's
147
+ // to hear about.
148
+ onFailure: (error: Error) => latest.current.onError?.(error),
149
+ });
150
+
151
+ return h(
155
152
  'box',
156
- { style: sized ? [styles.clip, style] : [styles.pane, style] },
157
- React.createElement(ELEMENT, {
158
- ...(rest as MapViewProps),
159
- key: 'pane',
160
- ref: view,
161
- // The controlled/uncontrolled fork, and the whole of it. Controlled:
162
- // the application's camera goes down and the element only ever
163
- // *asks* to move. Uncontrolled: nothing goes down after the seed, the
164
- // element owns the camera, and a pan never reaches React — which is
165
- // the difference between a drag that blits a strip and one that
166
- // re-renders and re-claims the pane on every pointer step.
167
- camera: controlled ? camera : undefined,
168
- defaultCamera: controlled ? undefined : (defaultCamera ?? DEFAULT_CAMERA),
169
- onCameraChange,
170
- style: styles.fill,
171
- role: rest.role ?? 'group',
172
- 'aria-label': rest['aria-label'] ?? 'Map',
153
+ { style: sized ? [styles.clip, props.style] : [styles.pane, props.style] },
154
+ pane,
155
+ );
156
+ }
157
+
158
+ /** Props `<Map>` reads itself, or that only the GL renderer reads — kept off
159
+ * the `<mapview>` element, which would take each as a prop of its own. */
160
+ const NOT_FOR_THE_ELEMENT: readonly string[] = [
161
+ 'camera',
162
+ 'defaultCamera',
163
+ 'style',
164
+ 'children',
165
+ 'ref',
166
+ 'renderer',
167
+ 'onRendererChange',
168
+ 'onError',
169
+ 'levelFade',
170
+ 'adaptive',
171
+ 'buildWorkers',
172
+ 'antialias',
173
+ 'fillRule',
174
+ 'onAfterDraw',
175
+ ];
176
+
177
+ function elementProps(map: MapProps): MapViewProps {
178
+ const out: Record<string, unknown> = { ...map };
179
+ for (const name of NOT_FOR_THE_ELEMENT) delete out[name];
180
+ return out as MapViewProps;
181
+ }
182
+
183
+ interface PaneProps {
184
+ map: MapProps;
185
+ controller: MapController;
186
+ onFailure: (error: Error) => void;
187
+ }
188
+
189
+ /**
190
+ * `renderer="auto"`: GL where this connection draws through the direct
191
+ * backend, the retained renderer everywhere else — `./renderer.ts` is the
192
+ * decision, and this is where it reads the connection. Its own component so
193
+ * that a map that asked for a renderer by name never asks the connection: on
194
+ * the Cocoa backend the question is a GL runtime brought up to answer it.
195
+ */
196
+ function AutoPane(props: PaneProps & { failed: boolean }): ReactElement {
197
+ const { map } = props;
198
+ const app = useApp();
199
+ const shaders = useSupports('shaders');
200
+ const probing = useDirectGlProbe(app, shaders);
201
+ const choice = chooseRenderer({
202
+ requested: 'auto',
203
+ shaders,
204
+ probing,
205
+ failed: props.failed,
206
+ });
207
+ return h(MapPane, {
208
+ renderer: choice.renderer,
209
+ reason: choice.reason,
210
+ map,
211
+ controller: props.controller,
212
+ onFailure: props.onFailure,
213
+ });
214
+ }
215
+
216
+ /**
217
+ * Whether this connection's answer to `useSupports('shaders')` is still to
218
+ * come. Under a policy that could choose direct GL, X11 settles it before
219
+ * `createRoot()` hands the app back; the Cocoa backend settles it on the
220
+ * first ask, which is this. Until it has, a map shows its background rather
221
+ * than drawing through the retained renderer for a few milliseconds and
222
+ * fetching the view's tiles twice.
223
+ */
224
+ function useDirectGlProbe(app: unknown, shaders: boolean): boolean {
225
+ const mode = (app as { glPolicy?: { mode?: string } } | null)?.glPolicy?.mode;
226
+ const wants = mode === 'auto' || mode === 'direct';
227
+ const [settled, setSettled] = useState(false);
228
+ const pending = wants && !shaders && !settled;
229
+ useEffect(() => {
230
+ if (!pending) return;
231
+ const probe = (
232
+ app as { glCapabilities?(): Promise<unknown> } | null
233
+ )?.glCapabilities?.();
234
+ if (!probe) {
235
+ setSettled(true);
236
+ return;
237
+ }
238
+ let live = true;
239
+ const done = (): void => {
240
+ if (live) setSettled(true);
241
+ };
242
+ probe.then(done, done);
243
+ return () => {
244
+ live = false;
245
+ };
246
+ }, [app, pending]);
247
+ return pending;
248
+ }
249
+
250
+ /**
251
+ * The pane, drawn by the renderer chosen: the `<mapview>` element, the GL
252
+ * renderer once its module has loaded, or — while the connection's answer
253
+ * or the module is on its way — a box in the style's background. Whichever
254
+ * it is, it answers to the map's one controller.
255
+ */
256
+ function MapPane(
257
+ props: PaneProps & {
258
+ renderer: MapRenderer | 'pending';
259
+ reason: MapRendererReason | null;
260
+ },
261
+ ): ReactElement {
262
+ const { renderer, reason, map, controller } = props;
263
+ const theme = useTheme();
264
+ const overlay = useSupports('glOverlay');
265
+
266
+ // `onRendererChange`: whenever the map lands somewhere it did not ask for
267
+ // — its first renderer, or the one it falls back to.
268
+ const reported = useRef<MapRenderer | null>(null);
269
+ useEffect(() => {
270
+ if (renderer === 'pending') return;
271
+ const before = reported.current;
272
+ reported.current = renderer;
273
+ if (before === renderer || reason === null) return;
274
+ map.onRendererChange?.(renderer, reason);
275
+ }, [renderer, reason]);
276
+
277
+ const wantGl = renderer === 'gl';
278
+ const [gl, setGl] = useState<GlRendererModule | null>(glRendererModule);
279
+ useEffect(() => {
280
+ if (!wantGl || gl) return;
281
+ let live = true;
282
+ loadGlRenderer().then(
283
+ (module) => {
284
+ if (live) setGl(module);
285
+ },
286
+ (error: unknown) => {
287
+ if (!live) return;
288
+ props.onFailure(
289
+ error instanceof Error ? error : new Error(String(error)),
290
+ );
291
+ },
292
+ );
293
+ return () => {
294
+ live = false;
295
+ };
296
+ }, [wantGl, gl]);
297
+
298
+ const common = {
299
+ style: styles.fill,
300
+ role: map.role ?? 'group',
301
+ 'aria-label': map['aria-label'] ?? 'Map',
302
+ 'data-testname': map['data-testname'],
303
+ };
304
+ // What the application puts over the map is the pane's sibling, after
305
+ // it, rather than its child: a registered element's own drawing happens
306
+ // *after* `super.paint` has painted its children, so anything mounted
307
+ // inside `<mapview>` would be painted over by the map. `<Flow>` makes the
308
+ // same arrangement for the same reason. The one exception is a GL pane:
309
+ // a sibling is under its surface, so there they go inside the surface,
310
+ // whose children core draws above it.
311
+ const over = (pane: ReactElement, children: ReactNode): ReactElement =>
312
+ h(React.Fragment, null, pane, children);
313
+ if (renderer === 'retained') {
314
+ return over(
315
+ h(ELEMENT, {
316
+ ...elementProps(map),
317
+ ...common,
318
+ key: 'retained',
319
+ mapController: controller,
320
+ // The controlled/uncontrolled fork, and the whole of it.
321
+ // Controlled: the application's camera goes down and the element
322
+ // only ever *asks* to move. Uncontrolled: nothing goes down, the
323
+ // controller owns the camera, and a pan never reaches React —
324
+ // which is the difference between a drag that blits a strip and
325
+ // one that re-renders and re-claims the pane on every pointer
326
+ // step.
327
+ camera: map.camera,
328
+ } as MapViewProps),
329
+ map.children,
330
+ );
331
+ }
332
+ if (renderer === 'gl' && gl) {
333
+ return over(
334
+ h(
335
+ gl.GlMapPane,
336
+ {
337
+ ...common,
338
+ key: 'gl',
339
+ controller,
340
+ map,
341
+ onFailure: props.onFailure,
342
+ },
343
+ overlay ? map.children : undefined,
344
+ ),
345
+ overlay ? null : map.children,
346
+ );
347
+ }
348
+ const background = (map.mapStyle ?? defaultStyleFor(isDarkTheme(theme)))
349
+ .background;
350
+ return over(
351
+ h('box', {
352
+ ...common,
353
+ key: 'pending',
354
+ style: background
355
+ ? [styles.fill, { backgroundColor: background }]
356
+ : styles.fill,
173
357
  }),
174
- children,
358
+ map.children,
175
359
  );
176
360
  }
177
361
 
@@ -256,9 +440,14 @@ export { TileCache } from './tiles.js';
256
440
  export type {
257
441
  FitBoundsOptions,
258
442
  MapFrameStats,
443
+ MapGlFrameStats,
444
+ MapGlProps,
259
445
  MapHandle,
260
446
  MapPointerEvent,
261
447
  MapProps,
448
+ MapRenderer,
449
+ MapRendererReason,
450
+ MapRendererRequest,
262
451
  MapViewProps,
263
452
  } from './types.js';
264
453
  export type {