@humanforest/nuxt-layer 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.
Files changed (324) hide show
  1. package/LICENSE +67 -0
  2. package/README.md +208 -0
  3. package/app.config.ts +10 -0
  4. package/assets/icons/forest/bike-asterisk.svg +21 -0
  5. package/assets/icons/forest/bike-down.svg +19 -0
  6. package/assets/icons/forest/bike-off.svg +20 -0
  7. package/assets/icons/forest/bike-up.svg +19 -0
  8. package/assets/icons/forest/bike-x.svg +19 -0
  9. package/assets/icons/forest/bike.svg +18 -0
  10. package/assets/icons/forest/mark-fill.svg +6 -0
  11. package/assets/icons/forest/mark.svg +6 -0
  12. package/assets/icons/forest/parking.svg +3 -0
  13. package/assets/icons/forest/star-fill.svg +4 -0
  14. package/assets/icons/forest/star.svg +4 -0
  15. package/assets/icons/forest/traffic-light-caution.svg +8 -0
  16. package/assets/icons/forest/traffic-light-go.svg +8 -0
  17. package/assets/icons/forest/traffic-light-lit.svg +8 -0
  18. package/assets/icons/forest/traffic-light-stop.svg +8 -0
  19. package/assets/icons/forest/traffic-light.svg +8 -0
  20. package/nuxt.config.ts +81 -0
  21. package/package.json +35 -0
  22. package/packages/charts/src/FBoroughShape.vue +52 -0
  23. package/packages/charts/src/FCalendarGrid.vue +332 -0
  24. package/packages/charts/src/FCellLegend.vue +54 -0
  25. package/packages/charts/src/FChartFrame.vue +201 -0
  26. package/packages/charts/src/FChartFrameSingle.vue +91 -0
  27. package/packages/charts/src/FChartLegend.vue +26 -0
  28. package/packages/charts/src/FDistributionBar.vue +224 -0
  29. package/packages/charts/src/FDottedMap.vue +335 -0
  30. package/packages/charts/src/FLondonMap.vue +399 -0
  31. package/packages/charts/src/FSparkline.vue +198 -0
  32. package/packages/charts/src/FStatusTrack.vue +151 -0
  33. package/packages/charts/src/cellSize.ts +46 -0
  34. package/packages/charts/src/distribution.ts +99 -0
  35. package/packages/charts/src/engine.ts +15 -0
  36. package/packages/charts/src/forestTooltip.ts +110 -0
  37. package/packages/charts/src/fromCategories.ts +43 -0
  38. package/packages/charts/src/index.ts +87 -0
  39. package/packages/charts/src/londonAreas.ts +75 -0
  40. package/packages/charts/src/londonGrid.ts +67 -0
  41. package/packages/charts/src/motionDuration.ts +49 -0
  42. package/packages/charts/src/presets.ts +143 -0
  43. package/packages/charts/src/resolveVar.ts +21 -0
  44. package/packages/charts/src/snapTooltip.ts +154 -0
  45. package/packages/charts/src/tooltipStandIn.ts +58 -0
  46. package/packages/charts/src/useCellScale.ts +155 -0
  47. package/packages/charts/src/useChartPalette.ts +61 -0
  48. package/packages/charts/src/useChartRepaintKey.ts +22 -0
  49. package/packages/charts/src/useThemeVersion.ts +17 -0
  50. package/packages/frames/src/FBrowserFrame.vue +53 -0
  51. package/packages/frames/src/FDeviceFrame.vue +242 -0
  52. package/packages/frames/src/FFitFrame.vue +83 -0
  53. package/packages/frames/src/art/iphone-11.svg +124 -0
  54. package/packages/frames/src/art/iphone-12.svg +601 -0
  55. package/packages/frames/src/art/iphone-13-pro.svg +820 -0
  56. package/packages/frames/src/art/iphone-13.svg +849 -0
  57. package/packages/frames/src/art/iphone-14.svg +681 -0
  58. package/packages/frames/src/art/iphone-15-pro.svg +633 -0
  59. package/packages/frames/src/art/iphone-15.svg +713 -0
  60. package/packages/frames/src/art/iphone-16-plus.svg +685 -0
  61. package/packages/frames/src/art/iphone-16-pro-max.svg +611 -0
  62. package/packages/frames/src/art/iphone-16-pro.svg +631 -0
  63. package/packages/frames/src/art/iphone-16.svg +713 -0
  64. package/packages/frames/src/art/iphone-17-pro-max.svg +355 -0
  65. package/packages/frames/src/art/iphone-17.svg +305 -0
  66. package/packages/frames/src/art/iphone-air.svg +389 -0
  67. package/packages/frames/src/art/iphone-se-3.svg +473 -0
  68. package/packages/frames/src/art/iphone-x.svg +652 -0
  69. package/packages/frames/src/art/pixel-10-pro-xl.svg +32 -0
  70. package/packages/frames/src/art/pixel-10-pro.svg +32 -0
  71. package/packages/frames/src/art/pixel-10.svg +32 -0
  72. package/packages/frames/src/art/pixel-8-pro.svg +208 -0
  73. package/packages/frames/src/art/pixel-8.svg +208 -0
  74. package/packages/frames/src/art/pixel-8a.svg +8 -0
  75. package/packages/frames/src/art/pixel-9-pro-xl.svg +32 -0
  76. package/packages/frames/src/art/pixel-9-pro.svg +34 -0
  77. package/packages/frames/src/art/pixel-9a.svg +25 -0
  78. package/packages/frames/src/art.ts +29 -0
  79. package/packages/frames/src/devices.ts +337 -0
  80. package/packages/frames/src/index.ts +15 -0
  81. package/packages/tokens/geo/london-boroughs.json +1 -0
  82. package/packages/tokens/geo/london-dots-coarse.json +1 -0
  83. package/packages/tokens/geo/london-dots-fine.json +1 -0
  84. package/packages/tokens/geo/london-dots-medium.json +1 -0
  85. package/packages/tokens/geo/london-dots-ultra.json +1 -0
  86. package/packages/tokens/geo/london-wards.json +1 -0
  87. package/packages/tokens/glyphs/glyphs.json +1 -0
  88. package/packages/tokens/logos/f-rot.svg +4 -0
  89. package/packages/tokens/logos/f.svg +4 -0
  90. package/packages/tokens/logos/favicon-c2w.svg +9 -0
  91. package/packages/tokens/logos/favicon-console.svg +9 -0
  92. package/packages/tokens/logos/favicon-ds.svg +9 -0
  93. package/packages/tokens/logos/favicon-fleet.svg +9 -0
  94. package/packages/tokens/logos/favicon-flex.svg +9 -0
  95. package/packages/tokens/logos/favicon-plus.svg +9 -0
  96. package/packages/tokens/logos/favicon-radar.svg +9 -0
  97. package/packages/tokens/logos/favicon-tower.svg +9 -0
  98. package/packages/tokens/logos/lockup-horizontal.svg +17 -0
  99. package/packages/tokens/logos/lockup-vertical.svg +17 -0
  100. package/packages/tokens/logos/mark-mono.svg +4 -0
  101. package/packages/tokens/logos/mark.svg +7 -0
  102. package/packages/tokens/logos/wordmark.svg +9 -0
  103. package/packages/tokens/src/colourEngine.ts +604 -0
  104. package/packages/tokens/src/glyphs.ts +127 -0
  105. package/packages/tokens/src/logo.ts +50 -0
  106. package/packages/tokens/src/subbrands.ts +44 -0
  107. package/packages/tokens/src/sublogo.ts +80 -0
  108. package/packages/ui/icons/forest/map/forest-place-count.svg +21 -0
  109. package/packages/ui/icons/forest/map/forest-place-marker.svg +17 -0
  110. package/packages/ui/icons/forest/map/forest-vehicle-marker.svg +73 -0
  111. package/packages/ui/icons/forest/map/manifest.json +373 -0
  112. package/packages/ui/icons/forest/map/place-count-manifest.json +83 -0
  113. package/packages/ui/icons/forest/map/place-manifest.json +74 -0
  114. package/packages/ui/src/address/address.ts +125 -0
  115. package/packages/ui/src/address/autofill.ts +78 -0
  116. package/packages/ui/src/address/geocode.ts +55 -0
  117. package/packages/ui/src/address/index.ts +31 -0
  118. package/packages/ui/src/address/validate.ts +91 -0
  119. package/packages/ui/src/canvas/FCanvasCard.vue +97 -0
  120. package/packages/ui/src/canvas/FCanvasControl.vue +121 -0
  121. package/packages/ui/src/canvas/FCanvasControlGroup.vue +100 -0
  122. package/packages/ui/src/canvas/FCanvasFlyout.vue +216 -0
  123. package/packages/ui/src/canvas/FCanvasHud.vue +402 -0
  124. package/packages/ui/src/canvas/canvasSurface.ts +39 -0
  125. package/packages/ui/src/canvas/index.ts +17 -0
  126. package/packages/ui/src/contexts/ForestContext.vue +53 -0
  127. package/packages/ui/src/contexts/contexts.ts +153 -0
  128. package/packages/ui/src/contexts/index.ts +23 -0
  129. package/packages/ui/src/contexts/marketing-theme.ts +214 -0
  130. package/packages/ui/src/contexts/mobile-theme.ts +402 -0
  131. package/packages/ui/src/filter/FFilterBar.vue +72 -0
  132. package/packages/ui/src/filter/FFilterChip.vue +362 -0
  133. package/packages/ui/src/filter/FFilterClauses.vue +104 -0
  134. package/packages/ui/src/filter/FFilterTrigger.vue +183 -0
  135. package/packages/ui/src/filter/filter.theme.ts +27 -0
  136. package/packages/ui/src/filter/filterClause.ts +265 -0
  137. package/packages/ui/src/filter/filterPredicate.ts +134 -0
  138. package/packages/ui/src/filter/filterSchema.ts +82 -0
  139. package/packages/ui/src/filter/index.ts +36 -0
  140. package/packages/ui/src/forest-preset.ts +68 -0
  141. package/packages/ui/src/icons.ts +79 -0
  142. package/packages/ui/src/index.ts +17 -0
  143. package/packages/ui/src/kpi/FKpi.vue +253 -0
  144. package/packages/ui/src/kpi/FKpiGroup.vue +161 -0
  145. package/packages/ui/src/kpi/index.ts +20 -0
  146. package/packages/ui/src/kpi/kpi.theme.ts +168 -0
  147. package/packages/ui/src/kpi/kpiDelta.ts +82 -0
  148. package/packages/ui/src/kpi/kpiFormat.ts +72 -0
  149. package/packages/ui/src/kpi/kpiGroupState.ts +63 -0
  150. package/packages/ui/src/logo/FLogo.vue +52 -0
  151. package/packages/ui/src/logo/FSubLogo.vue +214 -0
  152. package/packages/ui/src/logo/index.ts +8 -0
  153. package/packages/ui/src/map/FMap.vue +355 -0
  154. package/packages/ui/src/map/FMapCompass.vue +154 -0
  155. package/packages/ui/src/map/FMapCoordinates.vue +134 -0
  156. package/packages/ui/src/map/FMapDrawTools.vue +116 -0
  157. package/packages/ui/src/map/FMapFullscreen.vue +92 -0
  158. package/packages/ui/src/map/FMapGeolocate.vue +208 -0
  159. package/packages/ui/src/map/FMapLayers.vue +75 -0
  160. package/packages/ui/src/map/FMapLegend.vue +45 -0
  161. package/packages/ui/src/map/FMapPopup.vue +129 -0
  162. package/packages/ui/src/map/FMapRamp.vue +69 -0
  163. package/packages/ui/src/map/FMapRegion.vue +77 -0
  164. package/packages/ui/src/map/FMapScale.vue +107 -0
  165. package/packages/ui/src/map/FMapSearch.vue +362 -0
  166. package/packages/ui/src/map/FMapTilt.vue +87 -0
  167. package/packages/ui/src/map/FMapTools.vue +61 -0
  168. package/packages/ui/src/map/FMapZoom.vue +81 -0
  169. package/packages/ui/src/map/FMarker.vue +218 -0
  170. package/packages/ui/src/map/FTrackScrubber.vue +94 -0
  171. package/packages/ui/src/map/FVehicleLegend.vue +72 -0
  172. package/packages/ui/src/map/FVehicleMark.vue +166 -0
  173. package/packages/ui/src/map/bin.ts +196 -0
  174. package/packages/ui/src/map/coordinates.ts +83 -0
  175. package/packages/ui/src/map/density.ts +401 -0
  176. package/packages/ui/src/map/draw.ts +356 -0
  177. package/packages/ui/src/map/ease.ts +67 -0
  178. package/packages/ui/src/map/fullscreen.ts +59 -0
  179. package/packages/ui/src/map/heat.ts +160 -0
  180. package/packages/ui/src/map/index.ts +351 -0
  181. package/packages/ui/src/map/liveMarkers.ts +60 -0
  182. package/packages/ui/src/map/map.css +116 -0
  183. package/packages/ui/src/map/mapColor.ts +31 -0
  184. package/packages/ui/src/map/mapContext.ts +12 -0
  185. package/packages/ui/src/map/mapLayers.ts +55 -0
  186. package/packages/ui/src/map/mapLegend.ts +70 -0
  187. package/packages/ui/src/map/mapSearch.theme.ts +79 -0
  188. package/packages/ui/src/map/mapSearch.ts +279 -0
  189. package/packages/ui/src/map/mapUrl.ts +117 -0
  190. package/packages/ui/src/map/markerAsset.ts +3 -0
  191. package/packages/ui/src/map/padding.ts +39 -0
  192. package/packages/ui/src/map/place.ts +192 -0
  193. package/packages/ui/src/map/ramp.ts +95 -0
  194. package/packages/ui/src/map/regionSelect.ts +163 -0
  195. package/packages/ui/src/map/route.ts +167 -0
  196. package/packages/ui/src/map/scale.ts +329 -0
  197. package/packages/ui/src/map/scaleBar.ts +67 -0
  198. package/packages/ui/src/map/selectionModel.ts +50 -0
  199. package/packages/ui/src/map/sources/coordinates.ts +58 -0
  200. package/packages/ui/src/map/sources/mapboxPlaces.ts +71 -0
  201. package/packages/ui/src/map/style.ts +14 -0
  202. package/packages/ui/src/map/track.ts +279 -0
  203. package/packages/ui/src/map/useFeatureSelect.ts +230 -0
  204. package/packages/ui/src/map/useLiveMarkers.ts +84 -0
  205. package/packages/ui/src/map/useMap.ts +150 -0
  206. package/packages/ui/src/map/useMapDraw.ts +776 -0
  207. package/packages/ui/src/map/useMapOverlay.ts +421 -0
  208. package/packages/ui/src/map/useMapTableSelection.ts +101 -0
  209. package/packages/ui/src/map/useMapUrlState.ts +155 -0
  210. package/packages/ui/src/map/useMarkerSource.ts +163 -0
  211. package/packages/ui/src/map/useRegionSelect.ts +309 -0
  212. package/packages/ui/src/map/useTokenMode.ts +61 -0
  213. package/packages/ui/src/map/useTrackPlayback.ts +131 -0
  214. package/packages/ui/src/map/useVehicleFilter.ts +51 -0
  215. package/packages/ui/src/map/useVisibleFeatures.ts +124 -0
  216. package/packages/ui/src/map/vehicle.ts +807 -0
  217. package/packages/ui/src/map/vehicleFilter.ts +284 -0
  218. package/packages/ui/src/map/vehicleLegend.ts +155 -0
  219. package/packages/ui/src/map/visibleFeatures.ts +192 -0
  220. package/packages/ui/src/map/zone.ts +291 -0
  221. package/packages/ui/src/shell/FAppNavbar.vue +68 -0
  222. package/packages/ui/src/shell/FAppShell.vue +163 -0
  223. package/packages/ui/src/shell/FAppSidebar.vue +215 -0
  224. package/packages/ui/src/shell/FPageToolbar.vue +126 -0
  225. package/packages/ui/src/shell/FScrollShadow.vue +43 -0
  226. package/packages/ui/src/shell/FSidePanel.vue +258 -0
  227. package/packages/ui/src/shell/FToolbarActions.vue +32 -0
  228. package/packages/ui/src/shell/index.ts +33 -0
  229. package/packages/ui/src/shell/nav.ts +37 -0
  230. package/packages/ui/src/shell/pageToolbar.ts +69 -0
  231. package/packages/ui/src/shell/shell.ts +74 -0
  232. package/packages/ui/src/shell/sidePanel.ts +115 -0
  233. package/packages/ui/src/themes/accordion.ts +36 -0
  234. package/packages/ui/src/themes/alert.ts +16 -0
  235. package/packages/ui/src/themes/auth-form.ts +19 -0
  236. package/packages/ui/src/themes/avatar-group.ts +9 -0
  237. package/packages/ui/src/themes/avatar.ts +18 -0
  238. package/packages/ui/src/themes/badge.ts +51 -0
  239. package/packages/ui/src/themes/banner.ts +21 -0
  240. package/packages/ui/src/themes/breadcrumb.ts +10 -0
  241. package/packages/ui/src/themes/button.mechanics.ts +191 -0
  242. package/packages/ui/src/themes/button.ts +136 -0
  243. package/packages/ui/src/themes/calendar.ts +29 -0
  244. package/packages/ui/src/themes/card.ts +67 -0
  245. package/packages/ui/src/themes/carousel.ts +41 -0
  246. package/packages/ui/src/themes/checkbox-group.ts +20 -0
  247. package/packages/ui/src/themes/checkbox.ts +47 -0
  248. package/packages/ui/src/themes/chip.ts +50 -0
  249. package/packages/ui/src/themes/collapsible.ts +26 -0
  250. package/packages/ui/src/themes/color-picker.ts +13 -0
  251. package/packages/ui/src/themes/command-palette.ts +37 -0
  252. package/packages/ui/src/themes/container.ts +14 -0
  253. package/packages/ui/src/themes/context-menu.ts +38 -0
  254. package/packages/ui/src/themes/dashboard-group.ts +11 -0
  255. package/packages/ui/src/themes/dashboard-navbar.ts +10 -0
  256. package/packages/ui/src/themes/dashboard-panel.ts +18 -0
  257. package/packages/ui/src/themes/dashboard-resize-handle.ts +35 -0
  258. package/packages/ui/src/themes/dashboard-sidebar-collapse.ts +7 -0
  259. package/packages/ui/src/themes/dashboard-sidebar-toggle.ts +7 -0
  260. package/packages/ui/src/themes/dashboard-sidebar.ts +58 -0
  261. package/packages/ui/src/themes/dashboard-toolbar.ts +15 -0
  262. package/packages/ui/src/themes/drawer.ts +45 -0
  263. package/packages/ui/src/themes/dropdown-menu.ts +38 -0
  264. package/packages/ui/src/themes/empty.ts +43 -0
  265. package/packages/ui/src/themes/field-group.ts +52 -0
  266. package/packages/ui/src/themes/file-upload.ts +38 -0
  267. package/packages/ui/src/themes/footer-columns.ts +13 -0
  268. package/packages/ui/src/themes/footer.ts +9 -0
  269. package/packages/ui/src/themes/form-field.ts +25 -0
  270. package/packages/ui/src/themes/form.ts +22 -0
  271. package/packages/ui/src/themes/header.ts +12 -0
  272. package/packages/ui/src/themes/icon.ts +61 -0
  273. package/packages/ui/src/themes/index.ts +99 -0
  274. package/packages/ui/src/themes/input-date.ts +73 -0
  275. package/packages/ui/src/themes/input-menu.ts +66 -0
  276. package/packages/ui/src/themes/input-number.ts +58 -0
  277. package/packages/ui/src/themes/input-rating.ts +25 -0
  278. package/packages/ui/src/themes/input-tags.ts +63 -0
  279. package/packages/ui/src/themes/input-time.ts +70 -0
  280. package/packages/ui/src/themes/input.ts +148 -0
  281. package/packages/ui/src/themes/kbd.ts +27 -0
  282. package/packages/ui/src/themes/link.ts +27 -0
  283. package/packages/ui/src/themes/listbox.ts +48 -0
  284. package/packages/ui/src/themes/main.ts +6 -0
  285. package/packages/ui/src/themes/marquee.ts +45 -0
  286. package/packages/ui/src/themes/modal.ts +46 -0
  287. package/packages/ui/src/themes/navigation-menu.ts +80 -0
  288. package/packages/ui/src/themes/page-anchors.ts +7 -0
  289. package/packages/ui/src/themes/page-aside.ts +7 -0
  290. package/packages/ui/src/themes/page-card.ts +22 -0
  291. package/packages/ui/src/themes/page-cta.ts +18 -0
  292. package/packages/ui/src/themes/page-feature.ts +13 -0
  293. package/packages/ui/src/themes/page-header.ts +16 -0
  294. package/packages/ui/src/themes/page-hero.ts +15 -0
  295. package/packages/ui/src/themes/page-links.ts +12 -0
  296. package/packages/ui/src/themes/page-section.ts +14 -0
  297. package/packages/ui/src/themes/page.ts +7 -0
  298. package/packages/ui/src/themes/pagination.ts +17 -0
  299. package/packages/ui/src/themes/pin-input.ts +73 -0
  300. package/packages/ui/src/themes/popover.ts +30 -0
  301. package/packages/ui/src/themes/progress.ts +29 -0
  302. package/packages/ui/src/themes/prose.ts +45 -0
  303. package/packages/ui/src/themes/radio-group.ts +39 -0
  304. package/packages/ui/src/themes/scroll-area.ts +23 -0
  305. package/packages/ui/src/themes/select-menu.ts +71 -0
  306. package/packages/ui/src/themes/select.ts +72 -0
  307. package/packages/ui/src/themes/separator.ts +28 -0
  308. package/packages/ui/src/themes/sidebar.ts +127 -0
  309. package/packages/ui/src/themes/skeleton.ts +15 -0
  310. package/packages/ui/src/themes/slideover.ts +87 -0
  311. package/packages/ui/src/themes/slider.ts +48 -0
  312. package/packages/ui/src/themes/stepper.ts +41 -0
  313. package/packages/ui/src/themes/switch.ts +63 -0
  314. package/packages/ui/src/themes/table.ts +34 -0
  315. package/packages/ui/src/themes/tabs.ts +74 -0
  316. package/packages/ui/src/themes/textarea.ts +77 -0
  317. package/packages/ui/src/themes/timeline.ts +25 -0
  318. package/packages/ui/src/themes/toast.ts +29 -0
  319. package/packages/ui/src/themes/toaster.ts +66 -0
  320. package/packages/ui/src/themes/tooltip.ts +31 -0
  321. package/packages/ui/src/themes/tree.ts +41 -0
  322. package/packages/ui/src/themes/user.ts +28 -0
  323. package/styles.cdn.gen.css +2047 -0
  324. package/theme.gen.json +1 -0
@@ -0,0 +1,32 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+ <svg width="771" height="1628" version="1.1" viewBox="0 0 771 1628" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
4
+ <metadata>
5
+ <rdf:RDF>
6
+ <cc:Work rdf:about="">
7
+ <dc:format>image/svg+xml</dc:format>
8
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
9
+ <dc:title/>
10
+ </cc:Work>
11
+ </rdf:RDF>
12
+ </metadata>
13
+ <g transform="translate(0 207.28)">
14
+ <g>
15
+ <rect x="761" y="215.72" width="10" height="120" rx="5" ry="5" fill="#343434"/>
16
+ <rect x="761" y="405.72" width="10" height="220" rx="4.3333" ry="5" fill="#343434"/>
17
+ <rect x="6.25" y="-201.03" width="753.5" height="1615.5" rx="115" ry="115" fill="#333" stroke="#525252" stroke-linecap="round" stroke-linejoin="round" stroke-width="12.5"/>
18
+ </g>
19
+ <g fill="none" stroke="#000" stroke-opacity=".2" stroke-width="12.5">
20
+ <path d="m6.25 25.717v15"/>
21
+ <path d="m375.5-201.03h15"/>
22
+ <path d="m759.75 25.717v15"/>
23
+ <path d="m6.25 1250.6v15"/>
24
+ <path d="m759.75 1250.6v15"/>
25
+ <path d="m462.5 1414.5h15"/>
26
+ </g>
27
+ <rect x="31.25" y="-181.03" width="705" height="1568" rx="90" ry="90" fill="#ddd"/>
28
+ <circle cx="383" cy="-128.53" r="24" fill="#333"/>
29
+ <circle cx="383" cy="-128.53" r="15" fill="#ddf" fill-opacity=".13333"/>
30
+ <circle cx="383" cy="-128.53" r="15" fill="#08f" fill-opacity=".13333"/>
31
+ </g>
32
+ </svg>
@@ -0,0 +1,34 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+ <svg width="725" height="1528" version="1.1" viewBox="0 0 725 1528" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
4
+ <metadata>
5
+ <rdf:RDF>
6
+ <cc:Work rdf:about="">
7
+ <dc:format>image/svg+xml</dc:format>
8
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
9
+ <dc:title/>
10
+ </cc:Work>
11
+ </rdf:RDF>
12
+ </metadata>
13
+ <g transform="translate(0 107.28)">
14
+ <g fill="#969a98">
15
+ <rect x="715" y="308.22" width="10" height="120" rx="5" ry="5" opacity=".301"/>
16
+ <rect x="715" y="498.22" width="10" height="220" rx="4.3333" ry="5" opacity=".301"/>
17
+ <rect x="715" y="308.22" width="10" height="120" rx="5" ry="5"/>
18
+ <rect x="715" y="498.22" width="10" height="220" rx="4.3333" ry="5"/>
19
+ </g>
20
+ <rect x="6.25" y="-101.03" width="707.5" height="1515.5" rx="115" ry="115" fill="#333" stroke="#a4b4ac" stroke-linecap="round" stroke-linejoin="round" stroke-width="12.5"/>
21
+ <g fill="none" stroke="#000" stroke-opacity=".2" stroke-width="12.5">
22
+ <path d="m713.75 130.72v15"/>
23
+ <path d="m6.25 130.72v15"/>
24
+ <path d="m437.5 1414.5h15"/>
25
+ <path d="m352.5-101.03h15"/>
26
+ <path d="m713.75 1253.2v15"/>
27
+ <path d="m6.25 1253.2v15"/>
28
+ </g>
29
+ <rect x="33.75" y="-73.533" width="652.5" height="1460.5" rx="90" ry="90" fill="#ddd"/>
30
+ <circle cx="360" cy="-20.533" r="24" fill="#333"/>
31
+ <circle cx="360" cy="-20.533" r="15" fill="#ddf" fill-opacity=".13333"/>
32
+ <circle cx="360" cy="-20.533" r="15" fill="#08f" fill-opacity=".13333"/>
33
+ </g>
34
+ </svg>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg viewBox="0 0 800 1687" xmlns="http://www.w3.org/2000/svg" version="1.1" width="800px" height="1687px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
4
+ <g><path style="opacity:1" fill="#fd9ca8" d="M 127.5,-0.5 C 196.167,-0.5 264.833,-0.5 333.5,-0.5C 333.5,4.16667 333.5,8.83333 333.5,13.5C 261.166,13.3333 188.833,13.5 116.5,14C 55.5922,26.5748 21.4255,63.7415 14,125.5C 13.5,170.832 13.3333,216.165 13.5,261.5C 8.83333,261.5 4.16667,261.5 -0.5,261.5C -0.5,212.833 -0.5,164.167 -0.5,115.5C 8.51828,63.8189 37.1849,27.8189 85.5,7.5C 99.5389,3.96909 113.539,1.30243 127.5,-0.5 Z"/></g>
5
+ <g><path style="opacity:1" fill="#ca6374" d="M 333.5,-0.5 C 339.5,-0.5 345.5,-0.5 351.5,-0.5C 351.5,4.16667 351.5,8.83333 351.5,13.5C 345.5,13.5 339.5,13.5 333.5,13.5C 333.5,8.83333 333.5,4.16667 333.5,-0.5 Z"/></g>
6
+ <g><path style="opacity:1" fill="#fe9ca8" d="M 351.5,-0.5 C 381.167,-0.5 410.833,-0.5 440.5,-0.5C 440.5,4.16667 440.5,8.83333 440.5,13.5C 410.833,13.5 381.167,13.5 351.5,13.5C 351.5,8.83333 351.5,4.16667 351.5,-0.5 Z"/></g>
7
+ <g><path style="opacity:1" fill="#ca6474" d="M 440.5,-0.5 C 446.5,-0.5 452.5,-0.5 458.5,-0.5C 458.5,4.16667 458.5,8.83333 458.5,13.5C 452.5,13.5 446.5,13.5 440.5,13.5C 440.5,8.83333 440.5,4.16667 440.5,-0.5 Z"/></g>
8
+ <g><path style="opacity:1" fill="#fd9ca8" d="M 458.5,-0.5 C 532.833,-0.5 607.167,-0.5 681.5,-0.5C 727.094,8.56526 760.594,33.8986 782,75.5C 788.651,89.7881 792.651,104.788 794,120.5C 794.5,167.499 794.667,214.499 794.5,261.5C 789.833,261.5 785.167,261.5 780.5,261.5C 780.832,214.33 780.499,167.33 779.5,120.5C 771.352,66.5177 741.019,32.0177 688.5,17C 681.898,15.5664 675.232,14.5664 668.5,14C 598.501,13.5 528.501,13.3333 458.5,13.5C 458.5,8.83333 458.5,4.16667 458.5,-0.5 Z"/></g>
9
+ <g><path style="opacity:0.99" fill="#d48e97" d="M 111.5,-0.5 C 116.833,-0.5 122.167,-0.5 127.5,-0.5C 113.539,1.30243 99.5389,3.96909 85.5,7.5C 85.8807,6.06888 86.8807,5.23555 88.5,5C 96.2487,2.95516 103.915,1.12183 111.5,-0.5 Z"/></g>
10
+ <g><path style="opacity:1" fill="#343434" d="M 333.5,13.5 C 339.5,13.5 345.5,13.5 351.5,13.5C 381.167,13.5 410.833,13.5 440.5,13.5C 446.5,13.5 452.5,13.5 458.5,13.5C 528.501,13.3333 598.501,13.5 668.5,14C 675.232,14.5664 681.898,15.5664 688.5,17C 741.019,32.0177 771.352,66.5177 779.5,120.5C 779.5,580.5 779.5,1040.5 779.5,1500.5C 779.5,1521.83 779.5,1543.17 779.5,1564.5C 770.723,1620.11 739.056,1654.94 684.5,1669C 679.201,1669.93 673.867,1670.6 668.5,1671C 612.168,1671.5 555.834,1671.67 499.5,1671.5C 493.5,1671.5 487.5,1671.5 481.5,1671.5C 362.5,1671.67 243.5,1671.5 124.5,1671C 63.6174,1663.45 27.1174,1629.62 15,1569.5C 13.6594,1552.2 13.1594,1534.86 13.5,1517.5C 13.5,1511.83 13.5,1506.17 13.5,1500.5C 13.5,1093.17 13.5,685.833 13.5,278.5C 13.5,272.833 13.5,267.167 13.5,261.5C 13.3333,216.165 13.5,170.832 14,125.5C 21.4255,63.7415 55.5922,26.5748 116.5,14C 188.833,13.5 261.166,13.3333 333.5,13.5 Z"/></g>
11
+ <g><path style="opacity:1" fill="#dcdcdc" d="M 126.5,46.5 C 305.5,46.3333 484.5,46.5 663.5,47C 707.705,54.2042 735.871,79.3709 748,122.5C 748.93,127.799 749.597,133.133 750,138.5C 750.667,607.167 750.667,1075.83 750,1544.5C 746.203,1583.62 726.703,1611.78 691.5,1629C 682.783,1632.43 673.783,1634.76 664.5,1636C 485.5,1636.67 306.5,1636.67 127.5,1636C 88.6405,1630.15 61.8071,1608.98 47,1572.5C 44.7019,1565.31 43.0352,1557.97 42,1550.5C 41.3333,1077.83 41.3333,605.167 42,132.5C 47.9019,95.5124 67.7353,69.0124 101.5,53C 109.878,50.4758 118.211,48.3091 126.5,46.5 Z"/></g>
12
+ <g><path style="opacity:1" fill="#353535" d="M 390.5,75.5 C 406.443,74.3853 416.943,81.3853 422,96.5C 422.463,119.37 411.297,129.87 388.5,128C 370.641,119.786 365.474,106.619 373,88.5C 377.603,82.3812 383.437,78.0478 390.5,75.5 Z"/></g>
13
+ <g><path style="opacity:1" fill="#2c3d4d" d="M 391.5,85.5 C 401.823,84.4078 408.656,88.7411 412,98.5C 412.486,113.345 405.32,119.845 390.5,118C 379.683,112.216 376.85,103.716 382,92.5C 384.79,89.535 387.957,87.2016 391.5,85.5 Z"/></g>
14
+ <g><path style="opacity:1" fill="#c96273" d="M -0.5,261.5 C 4.16667,261.5 8.83333,261.5 13.5,261.5C 13.5,267.167 13.5,272.833 13.5,278.5C 8.83333,278.5 4.16667,278.5 -0.5,278.5C -0.5,272.833 -0.5,267.167 -0.5,261.5 Z"/></g>
15
+ <g><path style="opacity:1" fill="#cc7280" d="M 779.5,120.5 C 780.499,167.33 780.832,214.33 780.5,261.5C 785.167,261.5 789.833,261.5 794.5,261.5C 794.5,267.167 794.5,272.833 794.5,278.5C 794.167,278.5 793.833,278.5 793.5,278.5C 789.167,278.5 784.833,278.5 780.5,278.5C 780.5,685.833 780.5,1093.17 780.5,1500.5C 780.167,1500.5 779.833,1500.5 779.5,1500.5C 779.5,1040.5 779.5,580.5 779.5,120.5 Z"/></g>
16
+ <g><path style="opacity:1" fill="#fd9ba7" d="M -0.5,278.5 C 4.16667,278.5 8.83333,278.5 13.5,278.5C 13.5,685.833 13.5,1093.17 13.5,1500.5C 8.83333,1500.5 4.16667,1500.5 -0.5,1500.5C -0.5,1093.17 -0.5,685.833 -0.5,278.5 Z"/></g>
17
+ <g><path style="opacity:1" fill="#fe9da8" d="M 793.5,278.5 C 793.5,685.833 793.5,1093.17 793.5,1500.5C 789.167,1500.5 784.833,1500.5 780.5,1500.5C 780.5,1093.17 780.5,685.833 780.5,278.5C 784.833,278.5 789.167,278.5 793.5,278.5 Z"/></g>
18
+ <g><path style="opacity:1" fill="#fb97a5" d="M 793.5,278.5 C 793.833,278.5 794.167,278.5 794.5,278.5C 794.5,338.167 794.5,397.833 794.5,457.5C 796.514,457.392 798.18,458.058 799.5,459.5C 799.5,502.5 799.5,545.5 799.5,588.5C 798.18,589.942 796.514,590.608 794.5,590.5C 794.5,616.167 794.5,641.833 794.5,667.5C 796.292,667.366 797.958,667.699 799.5,668.5C 799.5,669.5 799.5,670.5 799.5,671.5C 798.626,669.31 796.959,668.31 794.5,668.5C 794.5,748.833 794.5,829.167 794.5,909.5C 795.833,909.5 797.167,909.5 798.5,909.5C 797.432,910.434 796.099,910.768 794.5,910.5C 794.5,1107.17 794.5,1303.83 794.5,1500.5C 794.167,1500.5 793.833,1500.5 793.5,1500.5C 793.5,1093.17 793.5,685.833 793.5,278.5 Z"/></g>
19
+ <g><path style="opacity:1" fill="#fe9ca8" d="M 799.5,671.5 C 799.5,750.833 799.5,830.167 799.5,909.5C 799.167,909.5 798.833,909.5 798.5,909.5C 797.167,909.5 795.833,909.5 794.5,909.5C 794.5,829.167 794.5,748.833 794.5,668.5C 796.959,668.31 798.626,669.31 799.5,671.5 Z"/></g>
20
+ <g><path style="opacity:1" fill="#c96273" d="M -0.5,1500.5 C 4.16667,1500.5 8.83333,1500.5 13.5,1500.5C 13.5,1506.17 13.5,1511.83 13.5,1517.5C 8.83333,1517.5 4.16667,1517.5 -0.5,1517.5C -0.5,1511.83 -0.5,1506.17 -0.5,1500.5 Z"/></g>
21
+ <g><path style="opacity:1" fill="#c96676" d="M 779.5,1500.5 C 779.833,1500.5 780.167,1500.5 780.5,1500.5C 784.833,1500.5 789.167,1500.5 793.5,1500.5C 793.833,1500.5 794.167,1500.5 794.5,1500.5C 794.5,1506.17 794.5,1511.83 794.5,1517.5C 789.833,1517.5 785.167,1517.5 780.5,1517.5C 780.83,1533.34 780.496,1549.01 779.5,1564.5C 779.5,1543.17 779.5,1521.83 779.5,1500.5 Z"/></g>
22
+ <g><path style="opacity:1" fill="#fd9ba7" d="M -0.5,1517.5 C 4.16667,1517.5 8.83333,1517.5 13.5,1517.5C 13.1594,1534.86 13.6594,1552.2 15,1569.5C 27.1174,1629.62 63.6174,1663.45 124.5,1671C 243.5,1671.5 362.5,1671.67 481.5,1671.5C 481.5,1676.5 481.5,1681.5 481.5,1686.5C 363.833,1686.5 246.167,1686.5 128.5,1686.5C 66.1785,1681.02 24.6785,1648.69 4,1589.5C 2.37825,1582.72 0.878249,1576.06 -0.5,1569.5C -0.5,1552.17 -0.5,1534.83 -0.5,1517.5 Z"/></g>
23
+ <g><path style="opacity:1" fill="#fd9ba7" d="M 794.5,1517.5 C 794.667,1533.17 794.5,1548.84 794,1564.5C 786.458,1621.7 755.625,1660.2 701.5,1680C 689.57,1683.38 677.57,1685.54 665.5,1686.5C 610.167,1686.5 554.833,1686.5 499.5,1686.5C 499.5,1681.5 499.5,1676.5 499.5,1671.5C 555.834,1671.67 612.168,1671.5 668.5,1671C 673.867,1670.6 679.201,1669.93 684.5,1669C 739.056,1654.94 770.723,1620.11 779.5,1564.5C 780.496,1549.01 780.83,1533.34 780.5,1517.5C 785.167,1517.5 789.833,1517.5 794.5,1517.5 Z"/></g>
24
+ <g><path style="opacity:1" fill="#c86273" d="M 481.5,1671.5 C 487.5,1671.5 493.5,1671.5 499.5,1671.5C 499.5,1676.5 499.5,1681.5 499.5,1686.5C 493.5,1686.5 487.5,1686.5 481.5,1686.5C 481.5,1681.5 481.5,1676.5 481.5,1671.5 Z"/></g>
25
+ </svg>
@@ -0,0 +1,29 @@
1
+ // Device art lives INSIDE the package, not on a consumer's public path.
2
+ //
3
+ // The registry used to name each render as `/devices/<id>.svg`, an absolute URL that only resolved
4
+ // because the docs app happened to serve `public/devices/`. A package cannot make that promise: the
5
+ // moment a second app consumed the frame it would render a broken `<img>` until someone copied 784KB
6
+ // of SVG into the right directory. So the art ships here and the bundler resolves it — `?url` emits
7
+ // each file as an asset and hands back its hashed, base-aware URL, which works under any base path,
8
+ // any dev server, and any deploy.
9
+ //
10
+ // Eager, not lazy: the glob is 25 URL strings (not the SVG bodies), so resolving them up front costs
11
+ // nothing and keeps `artUrl` synchronous — a computed in the frame's template cannot await.
12
+ import { DEVICES, type DeviceModel } from './devices';
13
+
14
+ const ART = import.meta.glob<string>('./art/*.svg', {
15
+ eager: true,
16
+ query: '?url',
17
+ import: 'default',
18
+ });
19
+
20
+ /** Bundled URL for a device's hardware render. */
21
+ export function artUrl(device: DeviceModel): string {
22
+ return ART[`./art/${device.art.file}`] ?? '';
23
+ }
24
+
25
+ /**
26
+ * Attribution for the bundled renders — CC BY-SA 4.0 REQUIRES this to be displayed wherever the art
27
+ * is. Deduped and sorted, so a page can print the whole list without knowing which devices it drew.
28
+ */
29
+ export const ART_CREDITS: string[] = [...new Set(DEVICES.map((d) => d.art.credit))].sort();
@@ -0,0 +1,337 @@
1
+ // Device-shell registry — every entry is PHOTOREAL: an unmodified Wikimedia Commons vector render is
2
+ // the hardware, the live screen is sized to the model's TRUE logical points (CSS px, Apple/GSMArena
3
+ // spec — verified, NOT screensizechecker) and clipped to the SVG's own screen aperture.
4
+ //
5
+ // Per device:
6
+ // • screen — logical points (what a browser viewport reports). The shell sizes the live screen to this.
7
+ // • art.file — the SVG's own name inside `./art/`, used AS-IS → attribution only (art.credit).
8
+ // A bare filename, NOT a URL: the art is bundled with this package and resolved by
9
+ // `artUrl()` (see art.ts), so a consumer needs nothing served at a public path.
10
+ // • art.inset— screen-area insets as % of the SVG viewBox (measured in-browser via getBoundingClientRect).
11
+ // • art.clip — the screen outline normalised 0..1 over its own bbox, applied as an objectBoundingBox
12
+ // clip-path so corners match the SVG exactly (Apple squircle). Absent → use radiusPct
13
+ // (Pixel screens are plain rounded rects; the SE LCD has square corners).
14
+ //
15
+ // Frames discovered + specced by a parallel research workflow; sizes cross-checked on ios-resolution /
16
+ // useyourloaf / GSMArena. Devices with no Commons SVG (iPads, most Samsung/OnePlus/Nothing, Zebra
17
+ // rugged) were skipped — they have no vector render on Commons.
18
+
19
+ export type Cutout = 'island' | 'notch' | 'punch-hole' | 'home' | 'none';
20
+
21
+ export interface DeviceModel {
22
+ id: string;
23
+ brand: string;
24
+ name: string;
25
+ /** Logical points (CSS px) — the true-to-spec screen size. */
26
+ screen: { w: number; h: number };
27
+ scaleFactor: number;
28
+ diagonalIn: number;
29
+ cutout: Cutout;
30
+ /** Simulated safe-area insets (pt) — status-bar / home-indicator clearance. */
31
+ safe: { top: number; bottom: number };
32
+ art: {
33
+ /** Filename inside `./art/` — resolve to a URL with `artUrl()`, never render it directly. */
34
+ file: string;
35
+ /** SVG height ÷ width (viewBox aspect). */
36
+ aspect: number;
37
+ /** Screen-area insets as % of the SVG viewBox. */
38
+ inset: { top: number; right: number; bottom: number; left: number };
39
+ /** Screen outline normalised 0..1 over its bbox (objectBoundingBox clip). */
40
+ clip?: string;
41
+ /** Fallback screen corner radius as % of screen width (when no clip). */
42
+ radiusPct?: number;
43
+ /** Attribution — MUST be displayed (CC BY-SA 4.0). */
44
+ credit: string;
45
+ };
46
+ }
47
+
48
+ const GOLDENBOX = 'Rafael Fernandez · CC BY-SA 4.0';
49
+ const MLIU = 'Mliu92 · CC BY-SA 4.0';
50
+ const DM115 = 'DM115Wiki · CC BY-SA 4.0';
51
+
52
+ // Canonical Apple squircle (extracted from the iPhone 17 Pro Max screen path) — reused for iPhone
53
+ // models whose SVG rounds the screen via a clipPath rather than an addressable path.
54
+ const APPLE_SQUIRCLE =
55
+ 'M 0.14184 0 C 0.06268 0 0 0.02898 0 0.06559 L 0 0.93441 C 0 0.97102 0.06268 1 0.14184 1 L 0.85816 1 C 0.93732 1 1 0.97102 1 0.93441 L 1 0.06559 C 1 0.02898 0.93732 0 0.85816 0 Z';
56
+
57
+ export const DEVICES: DeviceModel[] = [
58
+ {
59
+ id: 'iphone-se-3',
60
+ brand: 'Apple',
61
+ name: 'iPhone SE (3rd gen)',
62
+ screen: { w: 375, h: 667 },
63
+ scaleFactor: 2,
64
+ diagonalIn: 4.7,
65
+ cutout: 'home',
66
+ safe: { top: 20, bottom: 0 },
67
+ art: {
68
+ file: 'iphone-se-3.svg',
69
+ aspect: 2.00101,
70
+ inset: { top: 11.434, right: 6.411, bottom: 11.429, left: 6.482 },
71
+ radiusPct: 0, // SE LCD has square screen corners
72
+ credit: `Hajoon0102 · CC BY-SA 4.0`,
73
+ },
74
+ },
75
+ {
76
+ id: 'iphone-11',
77
+ brand: 'Apple',
78
+ name: 'iPhone 11',
79
+ screen: { w: 414, h: 896 },
80
+ scaleFactor: 2,
81
+ diagonalIn: 6.1,
82
+ cutout: 'notch',
83
+ safe: { top: 48, bottom: 34 },
84
+ art: {
85
+ file: 'iphone-11.svg',
86
+ aspect: 2.00556,
87
+ inset: { top: 3.075, right: 6.5853, bottom: 3.1107, left: 6.5853 },
88
+ // the SVG's own screen path (arcs converted to béziers) — smooth corners + notch
89
+ clip: 'M 0.1026 0.0 C 0.04576 0.0 0.0 0.02112 0.0 0.04735 L 0.0 0.95265 C 0.0 0.97888 0.04576 1.0 0.1026 1.0 L 0.8974 1.0 C 0.95424 1.0 1.0 0.97888 1.0 0.95265 L 1.0 0.04735 C 1.0 0.02112 0.95424 0.0 0.8974 0.0 L 0.79703 0.0 C 0.78757 0.0 0.77989 0.00354 0.77989 0.00791 L 0.77989 0.01155 C 0.77989 0.0258 0.75503 0.03728 0.72415 0.03728 L 0.27585 0.03728 C 0.24497 0.03728 0.22011 0.0258 0.22011 0.01155 L 0.22011 0.00791 C 0.22011 0.00354 0.21243 0.0 0.20297 0.0 Z',
90
+ credit: GOLDENBOX,
91
+ },
92
+ },
93
+ {
94
+ id: 'iphone-14',
95
+ brand: 'Apple',
96
+ name: 'iPhone 14',
97
+ screen: { w: 390, h: 844 },
98
+ scaleFactor: 3,
99
+ diagonalIn: 6.1,
100
+ cutout: 'notch',
101
+ safe: { top: 47, bottom: 34 },
102
+ art: {
103
+ file: 'iphone-14.svg',
104
+ aspect: 2.01102,
105
+ inset: { top: 2.3242, right: 5.7243, bottom: 2.3242, left: 5.7243 },
106
+ // the SVG's own screen path (béziers, notch included) — smooth corners, no hand-rolled overlay
107
+ clip: 'M 0.1185 0.0 C 0.05306 0 0.0 0.0245 0.0 0.05472 L 0.0 0.94528 C 0 0.9755 0.05305 1.0 0.1185 1.0 L 0.5 1.0 L 0.8815 1.0 C 0.94695 1.0 1.0 0.9755 1.0 0.94528 L 1.0 0.05472 C 1.0 0.0245 0.94694 0 0.8815 0.0 L 0.72939 0.0 C 0.71554 0.0 0.70838 0.00066 0.70838 0.01081 C 0.70838 0.02095 0.69476 0.03804 0.64605 0.03804 L 0.61135 0.03804 L 0.38864 0.03804 L 0.35395 0.03804 C 0.30524 0.03804 0.29162 0.02095 0.29162 0.01081 C 0.29162 0.00066 0.28446 0.0 0.27061 0.0 Z',
108
+ credit: GOLDENBOX,
109
+ },
110
+ },
111
+ {
112
+ id: 'iphone-15',
113
+ brand: 'Apple',
114
+ name: 'iPhone 15',
115
+ screen: { w: 393, h: 852 },
116
+ scaleFactor: 3,
117
+ diagonalIn: 6.1,
118
+ cutout: 'island',
119
+ safe: { top: 59, bottom: 34 },
120
+ art: {
121
+ file: 'iphone-15.svg',
122
+ aspect: 2.02778,
123
+ inset: { top: 2.245, right: 5.29, bottom: 2.245, left: 5.29 },
124
+ clip: APPLE_SQUIRCLE,
125
+ credit: GOLDENBOX,
126
+ },
127
+ },
128
+ {
129
+ id: 'iphone-16-pro',
130
+ brand: 'Apple',
131
+ name: 'iPhone 16 Pro',
132
+ screen: { w: 402, h: 874 },
133
+ scaleFactor: 3,
134
+ diagonalIn: 6.3,
135
+ cutout: 'island',
136
+ safe: { top: 59, bottom: 34 },
137
+ art: {
138
+ file: 'iphone-16-pro.svg',
139
+ aspect: 2.05056,
140
+ inset: { top: 1.62, right: 4.373, bottom: 1.62, left: 4.373 },
141
+ clip: APPLE_SQUIRCLE,
142
+ credit: GOLDENBOX,
143
+ },
144
+ },
145
+ {
146
+ id: 'iphone-air',
147
+ brand: 'Apple',
148
+ name: 'iPhone Air',
149
+ screen: { w: 420, h: 912 },
150
+ scaleFactor: 3,
151
+ diagonalIn: 6.5,
152
+ cutout: 'island',
153
+ safe: { top: 59, bottom: 34 },
154
+ art: {
155
+ file: 'iphone-air.svg',
156
+ aspect: 2.06077,
157
+ inset: { top: 1.763, right: 4.42, bottom: 1.763, left: 4.42 },
158
+ clip: APPLE_SQUIRCLE,
159
+ credit: GOLDENBOX,
160
+ },
161
+ },
162
+ {
163
+ id: 'iphone-17-pro-max',
164
+ brand: 'Apple',
165
+ name: 'iPhone 17 Pro Max',
166
+ screen: { w: 440, h: 956 },
167
+ scaleFactor: 3,
168
+ diagonalIn: 6.9,
169
+ cutout: 'island',
170
+ safe: { top: 62, bottom: 34 },
171
+ art: {
172
+ file: 'iphone-17-pro-max.svg',
173
+ aspect: 2.06439,
174
+ inset: { top: 1.508, right: 3.711, bottom: 1.508, left: 3.71 },
175
+ clip: APPLE_SQUIRCLE,
176
+ credit: GOLDENBOX,
177
+ },
178
+ },
179
+ {
180
+ id: 'pixel-8',
181
+ brand: 'Google',
182
+ name: 'Pixel 8',
183
+ screen: { w: 412, h: 915 },
184
+ scaleFactor: 2.625,
185
+ diagonalIn: 6.2,
186
+ cutout: 'punch-hole',
187
+ safe: { top: 28, bottom: 24 },
188
+ art: {
189
+ // cropped from the dual "Pixel 8 and 8 Pro front" SVG (left phone) via viewBox
190
+ file: 'pixel-8.svg',
191
+ aspect: 2.12739,
192
+ inset: { top: 2.1926, right: 4.9207, bottom: 2.3207, left: 4.373 },
193
+ clip: 'M 0.9028 0.0 L 0.0972 0.0 C 0.04352 0.0 0.0 0.01943 0.0 0.0434 L 0.0 0.9566 C 0.0 0.98057 0.04352 1.0 0.0972 1.0 L 0.9028 1.0 C 0.95648 1.0 1.0 0.98057 1.0 0.9566 L 1.0 0.0434 C 1.0 0.01943 0.95648 0.0 0.9028 0.0 Z',
194
+ credit: MLIU,
195
+ },
196
+ },
197
+ {
198
+ id: 'pixel-8-pro',
199
+ brand: 'Google',
200
+ name: 'Pixel 8 Pro',
201
+ screen: { w: 448, h: 998 },
202
+ scaleFactor: 3,
203
+ diagonalIn: 6.7,
204
+ cutout: 'punch-hole',
205
+ safe: { top: 28, bottom: 24 },
206
+ art: {
207
+ // cropped from the dual "Pixel 8 and 8 Pro front" SVG (right phone) via viewBox
208
+ file: 'pixel-8-pro.svg',
209
+ aspect: 2.11437,
210
+ inset: { top: 1.8568, right: 4.6413, bottom: 1.7515, left: 4.3672 },
211
+ clip: 'M 0.08243 0.0 L 0.91757 0.0 C 0.96309 0.0 1.0 0.01648 1.0 0.0368 L 1.0 0.9632 C 1.0 0.98352 0.96309 1.0 0.91757 1.0 L 0.08243 1.0 C 0.03691 1.0 0.0 0.98352 0.0 0.9632 L 0.0 0.0368 C 0.0 0.01648 0.03691 0.0 0.08243 0.0 Z',
212
+ credit: MLIU,
213
+ },
214
+ },
215
+ {
216
+ id: 'pixel-9-pro',
217
+ brand: 'Google',
218
+ name: 'Pixel 9 Pro',
219
+ screen: { w: 427, h: 952 },
220
+ scaleFactor: 3,
221
+ diagonalIn: 6.3,
222
+ cutout: 'punch-hole',
223
+ safe: { top: 28, bottom: 24 },
224
+ art: {
225
+ file: 'pixel-9-pro.svg',
226
+ aspect: 2.10759,
227
+ inset: { top: 2.209, right: 5.345, bottom: 2.209, left: 4.655 },
228
+ radiusPct: 13.793,
229
+ credit: MLIU,
230
+ },
231
+ },
232
+
233
+ // ── added: more iPhones ──
234
+ {
235
+ id: 'iphone-x', brand: 'Apple', name: 'iPhone X',
236
+ screen: { w: 375, h: 812 }, scaleFactor: 3, diagonalIn: 5.8, cutout: 'notch', safe: { top: 44, bottom: 34 },
237
+ art: { file: 'iphone-x.svg', aspect: 2.00556, inset: { top: 3.075, right: 6.5853, bottom: 3.1107, left: 6.5853 },
238
+ clip: 'M 0.1026 0.0 C 0.04576 0.0 0.0 0.02112 0.0 0.04735 L 0.0 0.95265 C 0.0 0.97888 0.04576 1.0 0.1026 1.0 L 0.8974 1.0 C 0.95424 1.0 1.0 0.97888 1.0 0.95265 L 1.0 0.04735 C 1.0 0.02112 0.95424 0.0 0.8974 0.0 L 0.79703 0.0 C 0.78757 0.0 0.77989 0.00354 0.77989 0.00791 L 0.77989 0.01155 C 0.77989 0.0258 0.75503 0.03728 0.72415 0.03728 L 0.27585 0.03728 C 0.24497 0.03728 0.22011 0.0258 0.22011 0.01155 L 0.22011 0.00791 C 0.22011 0.00354 0.21243 0.0 0.20297 0.0 L 0.1026 0.0 Z',
239
+ credit: GOLDENBOX } },
240
+ {
241
+ id: 'iphone-12', brand: 'Apple', name: 'iPhone 12',
242
+ screen: { w: 390, h: 844 }, scaleFactor: 3, diagonalIn: 6.1, cutout: 'notch', safe: { top: 47, bottom: 34 },
243
+ art: { file: 'iphone-12.svg', aspect: 2.01102, inset: { top: 2.3241, right: 5.7243, bottom: 2.3241, left: 5.7243 },
244
+ clip: 'M 0.1185 0.0 C 0.05306 0 0.0 0.0245 0.0 0.05472 L 0.0 0.94528 C 0 0.9755 0.05305 1.0 0.1185 1.0 L 0.8815 1.0 C 0.94695 1.0 1.0 0.9755 1.0 0.94528 L 1.0 0.05472 C 1.0 0.0245 0.94694 0 0.8815 0.0 L 0.79107 0.0 C 0.77722 0.0 0.77006 0.00066 0.77006 0.01081 C 0.77006 0.02095 0.75645 0.03804 0.70774 0.03804 L 0.29226 0.03804 C 0.24355 0.03804 0.22994 0.02095 0.22994 0.01081 C 0.22994 0.00066 0.22278 0.0 0.20893 0.0 Z',
245
+ credit: GOLDENBOX } },
246
+ {
247
+ id: 'iphone-13', brand: 'Apple', name: 'iPhone 13',
248
+ screen: { w: 390, h: 844 }, scaleFactor: 3, diagonalIn: 6.1, cutout: 'notch', safe: { top: 47, bottom: 34 },
249
+ art: { file: 'iphone-13.svg', aspect: 2.01102, inset: { top: 2.3242, right: 5.7243, bottom: 2.3242, left: 5.7243 },
250
+ clip: 'M 0.1185 0.0 C 0.05306 0 0.0 0.0245 0.0 0.05472 L 0.0 0.94528 C 0 0.9755 0.05305 1.0 0.1185 1.0 L 0.5 1.0 L 0.8815 1.0 C 0.94695 1.0 1.0 0.9755 1.0 0.94528 L 1.0 0.05472 C 1.0 0.0245 0.94694 0 0.8815 0.0 L 0.72939 0.0 C 0.71554 0.0 0.70838 0.00066 0.70838 0.01081 C 0.70838 0.02095 0.69476 0.03804 0.64605 0.03804 L 0.61135 0.03804 L 0.38864 0.03804 L 0.35395 0.03804 C 0.30524 0.03804 0.29162 0.02095 0.29162 0.01081 C 0.29162 0.00066 0.28446 0.0 0.27061 0.0 Z',
251
+ credit: GOLDENBOX } },
252
+ {
253
+ id: 'iphone-13-pro', brand: 'Apple', name: 'iPhone 13 Pro',
254
+ screen: { w: 390, h: 844 }, scaleFactor: 3, diagonalIn: 6.1, cutout: 'notch', safe: { top: 47, bottom: 34 },
255
+ art: { file: 'iphone-13-pro.svg', aspect: 2.02216, inset: { top: 2.3242, right: 5.4666, bottom: 2.3242, left: 5.4915 },
256
+ clip: 'M 0.1185 0.0 C 0.05306 0 0.0 0.0245 0.0 0.05472 L 0.0 0.94528 C 0 0.9755 0.05305 1.0 0.1185 1.0 L 0.5 1.0 L 0.8815 1.0 C 0.94695 1.0 1.0 0.9755 1.0 0.94528 L 1.0 0.05472 C 1.0 0.0245 0.94694 0 0.8815 0.0 L 0.72953 0.0 C 0.71568 0.0 0.70852 0.00066 0.70852 0.01081 C 0.70852 0.02095 0.69491 0.03804 0.6462 0.03804 L 0.3538 0.03804 C 0.30509 0.03804 0.29148 0.02095 0.29148 0.01081 C 0.29148 0.00066 0.28432 0.0 0.27047 0.0 Z',
257
+ credit: GOLDENBOX } },
258
+ {
259
+ id: 'iphone-15-pro', brand: 'Apple', name: 'iPhone 15 Pro',
260
+ screen: { w: 393, h: 852 }, scaleFactor: 3, diagonalIn: 6.1, cutout: 'island', safe: { top: 59, bottom: 34 },
261
+ art: { file: 'iphone-15-pro.svg', aspect: 2.05056, inset: { top: 1.97, right: 4.373, bottom: 1.97, left: 4.373 },
262
+ clip: APPLE_SQUIRCLE, credit: GOLDENBOX } },
263
+ {
264
+ id: 'iphone-16', brand: 'Apple', name: 'iPhone 16',
265
+ screen: { w: 393, h: 852 }, scaleFactor: 3, diagonalIn: 6.1, cutout: 'island', safe: { top: 59, bottom: 34 },
266
+ art: { file: 'iphone-16.svg', aspect: 2.02778, inset: { top: 2.245, right: 5.29, bottom: 2.245, left: 5.29 },
267
+ clip: APPLE_SQUIRCLE, credit: GOLDENBOX } },
268
+ {
269
+ id: 'iphone-16-plus', brand: 'Apple', name: 'iPhone 16 Plus',
270
+ screen: { w: 430, h: 932 }, scaleFactor: 3, diagonalIn: 6.7, cutout: 'island', safe: { top: 59, bottom: 34 },
271
+ art: { file: 'iphone-16-plus.svg', aspect: 2.04103, inset: { top: 2.0185, right: 4.7627, bottom: 2.0185, left: 4.7627 },
272
+ clip: 'M 0.13026 0.0 L 0.86974 0.0 C 0.9419 0.0 1.0 0.02684 1.0 0.06017 L 1.0 0.93983 C 1.0 0.97316 0.9419 1.0 0.86974 1.0 L 0.13026 1.0 C 0.0581 1.0 0.0 0.97316 0.0 0.93983 L 0.0 0.06017 C 0.0 0.02684 0.0581 0.0 0.13026 0.0 Z',
273
+ credit: GOLDENBOX } },
274
+ {
275
+ id: 'iphone-16-pro-max', brand: 'Apple', name: 'iPhone 16 Pro Max',
276
+ screen: { w: 440, h: 956 }, scaleFactor: 3, diagonalIn: 6.9, cutout: 'island', safe: { top: 62, bottom: 34 },
277
+ art: { file: 'iphone-16-pro-max.svg', aspect: 2.05641, inset: { top: 1.6257, right: 3.8819, bottom: 1.6257, left: 3.8819 },
278
+ clip: 'M 0.13021 0.0 L 0.86979 0.0 C 0.94192 0.0 1.0 0.02692 1.0 0.06037 L 1.0 0.93963 C 1.0 0.97308 0.94192 1.0 0.86979 1.0 L 0.13021 1.0 C 0.05808 1.0 0.0 0.97308 0.0 0.93963 L 0.0 0.06037 C 0.0 0.02692 0.05808 0.0 0.13021 0.0 Z',
279
+ credit: GOLDENBOX } },
280
+ {
281
+ id: 'iphone-17', brand: 'Apple', name: 'iPhone 17',
282
+ screen: { w: 402, h: 874 }, scaleFactor: 3, diagonalIn: 6.3, cutout: 'island', safe: { top: 59, bottom: 34 },
283
+ art: { file: 'iphone-17.svg', aspect: 2.06897, inset: { top: 1.6433, right: 4.0166, bottom: 1.6433, left: 4.0172 },
284
+ clip: 'M 0.15599 0.0 C 0.06893 0.0 0.0 0.03168 0.0 0.07169 L 0.0 0.92831 C 0.0 0.96832 0.06893 1.0 0.15599 1.0 L 0.84401 1.0 C 0.93107 1.0 1.0 0.96832 1.0 0.92831 L 1.0 0.07169 C 1.0 0.03168 0.93107 0.0 0.84401 0.0 Z',
285
+ credit: GOLDENBOX } },
286
+
287
+ // ── added: more Pixels (a-series, 9 Pro XL, 10 family) ──
288
+ {
289
+ id: 'pixel-8a', brand: 'Google', name: 'Pixel 8a',
290
+ screen: { w: 412, h: 916 }, scaleFactor: 2.625, diagonalIn: 6.1, cutout: 'punch-hole', safe: { top: 28, bottom: 24 },
291
+ art: { file: 'pixel-8a.svg', aspect: 2.12625, inset: { top: 2.0245, right: 5.6875, bottom: 3.6154, left: 4.8125 },
292
+ clip: 'M 0.07961 4e-05 C 0.3594 -6e-05 0.6392 4e-05 0.91899 0.00035 C 0.9663 0.00526 0.99307 0.01844 0.9993 0.03991 C 1.00023 0.34665 1.00023 0.65339 0.9993 0.96012 C 0.99307 0.98159 0.9663 0.99478 0.91899 0.99969 C 0.63966 1.0001 0.36034 1.0001 0.08101 0.99969 C 0.03748 0.99522 0.01071 0.98328 0.0007 0.96386 C -0.00023 0.65464 -0.00023 0.3454 0.0007 0.03617 C 0.01082 0.01702 0.03712 0.00497 0.07961 4e-05 Z',
293
+ credit: DM115 } },
294
+ {
295
+ id: 'pixel-9a', brand: 'Google', name: 'Pixel 9a',
296
+ screen: { w: 412, h: 923 }, scaleFactor: 2.625, diagonalIn: 6.3, cutout: 'punch-hole', safe: { top: 28, bottom: 24 },
297
+ art: { file: 'pixel-9a.svg', aspect: 2.10875, inset: { top: 2.7527, right: 6.1875, bottom: 2.9933, left: 5.1875 },
298
+ clip: 'M 0.11989 4e-05 C 0.37236 -7e-05 0.62482 4e-05 0.87729 0.00035 C 0.93964 0.00488 0.97937 0.02071 0.99647 0.04784 C 0.99779 0.05117 0.99873 0.05452 0.99929 0.0579 C 1.00024 0.35265 1.00024 0.64739 0.99929 0.94214 C 0.99394 0.96674 0.96644 0.98445 0.91678 0.99528 C 0.90449 0.99744 0.8918 0.9989 0.8787 0.99968 C 0.62623 1.00011 0.37377 1.00011 0.1213 0.99968 C 0.06649 0.996 0.02864 0.98269 0.00776 0.95975 C 0.00452 0.95523 0.00217 0.95061 0.00071 0.94591 C -0.00024 0.64865 -0.00024 0.35139 0.00071 0.05413 C 0.00903 0.03086 0.037 0.0142 0.08463 0.00413 C 0.09644 0.00254 0.1082 0.00118 0.11989 4e-05 Z',
299
+ credit: DM115 } },
300
+ {
301
+ id: 'pixel-9-pro-xl', brand: 'Google', name: 'Pixel 9 Pro XL',
302
+ screen: { w: 384, h: 854 }, scaleFactor: 3.5, diagonalIn: 6.8, cutout: 'punch-hole', safe: { top: 28, bottom: 24 },
303
+ art: { file: 'pixel-9-pro-xl.svg', aspect: 2.11154, inset: { top: 1.612, right: 4.507, bottom: 2.073, left: 4.053 },
304
+ radiusPct: 12.77, credit: MLIU } },
305
+ {
306
+ id: 'pixel-10', brand: 'Google', name: 'Pixel 10',
307
+ screen: { w: 411, h: 923 }, scaleFactor: 2.625, diagonalIn: 6.3, cutout: 'punch-hole', safe: { top: 28, bottom: 24 },
308
+ art: { file: 'pixel-10.svg', aspect: 2.10927, inset: { top: 2.033, right: 4.795, bottom: 1.902, left: 4.611 },
309
+ radiusPct: 14.12, credit: MLIU } },
310
+ {
311
+ id: 'pixel-10-pro', brand: 'Google', name: 'Pixel 10 Pro',
312
+ screen: { w: 427, h: 952 }, scaleFactor: 3, diagonalIn: 6.3, cutout: 'punch-hole', safe: { top: 28, bottom: 24 },
313
+ art: { file: 'pixel-10-pro.svg', aspect: 2.10927, inset: { top: 1.967, right: 5.083, bottom: 1.967, left: 4.323 },
314
+ radiusPct: 14.12, credit: MLIU } },
315
+ {
316
+ id: 'pixel-10-pro-xl', brand: 'Google', name: 'Pixel 10 Pro XL',
317
+ screen: { w: 448, h: 997 }, scaleFactor: 3, diagonalIn: 6.8, cutout: 'punch-hole', safe: { top: 28, bottom: 24 },
318
+ art: { file: 'pixel-10-pro-xl.svg', aspect: 2.10691, inset: { top: 1.733, right: 4.238, bottom: 1.702, left: 3.586 },
319
+ radiusPct: 14.5, credit: MLIU } },
320
+ ];
321
+
322
+ export const DEFAULT_DEVICE = 'iphone-17-pro-max';
323
+
324
+ /**
325
+ * ⚠ THE FALLBACK IS THE NAMED DEFAULT, NOT THE LAST ROW. It was `DEVICES[DEVICES.length - 1]`, which
326
+ * made "unknown id" mean "whatever happens to sit at the end of the array" — a Pixel, after the
327
+ * Pixels were appended. Measured in the deck: three frames asked for `iphone-17-pro` (no such id —
328
+ * the Pro Max is the one that exists) and rendered three Pixel 10 Pro XLs, punch-hole and all, with
329
+ * nothing anywhere saying so. Jose caught it as "why do the devices have no notch?".
330
+ *
331
+ * It still falls back rather than throwing, because a frame with no phone is worse than a frame with
332
+ * the wrong one — but a caller that cares (scripts/deckSpecimen.ts does) compares the id it asked for
333
+ * against the one it got.
334
+ */
335
+ export function findDevice(id: string): DeviceModel {
336
+ return DEVICES.find((d) => d.id === id) ?? DEVICES.find((d) => d.id === DEFAULT_DEVICE) ?? DEVICES[0];
337
+ }
@@ -0,0 +1,15 @@
1
+ // Presentation frames — the hardware a prototype is staged inside, not part of the brand surface.
2
+ //
3
+ // Nothing here renders in a shipped product: a rider app runs on a real phone and a console runs in a
4
+ // real browser, so neither ever draws a bezel or a traffic light. These exist so a specimen can be
5
+ // shown at TRUE scale — a 402pt screen laid out at 402pt, a console laid out at its real desktop
6
+ // width — which is the only way a reflow decision can be judged before it ships.
7
+ //
8
+ // They are still F-prefixed and still a package rather than app-local files: docs and the playground
9
+ // both stage prototypes, and the two copies that predated this package had already drifted apart.
10
+ export { default as FDeviceFrame } from './FDeviceFrame.vue';
11
+ export { default as FBrowserFrame } from './FBrowserFrame.vue';
12
+ export { default as FFitFrame } from './FFitFrame.vue';
13
+
14
+ export { DEVICES, DEFAULT_DEVICE, findDevice, type DeviceModel, type Cutout } from './devices';
15
+ export { artUrl, ART_CREDITS } from './art';