this.gui 1.3.41 → 1.3.42

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 (310) hide show
  1. package/package.json +3 -2
  2. package/src/GUI.tsx +46 -0
  3. package/src/QRouter/QRegistry.tsx +53 -0
  4. package/src/QRouter/QRouter.stories.tsx +31 -0
  5. package/src/QRouter/QRouter.tsx +57 -0
  6. package/src/gui/Theme/GuiProvider.tsx +111 -0
  7. package/src/gui/Theme/Icon/Icon.resolver.tsx +29 -0
  8. package/src/gui/Theme/Icon/Icon.tsx +43 -0
  9. package/src/gui/Theme/Layout/Content/Content.resolver.tsx +0 -0
  10. package/src/gui/Theme/Layout/Content/Content.stories.tsx +88 -0
  11. package/src/gui/Theme/Layout/Content/Content.tsx +53 -0
  12. package/src/gui/Theme/Layout/Content/Content.types.tsx +40 -0
  13. package/src/gui/Theme/Layout/Footer/Footer.resolver.tsx +45 -0
  14. package/src/gui/Theme/Layout/Footer/Footer.stories.tsx +205 -0
  15. package/src/gui/Theme/Layout/Footer/Footer.tsx +337 -0
  16. package/src/gui/Theme/Layout/Footer/Footer.types.ts +40 -0
  17. package/src/gui/Theme/Layout/Layout/Layout.resolver.tsx +37 -0
  18. package/src/gui/Theme/Layout/Layout/Layout.stories.tsx +289 -0
  19. package/src/gui/Theme/Layout/Layout/Layout.tsx +117 -0
  20. package/src/gui/Theme/Layout/Layout/Layout.types.ts +57 -0
  21. package/src/gui/Theme/Layout/Layout/useLayoutBreakpoints.ts +9 -0
  22. package/src/gui/Theme/Layout/Namespace/Namespace.stories.tsx +105 -0
  23. package/src/gui/Theme/Layout/Namespace/Namespace.tsx +26 -0
  24. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/LeftSidebar.resolver.tsx +87 -0
  25. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/LeftSidebar.stories.tsx +199 -0
  26. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/LeftSidebar.tsx +311 -0
  27. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/LeftSidebar.types.ts +41 -0
  28. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/SidebarToggleButton.tsx +53 -0
  29. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarAction/LeftSidebarAction.resolver.tsx +19 -0
  30. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarAction/LeftSidebarAction.tsx +107 -0
  31. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarLink/LeftSidebarLink.resolver.tsx +0 -0
  32. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarLink/LeftSidebarLink.tsx +134 -0
  33. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarLink/LeftSidebarLink.types.ts +15 -0
  34. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarMenu/LeftSidebarMenu.tsx +142 -0
  35. package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarToggleButton/LeftSidebarToggleButton.tsx +23 -0
  36. package/src/gui/Theme/Layout/Sidebars/RightSidebar/RightSidebar.resolver.tsx +35 -0
  37. package/src/gui/Theme/Layout/Sidebars/RightSidebar/RightSidebar.stories.tsx +239 -0
  38. package/src/gui/Theme/Layout/Sidebars/RightSidebar/RightSidebar.tsx +319 -0
  39. package/src/gui/Theme/Layout/Sidebars/RightSidebar/RightSidebar.types.ts +17 -0
  40. package/src/gui/Theme/Layout/Sidebars/RightSidebar/components/RightSidebarAction/RightSidebarAction.tsx +102 -0
  41. package/src/gui/Theme/Layout/Sidebars/RightSidebar/components/RightSidebarLink/RightSidebarLink.tsx +132 -0
  42. package/src/gui/Theme/Layout/Sidebars/RightSidebar/components/RightSidebarMenu/RightSidebarMenu.tsx +140 -0
  43. package/src/gui/Theme/Layout/Sidebars/RightSidebar/components/RightSidebarToggleButton/RightSidebarToggleButton.tsx +22 -0
  44. package/src/gui/Theme/Layout/StickyOptions/StickyOptionsTop.stories.tsx +469 -0
  45. package/src/gui/Theme/Layout/StickyOptions/StickyOptionsTop.tsx +489 -0
  46. package/src/gui/Theme/Layout/TopBar/TopBar.resolver.tsx +86 -0
  47. package/src/gui/Theme/Layout/TopBar/TopBar.stories.tsx +350 -0
  48. package/src/gui/Theme/Layout/TopBar/TopBar.tsx +292 -0
  49. package/src/gui/Theme/Layout/TopBar/TopBar.types.ts +39 -0
  50. package/src/gui/Theme/Layout/TopBar/components/TopBarAction/TopBarAction.stories.tsx +83 -0
  51. package/src/gui/Theme/Layout/TopBar/components/TopBarAction/TopBarAction.tsx +18 -0
  52. package/src/gui/Theme/Layout/TopBar/components/TopBarAction/TopBarAction.types.ts +4 -0
  53. package/src/gui/Theme/Layout/TopBar/components/TopBarLink/TopBarLink.stories.tsx +189 -0
  54. package/src/gui/Theme/Layout/TopBar/components/TopBarLink/TopBarLink.tsx +30 -0
  55. package/src/gui/Theme/Layout/TopBar/components/TopBarLink/TopBarLink.types.ts +9 -0
  56. package/src/gui/Theme/Layout/TopBar/components/TopBarMenu/TopBarMenu.resolver.tsx +14 -0
  57. package/src/gui/Theme/Layout/TopBar/components/TopBarMenu/TopBarMenu.stories.tsx +56 -0
  58. package/src/gui/Theme/Layout/TopBar/components/TopBarMenu/TopBarMenu.tsx +123 -0
  59. package/src/gui/Theme/Layout/TopBar/components/TopBarMenu/TopBarMenu.types.ts +44 -0
  60. package/src/gui/Theme/catalog/CherryByte/CherryByte.png +0 -0
  61. package/src/gui/Theme/catalog/CherryByte/dark.tokens.ts +47 -0
  62. package/src/gui/Theme/catalog/CherryByte/light.tokens.ts +47 -0
  63. package/src/gui/Theme/catalog/CherryByte/manifest.ts +24 -0
  64. package/src/gui/Theme/catalog/GhostShell/dark.tokens.ts +43 -0
  65. package/src/gui/Theme/catalog/GhostShell/ghost.png +0 -0
  66. package/src/gui/Theme/catalog/GhostShell/light.tokens.ts +39 -0
  67. package/src/gui/Theme/catalog/GhostShell/manifest.ts +24 -0
  68. package/src/gui/Theme/catalog/LunaHex/LunaHex.png +0 -0
  69. package/src/gui/Theme/catalog/LunaHex/dark.tokens.ts +34 -0
  70. package/src/gui/Theme/catalog/LunaHex/light.tokens.ts +74 -0
  71. package/src/gui/Theme/catalog/LunaHex/manifest.ts +24 -0
  72. package/src/gui/Theme/catalog/MUI/MUI.png +0 -0
  73. package/src/gui/Theme/catalog/MUI/dark.tokens.ts +58 -0
  74. package/src/gui/Theme/catalog/MUI/light.tokens.ts +74 -0
  75. package/src/gui/Theme/catalog/MUI/manifest.ts +24 -0
  76. package/src/gui/Theme/catalog/PrinceOfDarkness/dark.tokens.ts +48 -0
  77. package/src/gui/Theme/catalog/PrinceOfDarkness/light.tokens.ts +47 -0
  78. package/src/gui/Theme/catalog/PrinceOfDarkness/manifest.ts +24 -0
  79. package/src/gui/Theme/catalog/PrinceOfDarkness/prince.png +0 -0
  80. package/src/gui/Theme/catalog/PrinceOfDarkness/princeOfDarkness.png +0 -0
  81. package/src/gui/Theme/catalog/Seafoam/dark.tokens.ts +49 -0
  82. package/src/gui/Theme/catalog/Seafoam/light.tokens.ts +47 -0
  83. package/src/gui/Theme/catalog/Seafoam/manifest.ts +24 -0
  84. package/src/gui/Theme/catalog/Seafoam/seaFoam.png +0 -0
  85. package/src/gui/Theme/catalog/neurons/dark.tokens.ts +58 -0
  86. package/src/gui/Theme/catalog/neurons/light.tokens.ts +74 -0
  87. package/src/gui/Theme/catalog/neurons/manifest.ts +24 -0
  88. package/src/gui/Theme/catalog/neurons/neurons.me.png +0 -0
  89. package/src/gui/Theme/fromTokens.ts +272 -0
  90. package/src/gui/Theme/gui.css +31 -0
  91. package/src/gui/Theme/index.ts +17 -0
  92. package/src/gui/Theme/styles/buildShadows.ts +83 -0
  93. package/src/gui/Theme/styles/theme.tokens.ts +108 -0
  94. package/src/gui/Theme/utils/catalog.ts +61 -0
  95. package/src/gui/Theme/utils/persistence.ts +66 -0
  96. package/src/gui/Theme/utils/themeUtils.ts +34 -0
  97. package/src/gui/components/atoms/AppBar/AppBar.resolver.tsx +46 -0
  98. package/src/gui/components/atoms/AppBar/AppBar.stories.tsx +251 -0
  99. package/src/gui/components/atoms/AppBar/AppBar.tsx +107 -0
  100. package/src/gui/components/atoms/AppBar/AppBar.types.ts +28 -0
  101. package/src/gui/components/atoms/Avatar/Avatar.resolver.tsx +61 -0
  102. package/src/gui/components/atoms/Avatar/Avatar.stories.tsx +36 -0
  103. package/src/gui/components/atoms/Avatar/Avatar.tsx +14 -0
  104. package/src/gui/components/atoms/Box/Box.resolver.tsx +171 -0
  105. package/src/gui/components/atoms/Box/Box.stories.tsx +263 -0
  106. package/src/gui/components/atoms/Box/Box.tsx +15 -0
  107. package/src/gui/components/atoms/Button/Button.resolver.tsx +103 -0
  108. package/src/gui/components/atoms/Button/Button.stories.tsx +219 -0
  109. package/src/gui/components/atoms/Button/Button.tsx +40 -0
  110. package/src/gui/components/atoms/Card/Card.resolver.tsx +63 -0
  111. package/src/gui/components/atoms/Card/Card.stories.tsx +54 -0
  112. package/src/gui/components/atoms/Card/Card.tsx +13 -0
  113. package/src/gui/components/atoms/CardActions/CardActions.resolver.tsx +59 -0
  114. package/src/gui/components/atoms/CardActions/CardActions.stories.tsx +32 -0
  115. package/src/gui/components/atoms/CardActions/CardActions.tsx +14 -0
  116. package/src/gui/components/atoms/CardContent/CardContent.resolver.tsx +60 -0
  117. package/src/gui/components/atoms/CardContent/CardContent.stories.tsx +34 -0
  118. package/src/gui/components/atoms/CardContent/CardContent.tsx +13 -0
  119. package/src/gui/components/atoms/CardHeader/CardHeader.resolver.tsx +68 -0
  120. package/src/gui/components/atoms/CardHeader/CardHeader.stories.tsx +40 -0
  121. package/src/gui/components/atoms/CardHeader/CardHeader.tsx +12 -0
  122. package/src/gui/components/atoms/Collapse/Collapse.resolver.tsx +85 -0
  123. package/src/gui/components/atoms/Collapse/Collapse.stories.tsx +130 -0
  124. package/src/gui/components/atoms/Collapse/Collapse.tsx +17 -0
  125. package/src/gui/components/atoms/Divider/Divider.resolver.tsx +95 -0
  126. package/src/gui/components/atoms/Divider/Divider.stories.tsx +108 -0
  127. package/src/gui/components/atoms/Divider/Divider.tsx +14 -0
  128. package/src/gui/components/atoms/Drawer/Drawer.resolver.tsx +116 -0
  129. package/src/gui/components/atoms/Drawer/Drawer.stories.tsx +223 -0
  130. package/src/gui/components/atoms/Drawer/Drawer.tsx +25 -0
  131. package/src/gui/components/atoms/Grid/Grid.resolver.tsx +33 -0
  132. package/src/gui/components/atoms/Grid/Grid.stories.tsx +136 -0
  133. package/src/gui/components/atoms/Grid/Grid.tsx +15 -0
  134. package/src/gui/components/atoms/Grid/Grid.types.ts +9 -0
  135. package/src/gui/components/atoms/IconButton/IconButton.resolver.tsx +137 -0
  136. package/src/gui/components/atoms/IconButton/IconButton.stories.tsx +134 -0
  137. package/src/gui/components/atoms/IconButton/IconButton.tsx +22 -0
  138. package/src/gui/components/atoms/Link/Link.resolver.tsx +74 -0
  139. package/src/gui/components/atoms/Link/Link.stories.tsx +157 -0
  140. package/src/gui/components/atoms/Link/Link.tsx +36 -0
  141. package/src/gui/components/atoms/List/List.resolver.tsx +94 -0
  142. package/src/gui/components/atoms/List/List.stories.tsx +137 -0
  143. package/src/gui/components/atoms/List/List.tsx +20 -0
  144. package/src/gui/components/atoms/ListItem/ListItem.resolver.tsx +88 -0
  145. package/src/gui/components/atoms/ListItem/ListItem.stories.tsx +151 -0
  146. package/src/gui/components/atoms/ListItem/ListItem.tsx +19 -0
  147. package/src/gui/components/atoms/ListItemButton/ListItemButton.resolver.tsx +214 -0
  148. package/src/gui/components/atoms/ListItemButton/ListItemButton.stories.tsx +155 -0
  149. package/src/gui/components/atoms/ListItemButton/ListItemButton.tsx +15 -0
  150. package/src/gui/components/atoms/ListItemIcon/ListItemIcon.resolver.tsx +102 -0
  151. package/src/gui/components/atoms/ListItemIcon/ListItemIcon.stories.tsx +132 -0
  152. package/src/gui/components/atoms/ListItemIcon/ListItemIcon.tsx +11 -0
  153. package/src/gui/components/atoms/ListItemText/ListItemText.resolver.tsx +112 -0
  154. package/src/gui/components/atoms/ListItemText/ListItemText.stories.tsx +156 -0
  155. package/src/gui/components/atoms/ListItemText/ListItemText.tsx +15 -0
  156. package/src/gui/components/atoms/Menu/Menu.resolver.tsx +112 -0
  157. package/src/gui/components/atoms/Menu/Menu.stories.tsx +162 -0
  158. package/src/gui/components/atoms/Menu/Menu.tsx +17 -0
  159. package/src/gui/components/atoms/MenuItem/MenuItem.resolver.tsx +183 -0
  160. package/src/gui/components/atoms/MenuItem/MenuItem.stories.tsx +134 -0
  161. package/src/gui/components/atoms/MenuItem/MenuItem.tsx +14 -0
  162. package/src/gui/components/atoms/Paper/Paper.resolver.tsx +98 -0
  163. package/src/gui/components/atoms/Paper/Paper.stories.tsx +184 -0
  164. package/src/gui/components/atoms/Paper/Paper.tsx +15 -0
  165. package/src/gui/components/atoms/Section/Section.resolver.tsx +10 -0
  166. package/src/gui/components/atoms/Section/Section.stories.tsx +189 -0
  167. package/src/gui/components/atoms/Section/Section.tsx +76 -0
  168. package/src/gui/components/atoms/Section/Section.types.tsx +24 -0
  169. package/src/gui/components/atoms/Stack/Stack.resolver.tsx +94 -0
  170. package/src/gui/components/atoms/Stack/Stack.stories.tsx +160 -0
  171. package/src/gui/components/atoms/Stack/Stack.tsx +15 -0
  172. package/src/gui/components/atoms/Surface/Surface.resolver.tsx +37 -0
  173. package/src/gui/components/atoms/Surface/Surface.tsx +49 -0
  174. package/src/gui/components/atoms/Surface/Surface.types.ts +20 -0
  175. package/src/gui/components/atoms/Switch/Switch.resolver.tsx +53 -0
  176. package/src/gui/components/atoms/Switch/Switch.stories.tsx +236 -0
  177. package/src/gui/components/atoms/Switch/Switch.tsx +22 -0
  178. package/src/gui/components/atoms/Table/Body/TableBody.tsx +7 -0
  179. package/src/gui/components/atoms/Table/Cell/TableCell.tsx +18 -0
  180. package/src/gui/components/atoms/Table/Head/TableHead.tsx +9 -0
  181. package/src/gui/components/atoms/Table/Row/TableRow.tsx +20 -0
  182. package/src/gui/components/atoms/Table/Table.resolver.tsx +77 -0
  183. package/src/gui/components/atoms/Table/Table.stories.tsx +173 -0
  184. package/src/gui/components/atoms/Table/Table.tsx +20 -0
  185. package/src/gui/components/atoms/TextField/TextField.stories.tsx +25 -0
  186. package/src/gui/components/atoms/TextField/TextField.tsx +15 -0
  187. package/src/gui/components/atoms/Toolbar/Toolbar.resolver.tsx +60 -0
  188. package/src/gui/components/atoms/Toolbar/Toolbar.stories.tsx +155 -0
  189. package/src/gui/components/atoms/Toolbar/Toolbar.tsx +16 -0
  190. package/src/gui/components/atoms/Tooltip/Tooltip.resolver.tsx +142 -0
  191. package/src/gui/components/atoms/Tooltip/Tooltip.stories.tsx +117 -0
  192. package/src/gui/components/atoms/Tooltip/Tooltip.tsx +70 -0
  193. package/src/gui/components/atoms/Typography/Typography.resolver.tsx +158 -0
  194. package/src/gui/components/atoms/Typography/Typography.stories.tsx +222 -0
  195. package/src/gui/components/atoms/Typography/Typography.tsx +27 -0
  196. package/src/gui/components/atoms/Window/Nodes/node.ts +0 -0
  197. package/src/gui/components/atoms/Window/code/block/node.tsx +0 -0
  198. package/src/gui/components/atoms/Window/code/hugging.face.api.ts +11 -0
  199. package/src/gui/components/atoms/Window/connectors/index.ts +19 -0
  200. package/src/gui/components/atoms/Window/window.stories.tsx +20 -0
  201. package/src/gui/components/atoms/Window/window.tsx +636 -0
  202. package/src/gui/components/atoms/atoms.tsx +151 -0
  203. package/src/gui/components/atoms/index.ts +2 -0
  204. package/src/gui/components/generics/Cards/Gridme.jsx +52 -0
  205. package/src/gui/components/generics/Cards/LilBox.jsx +65 -0
  206. package/src/gui/components/generics/Cards/ModuleCard.jsx +106 -0
  207. package/src/gui/components/generics/Chats/FullChatBot.jsx +223 -0
  208. package/src/gui/components/generics/Code/CodeBlock.jsx +33 -0
  209. package/src/gui/components/generics/EmojiCursor/EmojiCursor.stories.tsx +153 -0
  210. package/src/gui/components/generics/EmojiCursor/EmojiCursor.tsx +23 -0
  211. package/src/gui/components/generics/Feedback/Callout.jsx +92 -0
  212. package/src/gui/components/generics/Layout/GridX.jsx +29 -0
  213. package/src/gui/components/generics/Layout/Hero2.jsx +132 -0
  214. package/src/gui/components/generics/Layout/PageContainer.jsx +29 -0
  215. package/src/gui/components/generics/Layout/PageDivider.jsx +20 -0
  216. package/src/gui/components/generics/Layout/Section.jsx +43 -0
  217. package/src/gui/components/generics/Layout/SectionHeader.jsx +21 -0
  218. package/src/gui/components/generics/Media/Img.jsx +58 -0
  219. package/src/gui/components/generics/Media/VideoEmbed.jsx +51 -0
  220. package/src/gui/components/generics/Organization/TableOfContents.jsx +51 -0
  221. package/src/gui/components/generics/Organization/Tabs.jsx +45 -0
  222. package/src/gui/components/generics/Text/TextList.jsx +41 -0
  223. package/src/gui/components/generics/Text/TextParagraph.jsx +28 -0
  224. package/src/gui/components/generics/Text/TextQuote.jsx +23 -0
  225. package/src/gui/components/generics/Text/TextTitle.jsx +44 -0
  226. package/src/gui/components/molecules/Dialog/Dialog.stories.tsx +18 -0
  227. package/src/gui/components/molecules/Dialog/Dialog.tsx +5 -0
  228. package/src/gui/components/molecules/Hero/Hero.stories.tsx +140 -0
  229. package/src/gui/components/molecules/Hero/Hero.tsx +152 -0
  230. package/src/gui/components/molecules/Hero/Hero.types.tsx +18 -0
  231. package/src/gui/components/molecules/Modal/Modal.resolver.tsx +38 -0
  232. package/src/gui/components/molecules/Modal/Modal.stories.tsx +82 -0
  233. package/src/gui/components/molecules/Modal/Modal.tsx +110 -0
  234. package/src/gui/components/molecules/Modal/Modal.types.ts +29 -0
  235. package/src/gui/components/molecules/Page/Page.stories.tsx +135 -0
  236. package/src/gui/components/molecules/Page/Page.tsx +94 -0
  237. package/src/gui/components/molecules/Theme/ThemeModeToggle/ThemeModeToggle.resolver.tsx +58 -0
  238. package/src/gui/components/molecules/Theme/ThemeModeToggle/ThemeModeToggle.stories.tsx +133 -0
  239. package/src/gui/components/molecules/Theme/ThemeModeToggle/ThemeModeToggle.tsx +101 -0
  240. package/src/gui/components/molecules/Theme/ThemeModeToggle/ThemeModeToggle.types.ts +29 -0
  241. package/src/gui/components/molecules/Theme/ThemesCatalog/ThemesCatalog.resolver.tsx +15 -0
  242. package/src/gui/components/molecules/Theme/ThemesCatalog/ThemesCatalog.stories.tsx +88 -0
  243. package/src/gui/components/molecules/Theme/ThemesCatalog/ThemesCatalog.tsx +167 -0
  244. package/src/gui/components/molecules/Theme/ThemesCatalog/ThemesCatalog.types.ts +34 -0
  245. package/src/gui/components/molecules/molecules.ts +49 -0
  246. package/src/gui/components/organisms/Blockchain/Blocks/BlocksTable.tsx +119 -0
  247. package/src/gui/components/organisms/Blockchain/Usernames/Identities.stories.tsx +20 -0
  248. package/src/gui/components/organisms/Blockchain/Usernames/QR.tsx +566 -0
  249. package/src/gui/components/organisms/Blockchain/Usernames/Usernames.tsx +448 -0
  250. package/src/gui/components/organisms/Blockchain/Usernames/identities.tsx +710 -0
  251. package/src/gui/components/organisms/Blockchain/blockchain.stories.tsx +17 -0
  252. package/src/gui/components/organisms/Blockchain/blockchain.tsx +368 -0
  253. package/src/gui/components/organisms/Blockchain/scripts/connection.ts +82 -0
  254. package/src/gui/components/organisms/Blockchain/scripts/match_face.ts +143 -0
  255. package/src/gui/components/organisms/HighLighter/HighLighter.stories.tsx +168 -0
  256. package/src/gui/components/organisms/HighLighter/HighLighter.tsx +420 -0
  257. package/src/gui/components/organisms/HighLighter/HighLightsDrawer.tsx +197 -0
  258. package/src/gui/components/organisms/IdentityNoise/FaceRecognition/FaceRecognition.stories.tsx +312 -0
  259. package/src/gui/components/organisms/IdentityNoise/FaceRecognition/FaceRecognition.tsx +765 -0
  260. package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceCameraPermission.ts +70 -0
  261. package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceLandmarker.ts +106 -0
  262. package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceOverlay.ts +489 -0
  263. package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceTemplate.ts +32 -0
  264. package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceTemplateBurst.ts +178 -0
  265. package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/verifyTemplate.ts +136 -0
  266. package/src/gui/components/organisms/IdentityNoise/IdentityNoise.tsx +403 -0
  267. package/src/gui/components/organisms/IdentityNoise/IndentityNoise.stories.tsx +15 -0
  268. package/src/gui/components/organisms/IdentityNoise/Noise/Noise.stories.tsx +206 -0
  269. package/src/gui/components/organisms/IdentityNoise/Noise/Noise.tsx +394 -0
  270. package/src/gui/components/organisms/IdentityNoise/Triad/QR.tsx +566 -0
  271. package/src/gui/components/organisms/IdentityNoise/Triad/Tiad.stories.tsx +6 -0
  272. package/src/gui/components/organisms/IdentityNoise/Triad/Triad.tsx +917 -0
  273. package/src/gui/components/organisms/IdentityNoise/Triad/handleCleak.ts +0 -0
  274. package/src/gui/components/organisms/IdentityNoise/Triad/identity.ts +31 -0
  275. package/src/gui/components/organisms/IdentityNoise/Triad/me/fundamentals/vectors/vectors.tsx +252 -0
  276. package/src/gui/components/organisms/IdentityNoise/Triad/me/me.stories.tsx +314 -0
  277. package/src/gui/components/organisms/IdentityNoise/Triad/me/me.tsx +0 -0
  278. package/src/gui/components/organisms/organisms.ts +15 -0
  279. package/src/gui/contexts/InsetsContext.tsx +40 -0
  280. package/src/gui/contexts/LeftSidebarContext.tsx +20 -0
  281. package/src/gui/contexts/RightSidebarContext.tsx +25 -0
  282. package/src/gui/contexts/ThemeContext.ts +34 -0
  283. package/src/gui/contexts/index.ts +4 -0
  284. package/src/gui/hooks/index.ts +11 -0
  285. package/src/gui/hooks/resolveColorToken.ts +39 -0
  286. package/src/gui/hooks/useCodeGen.ts +12 -0
  287. package/src/gui/hooks/useGuiMediaQuery.ts +18 -0
  288. package/src/gui/hooks/useGuiTheme.ts +18 -0
  289. package/src/gui/hooks/useInsets.ts +26 -0
  290. package/src/gui/hooks/useIsMobile.ts +13 -0
  291. package/src/gui/hooks/useIsTouchDevice.ts +25 -0
  292. package/src/gui/hooks/useLeftSidebar.ts +10 -0
  293. package/src/gui/hooks/useRightSidebar.ts +12 -0
  294. package/src/gui/hooks/useViewportKey.ts +19 -0
  295. package/src/gui/hooks/useViewportProp.ts +17 -0
  296. package/src/gui/registry/GuiRegistry.ts +19 -0
  297. package/src/gui/registry/factory.ts +12 -0
  298. package/src/gui/registry/index.ts +3 -0
  299. package/src/gui/registry/types.ts +6 -0
  300. package/src/gui/utils/nodeID.ts +11 -0
  301. package/src/registry/GuiRegistry.ts +19 -0
  302. package/src/stories/01.Home.mdx +22 -0
  303. package/src/stories/02.Understanding.This.GUI.mdx +149 -0
  304. package/src/stories/Theme/Palette.stories.tsx +86 -0
  305. package/src/stories/Theme/ThemeViewer.stories.tsx +91 -0
  306. package/src/stories/Theme/Typography.stories.jsx +211 -0
  307. package/src/stories/assets/this.GUI.png +0 -0
  308. package/src/types/gui.d.ts +67 -0
  309. package/src/types/theme.d.ts +191 -0
  310. package/src/types/viewport.ts +132 -0
@@ -0,0 +1,765 @@
1
+ import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react';
2
+ import Webcam from 'react-webcam';
3
+ import { Box, Button, Typography } from '@/gui/components/atoms/atoms';
4
+ import { Modal } from '@/gui/components/molecules/molecules';
5
+ import { useFaceLandmarker } from './modules/useFaceLandmarker';
6
+ import { useFaceCameraPermission } from './modules/useFaceCameraPermission';
7
+ import { useFaceOverlay } from './modules/useFaceOverlay';
8
+ import { useFaceTemplate } from './modules/useFaceTemplate';
9
+ import { verifyTemplateRequest } from './modules/verifyTemplate';
10
+ import type { VerifyTemplateArgs } from './modules/verifyTemplate';
11
+ import type { FaceTemplate as FaceTemplateType } from './modules/useFaceTemplateBurst';
12
+
13
+ // --- Option A face template/verification types ---
14
+ export type FaceTemplate = FaceTemplateType;
15
+ export type VerifyResponse = { match: boolean; userId?: string; score?: number; } & Record<string, any>;
16
+
17
+ export type FaceRecognitionProps = {
18
+ open: boolean;
19
+ onClose: () => void;
20
+ /** base64 data URL (jpeg) */
21
+ onCapture?: (imageDataUrl: string) => void;
22
+ /** Optional title for the modal */
23
+ title?: string;
24
+ /** Render style: modal (default) or inline camera-only */
25
+ variant?: 'modal' | 'inline';
26
+ /** Draw face landmarks overlay (inline mode). Placeholder detector until MediaPipe/TFJS is wired. */
27
+ showLandmarks?: boolean;
28
+ /** Option A: verify endpoint URL (default: /api/face/verify) */
29
+ verifyUrl?: string;
30
+ /** Option A: called with verification result */
31
+ onVerify?: (result: any) => void;
32
+ /** Option A: automatically verify after stable burst (default: true) */
33
+ autoVerify?: boolean;
34
+ /** If true, the component will include blendshapes in the template payload sent to verify/enroll. */
35
+ includeBlendshapes?: boolean;
36
+ /** Draw connection strokes between mesh points (default: true when mesh is available) */
37
+ showMeshConnections?: boolean;
38
+ /** Draw density for mesh points. 1 = full 468 points, 2 = every other, etc. (default: 1) */
39
+ meshStep?: number;
40
+ /** Mirror overlay coordinates to match a mirrored preview. Default: true. */
41
+ mirrorPreview?: boolean;
42
+ /** Externally-driven: If provided, triggers auto-verify with this template or object. */
43
+ verifyPayload?: any;
44
+ /** Externally-driven: App-level verification status (preferred over HTTP semantics). */
45
+ verifyStatus?: string | null;
46
+ /** Externally-driven: Transport HTTP status (optional, used only for debugging). */
47
+ verifyHttpStatus?: number | null;
48
+ /** Externally-driven: Provide a message for verification status badge. */
49
+ verifyMessage?: string | null;
50
+ /** Externally-driven: Called with stable template when available. */
51
+ onTemplate?: (template: FaceTemplate | null) => void;
52
+ /** Optional: emit status updates (http status/message/busy) to parent while verifying. */
53
+ onStatus?: (
54
+ next:
55
+ | { httpStatus?: number | null; message?: string | null; busy?: boolean }
56
+ | null
57
+ ) => void;
58
+ };
59
+
60
+ export default function FaceRecognition({
61
+ open,
62
+ onClose,
63
+ onCapture,
64
+ title = 'Face Scan',
65
+ variant = 'modal',
66
+ showLandmarks: propShowLandmarks,
67
+ verifyUrl = '/api/face/verify',
68
+ onVerify,
69
+ autoVerify = true,
70
+ includeBlendshapes = false,
71
+ showMeshConnections,
72
+ meshStep = 1,
73
+ mirrorPreview = true,
74
+ verifyPayload,
75
+ verifyStatus: externalVerifyStatus,
76
+ verifyHttpStatus: externalVerifyHttpStatus,
77
+ verifyMessage: externalVerifyMessage,
78
+ onTemplate,
79
+ onStatus,
80
+ }: FaceRecognitionProps) {
81
+ const webcamRef = useRef<Webcam | null>(null);
82
+ const canvasRef = useRef<HTMLCanvasElement | null>(null);
83
+ const lastRealPointsRef = useRef<{ x: number; y: number }[] | null>(null);
84
+ const lastBlendshapesRef = useRef<Array<{ categoryName: string; score: number }> | null>(null);
85
+ const lastHudAnchorRef = useRef<{ x: number; y: number } | null>(null);
86
+ // --- Option A: template refs ---
87
+ const lastStableTemplateRef = useRef<FaceTemplate | null>(null);
88
+ const autoVerifyTriggeredRef = useRef<boolean>(false);
89
+
90
+ // --- Option A: verification state ---
91
+ const [verifyLoading, setVerifyLoading] = useState(false);
92
+ const [verifyError, setVerifyError] = useState<string | null>(null);
93
+ const [verifyResult, setVerifyResult] = useState<VerifyResponse | null>(null);
94
+ const [verifyStatusLocal, setVerifyStatusLocal] = useState<string | null>(null);
95
+ const [verifyHttpStatusLocal, setVerifyHttpStatusLocal] = useState<number | null>(null);
96
+ const [error, setError] = useState<string | null>(null);
97
+
98
+ // Prefer external app-level status/message if provided
99
+ const effectiveVerifyStatus = externalVerifyStatus !== undefined ? externalVerifyStatus : verifyStatusLocal;
100
+ // Transport status is optional (debug). If parent passes it, prefer it.
101
+ const effectiveVerifyHttpStatus = externalVerifyHttpStatus !== undefined ? externalVerifyHttpStatus : verifyHttpStatusLocal;
102
+ const effectiveVerifyMessage = externalVerifyMessage ?? (verifyError || null);
103
+
104
+ // Camera permission and face landmarker state via hooks
105
+ const { permissionState } = useFaceCameraPermission({ enabled: open });
106
+
107
+ // Determine showLandmarks default: true if inline and undefined, else false
108
+ const showLandmarks = propShowLandmarks !== undefined ? propShowLandmarks : variant === 'inline';
109
+
110
+ const {
111
+ faceLandmarker,
112
+ faceLandmarkerLoading,
113
+ faceLandmarkerError,
114
+ } = useFaceLandmarker({
115
+ // Keep the MediaPipe runtime loaded while the modal/inline scanner is open.
116
+ // This avoids flicker caused by repeatedly attaching/detaching the landmarker.
117
+ enabled: open,
118
+ });
119
+
120
+ const [hasFace, setHasFace] = useState<boolean>(false);
121
+ // Keep preview + overlay mirroring deterministic and identical.
122
+ // If you want non-mirror behavior, pass mirrorPreview={false}.
123
+ const MIRROR_PREVIEW = mirrorPreview;
124
+
125
+ // --- Derived: verification target label for display ---
126
+ const verifyTargetLabel = React.useMemo(() => {
127
+ try {
128
+ const u = new URL(verifyUrl, window.location.origin);
129
+ // Only show hostname (no protocol, no port)
130
+ return u.hostname;
131
+ } catch {
132
+ // If it's a relative path (e.g. /faces/match), fall back to current hostname
133
+ return window.location.hostname;
134
+ }
135
+ }, [verifyUrl]);
136
+
137
+ const videoConstraints = useMemo(
138
+ () => ({
139
+ facingMode: 'user' as const,
140
+ width: { ideal: 720 },
141
+ height: { ideal: 720 },
142
+ }),
143
+ []
144
+ );
145
+
146
+ const handleCapture = () => {
147
+ setError(null);
148
+ const cam = webcamRef.current;
149
+ if (!cam) {
150
+ setError('Camera not ready');
151
+ return;
152
+ }
153
+
154
+ const imageDataUrl = cam.getScreenshot();
155
+ if (!imageDataUrl) {
156
+ setError('Could not capture image. Please ensure the camera is allowed.');
157
+ return;
158
+ }
159
+
160
+ onCapture?.(imageDataUrl);
161
+ };
162
+
163
+ const getVideoEl = (): HTMLVideoElement | undefined => webcamRef.current?.video as HTMLVideoElement | undefined;
164
+
165
+
166
+
167
+ // --- Option A: verification call ---
168
+ const verifyTemplate = useCallback(
169
+ async (template: FaceTemplate) => {
170
+ setVerifyLoading(true);
171
+ setVerifyError(null);
172
+ setVerifyResult(null);
173
+ setVerifyHttpStatusLocal(null);
174
+
175
+ const out = await verifyTemplateRequest({
176
+ verifyUrl,
177
+ payload: {
178
+ template,
179
+ version: includeBlendshapes ? 'mp_fmesh_v2_bs' : 'mp_fmesh_v2',
180
+ includeBlendshapes,
181
+ },
182
+ onStatus,
183
+ // App-level semantics should come from JSON (status fields), not HTTP codes.
184
+ treat404AsNotFound: false,
185
+ } satisfies VerifyTemplateArgs<any>);
186
+
187
+ // Mirror transport status into local state (debug/hud).
188
+ setVerifyHttpStatusLocal(out.status ?? null);
189
+
190
+ if (out.ok) {
191
+ // Prefer app-level status if server returns it (e.g. FACE_NOT_ENROLLED, OK, USER_NOT_FOUND).
192
+ const appStatus = (out.data as any)?.status ?? (out.data as any)?.code ?? null;
193
+ setVerifyStatusLocal(appStatus ? String(appStatus) : null);
194
+
195
+ setVerifyResult(out.data as any);
196
+ onVerify?.(out.data);
197
+ } else {
198
+ // On error we may only have an Error + status.
199
+ // App-level semantics should come from JSON on 200.
200
+ setVerifyStatusLocal(null);
201
+
202
+ const msg = out.error?.message || 'Verification failed';
203
+ setVerifyError(String(msg));
204
+ }
205
+
206
+ setVerifyLoading(false);
207
+ },
208
+ [verifyUrl, onVerify, includeBlendshapes, onStatus]
209
+ );
210
+
211
+ const { stableTemplate } = useFaceTemplate({
212
+ enabled: open && showLandmarks && hasFace,
213
+ hasFace,
214
+ pointsRef: lastRealPointsRef,
215
+ blendshapesRef: lastBlendshapesRef,
216
+ includeBlendshapes,
217
+ burstSize: 15,
218
+ intervalMs: 60,
219
+ onTemplate: (tpl) => {
220
+ // keep existing ref semantics for Verify button + auto-verify
221
+ lastStableTemplateRef.current = tpl;
222
+ autoVerifyTriggeredRef.current = false;
223
+ onTemplate?.(tpl);
224
+ },
225
+ });
226
+
227
+ // Reset verification/template state when the scanner is closed or landmarks are disabled.
228
+ useEffect(() => {
229
+ if (open && showLandmarks) return;
230
+ lastStableTemplateRef.current = null;
231
+ autoVerifyTriggeredRef.current = false;
232
+ setVerifyResult(null);
233
+ setVerifyError(null);
234
+ setVerifyLoading(false);
235
+ setVerifyStatusLocal(null);
236
+ setVerifyHttpStatusLocal(null);
237
+ onStatus?.(null);
238
+ onTemplate?.(null);
239
+ }, [open, showLandmarks, onStatus, onTemplate]);
240
+ // --- Option A: auto-verify on external payload ---
241
+ useEffect(() => {
242
+ if (!autoVerify) return;
243
+ if (!open || !showLandmarks) return;
244
+ if (!verifyPayload) return;
245
+ if (!hasFace) return;
246
+
247
+ // If parent provides a payload, we verify it (parent can manage HTTP status display too).
248
+ if (Array.isArray(verifyPayload) && verifyPayload.length) {
249
+ verifyTemplate(verifyPayload as FaceTemplate);
250
+ return;
251
+ }
252
+ if (Array.isArray((verifyPayload as any)?.template)) {
253
+ verifyTemplate((verifyPayload as any).template as FaceTemplate);
254
+ return;
255
+ }
256
+ // otherwise ignore
257
+ }, [autoVerify, open, showLandmarks, verifyPayload, verifyTemplate, hasFace]);
258
+
259
+ // --- Option A: auto-verify on stable template ---
260
+ useEffect(() => {
261
+ if (!autoVerify || !open || !showLandmarks) return;
262
+ if (verifyLoading) return;
263
+ if (!hasFace) return;
264
+ const stable = lastStableTemplateRef.current;
265
+ if (stable && !autoVerifyTriggeredRef.current) {
266
+ autoVerifyTriggeredRef.current = true;
267
+ verifyTemplate(stable);
268
+ }
269
+ // eslint-disable-next-line
270
+ }, [autoVerify, open, showLandmarks, verifyLoading, hasFace]);
271
+
272
+
273
+
274
+ useFaceOverlay({
275
+ // Only run the overlay loop when we actually have a landmarker instance.
276
+ // Running the loop while it's still loading can cause intermittent "no face" flicker.
277
+ enabled: open && showLandmarks && !!faceLandmarker,
278
+ canvasRef,
279
+ getVideoEl,
280
+ faceLandmarker,
281
+ faceLandmarkerLoading,
282
+ faceLandmarkerError,
283
+ variant,
284
+ mirrorPreview: MIRROR_PREVIEW,
285
+ showMeshConnections,
286
+ meshStep,
287
+ // Wire state/refs back into FaceRecognition for template extraction + HUD.
288
+ onHasFaceChange: setHasFace,
289
+ onPoints: (pts) => {
290
+ lastRealPointsRef.current = pts;
291
+ },
292
+ onBlendshapes: (bs) => {
293
+ lastBlendshapesRef.current = bs;
294
+ },
295
+ onHudAnchor: (anchor) => {
296
+ lastHudAnchorRef.current = anchor;
297
+ },
298
+ });
299
+
300
+
301
+ // --- Option A: status badge under canvas (modal + inline) ---
302
+ // In inline mode, we render a tiny HUD chip over the forehead area to feel “merged” into the mesh.
303
+ const renderStatusBadge = () => {
304
+ if (!showLandmarks) return null;
305
+
306
+ // If we have a live landmark anchor, pin the HUD to it.
307
+ // NOTE: We map normalized (0..1) landmark coordinates into the same cover/contain fit used by the canvas.
308
+ const getHudStyle = (): any => {
309
+ const videoNow = getVideoEl();
310
+ const canvasNow = canvasRef.current;
311
+ const anchor = lastHudAnchorRef.current;
312
+
313
+ if (!videoNow || !canvasNow || !anchor) {
314
+ return {
315
+ position: 'absolute',
316
+ top: 6,
317
+ left: '50%',
318
+ transform: 'translateX(-50%)',
319
+ };
320
+ }
321
+
322
+ const vw = videoNow.videoWidth || 0;
323
+ const vh = videoNow.videoHeight || 0;
324
+ if (!vw || !vh) {
325
+ return {
326
+ position: 'absolute',
327
+ top: 6,
328
+ left: '50%',
329
+ transform: 'translateX(-50%)',
330
+ };
331
+ }
332
+
333
+ const rect = canvasNow.getBoundingClientRect();
334
+ const cw = rect.width || canvasNow.width || 0;
335
+ const ch = rect.height || canvasNow.height || 0;
336
+ if (!cw || !ch) {
337
+ return {
338
+ position: 'absolute',
339
+ top: 6,
340
+ left: '50%',
341
+ transform: 'translateX(-50%)',
342
+ };
343
+ }
344
+
345
+ const fitMode = variant === 'inline' ? 'cover' : 'contain';
346
+ const scale = fitMode === 'cover' ? Math.max(cw / vw, ch / vh) : Math.min(cw / vw, ch / vh);
347
+ const drawW = vw * scale;
348
+ const drawH = vh * scale;
349
+ const offsetX = (cw - drawW) / 2;
350
+ const offsetY = (ch - drawH) / 2;
351
+
352
+ // Mirror to match canvas mapping
353
+ const isMirrored = MIRROR_PREVIEW;
354
+ const ax = isMirrored ? (1 - anchor.x) : anchor.x;
355
+ const ay = anchor.y;
356
+
357
+ const px = offsetX + (ax * vw) * scale;
358
+ const py = offsetY + (ay * vh) * scale;
359
+
360
+ // Nudge slightly upward so it sits on the forehead rather than inside the mask.
361
+ const nudgeY = -10;
362
+
363
+ return {
364
+ position: 'absolute',
365
+ left: `${px}px`,
366
+ top: `${py + nudgeY}px`,
367
+ transform: 'translate(-50%, -50%)',
368
+ };
369
+ };
370
+
371
+ // Determine a compact label + tone
372
+ let label: string | null = null;
373
+ let tone: 'warn' | 'error' | 'success' | 'neutral' = 'neutral';
374
+
375
+ // Highest priority: explicit app-level status from parent (Triad)
376
+ if (hasFace && !verifyLoading && !verifyResult && effectiveVerifyStatus) {
377
+ const s = String(effectiveVerifyStatus).toUpperCase();
378
+ if (s === 'FACE_NOT_ENROLLED' || s === 'NOT_ENROLLED') {
379
+ label = 'NEW';
380
+ tone = 'warn';
381
+ } else if (s === 'USER_NOT_FOUND') {
382
+ label = 'NOUSER';
383
+ tone = 'error';
384
+ } else if (s !== 'OK') {
385
+ // Unknown/non-OK app status
386
+ label = s.length > 6 ? s.slice(0, 6) : s;
387
+ tone = 'error';
388
+ }
389
+ } else if (verifyLoading) {
390
+ // Verifying state is shown as a fixed footer label instead of the HUD chip.
391
+ label = null;
392
+ } else if (hasFace && verifyError) {
393
+ // Prefer showing transport status if available, otherwise ERR.
394
+ label = effectiveVerifyHttpStatus ? String(effectiveVerifyHttpStatus) : 'ERR';
395
+ tone = 'error';
396
+ } else if (verifyResult) {
397
+ if (verifyResult.match) {
398
+ label = '✓';
399
+ tone = 'success';
400
+ } else {
401
+ label = '×';
402
+ tone = 'warn';
403
+ }
404
+ }
405
+
406
+ if (!label) return null;
407
+
408
+ const borderColor =
409
+ tone === 'success'
410
+ ? 'rgba(102,187,106,0.95)'
411
+ : tone === 'warn'
412
+ ? 'rgba(244,67,54,0.95)'
413
+ : tone === 'error'
414
+ ? 'rgba(229,115,115,0.95)'
415
+ : 'rgba(255,255,255,0.55)';
416
+
417
+ const textColor =
418
+ tone === 'success'
419
+ ? 'rgba(102,187,106,0.95)'
420
+ : tone === 'warn'
421
+ ? 'rgba(244,67,54,0.95)'
422
+ : tone === 'error'
423
+ ? 'rgba(229,115,115,0.95)'
424
+ : 'rgba(255,255,255,0.9)';
425
+ const tooltipText =
426
+ effectiveVerifyMessage ||
427
+ (effectiveVerifyStatus ? String(effectiveVerifyStatus) : null);
428
+
429
+ // Tiny HUD chip near forehead/top-center
430
+ return (
431
+ <Box
432
+ title={tooltipText || undefined}
433
+ sx={{
434
+ ...getHudStyle(),
435
+ px: 0.75,
436
+ py: 0.15,
437
+ borderRadius: 999,
438
+ fontSize: 10,
439
+ fontWeight: 900,
440
+ letterSpacing: 0.2,
441
+ color: textColor,
442
+ bgcolor: 'transparent',
443
+ border: '1px solid',
444
+ borderColor,
445
+ boxShadow: 'none',
446
+ userSelect: 'none',
447
+ pointerEvents: 'none',
448
+ lineHeight: 1.2,
449
+ }}
450
+ >
451
+ {label}
452
+ </Box>
453
+ );
454
+ };
455
+
456
+ // --- Inline: no buttons, just camera and badge ---
457
+ if (variant === 'inline') {
458
+ return (
459
+ <Box sx={{ width: '100%' }}>
460
+ <Box
461
+ sx={{
462
+ width: '100%',
463
+ position: 'relative',
464
+ aspectRatio: '1 / 1',
465
+ overflow: 'hidden',
466
+ borderRadius: 2,
467
+ border: '1px solid',
468
+ borderColor: 'divider',
469
+ bgcolor: 'background.default',
470
+ }}
471
+ >
472
+ <Webcam
473
+ audio={false}
474
+ mirrored={MIRROR_PREVIEW}
475
+ ref={webcamRef}
476
+ screenshotFormat="image/jpeg"
477
+ screenshotQuality={0.92}
478
+ videoConstraints={videoConstraints}
479
+ style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', opacity: 0 }}
480
+ onUserMedia={() => setError(null)}
481
+ onUserMediaError={() => setError('Could not access camera. Check permissions and try again.')}
482
+ />
483
+ {showLandmarks ? (
484
+ <>
485
+ <canvas
486
+ ref={canvasRef}
487
+ style={{
488
+ position: 'absolute',
489
+ inset: 0,
490
+ width: '100%',
491
+ height: '100%',
492
+ pointerEvents: 'none',
493
+ }}
494
+ />
495
+ <Box
496
+ sx={{
497
+ position: 'absolute',
498
+ top: 4,
499
+ left: 4,
500
+ bgcolor: 'rgba(0,0,0,0.5)',
501
+ px: 1,
502
+ py: 0.25,
503
+ borderRadius: 1,
504
+ color: 'white',
505
+ fontSize: 10,
506
+ fontWeight: 700,
507
+ userSelect: 'none',
508
+ }}
509
+ >
510
+ {faceLandmarkerLoading
511
+ ? 'Loading face model…'
512
+ : faceLandmarkerError
513
+ ? 'Face model not loaded (placeholder)'
514
+ : hasFace
515
+ ? ''
516
+ : 'No face'}
517
+ </Box>
518
+ </>
519
+ ) : null}
520
+ {/* Verification destination label (bottom left) */}
521
+ {verifyLoading ? (
522
+ <Box
523
+ sx={{
524
+ position: 'absolute',
525
+ left: 8,
526
+ bottom: 8,
527
+ px: 1,
528
+ py: 0.5,
529
+ borderRadius: 1,
530
+ bgcolor: 'rgba(0,0,0,0.55)',
531
+ color: 'white',
532
+ fontSize: 10,
533
+ fontWeight: 700,
534
+ userSelect: 'none',
535
+ pointerEvents: 'none',
536
+ maxWidth: 'calc(100% - 16px)',
537
+ overflow: 'hidden',
538
+ textOverflow: 'ellipsis',
539
+ whiteSpace: 'nowrap',
540
+ }}
541
+ >
542
+ {`Verifying → ${verifyTargetLabel}`}
543
+ </Box>
544
+ ) : null}
545
+ {renderStatusBadge()}
546
+ </Box>
547
+ {error ? (
548
+ <Typography sx={{ mt: 1, fontSize: 12, color: 'error.main' }}>
549
+ {error}
550
+ </Typography>
551
+ ) : null}
552
+ </Box>
553
+ );
554
+ }
555
+
556
+ // --- Modal: camera, badge, and modal-specific controls ---
557
+ return (
558
+ <Modal open={open} onClose={onClose} aria-labelledby="face-scan-modal">
559
+ <Box
560
+ sx={(theme) => ({
561
+ position: 'absolute',
562
+ top: '50%',
563
+ left: '50%',
564
+ transform: 'translate(-50%, -50%)',
565
+ width: 'min(92vw, 420px)',
566
+ borderRadius: 3,
567
+ overflow: 'hidden',
568
+ bgcolor: 'background.paper',
569
+ border: '1px solid',
570
+ borderColor: 'divider',
571
+ boxShadow: 24,
572
+ })}
573
+ >
574
+ <Box
575
+ sx={{
576
+ px: 2,
577
+ py: 1.5,
578
+ borderBottom: '1px solid',
579
+ borderColor: 'divider',
580
+ display: 'flex',
581
+ alignItems: 'center',
582
+ justifyContent: 'space-between',
583
+ gap: 1,
584
+ }}
585
+ >
586
+ <Typography fontWeight={800} sx={{ fontSize: 14 }}>
587
+ {title}
588
+ </Typography>
589
+ <Button
590
+ variant="text"
591
+ size="small"
592
+ onClick={onClose}
593
+ sx={{
594
+ minWidth: 32,
595
+ width: 32,
596
+ height: 32,
597
+ p: 0,
598
+ borderRadius: 2,
599
+ }}
600
+ aria-label="Close face scan"
601
+ >
602
+
603
+ </Button>
604
+ </Box>
605
+
606
+ <Box sx={{ p: 2 }}>
607
+ <Box
608
+ sx={{
609
+ width: '100%',
610
+ borderRadius: 2,
611
+ overflow: 'hidden',
612
+ border: '1px solid',
613
+ borderColor: 'divider',
614
+ bgcolor: 'background.default',
615
+ position: 'relative',
616
+ aspectRatio: '1 / 1',
617
+ }}
618
+ >
619
+ <Webcam
620
+ audio={false}
621
+ mirrored={MIRROR_PREVIEW}
622
+ ref={webcamRef}
623
+ screenshotFormat="image/jpeg"
624
+ screenshotQuality={0.92}
625
+ videoConstraints={videoConstraints}
626
+ style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block', opacity: 0 }}
627
+ onUserMedia={() => setError(null)}
628
+ onUserMediaError={() => setError('Could not access camera. Check permissions and try again.')}
629
+ />
630
+ {showLandmarks ? (
631
+ <>
632
+ <canvas
633
+ ref={canvasRef}
634
+ style={{
635
+ position: 'absolute',
636
+ inset: 0,
637
+ width: '100%',
638
+ height: '100%',
639
+ pointerEvents: 'none',
640
+ }}
641
+ />
642
+ <Box
643
+ sx={{
644
+ position: 'absolute',
645
+ top: 4,
646
+ left: 4,
647
+ bgcolor: 'rgba(0,0,0,0.5)',
648
+ px: 1,
649
+ py: 0.25,
650
+ borderRadius: 1,
651
+ color: 'white',
652
+ fontSize: 10,
653
+ fontWeight: 700,
654
+ userSelect: 'none',
655
+ }}
656
+ >
657
+ {faceLandmarkerLoading
658
+ ? 'Loading face model…'
659
+ : faceLandmarkerError
660
+ ? 'Face model not loaded (placeholder)'
661
+ : hasFace
662
+ ? ''
663
+ : 'No face'}
664
+ </Box>
665
+ </>
666
+ ) : null}
667
+ {/* Verification destination label (bottom left) */}
668
+ {verifyLoading ? (
669
+ <Box
670
+ sx={{
671
+ position: 'absolute',
672
+ left: 8,
673
+ bottom: 8,
674
+ px: 1,
675
+ py: 0.5,
676
+ borderRadius: 1,
677
+ bgcolor: 'rgba(0,0,0,0.55)',
678
+ color: 'white',
679
+ fontSize: 10,
680
+ fontWeight: 700,
681
+ userSelect: 'none',
682
+ pointerEvents: 'none',
683
+ maxWidth: 'calc(100% - 16px)',
684
+ overflow: 'hidden',
685
+ textOverflow: 'ellipsis',
686
+ whiteSpace: 'nowrap',
687
+ }}
688
+ >
689
+ {`Verifying → ${verifyTargetLabel}`}
690
+ </Box>
691
+ ) : null}
692
+ {renderStatusBadge()}
693
+ </Box>
694
+
695
+ {error ? (
696
+ <Typography sx={{ mt: 1, fontSize: 12, color: 'error.main' }}>
697
+ {error}
698
+ </Typography>
699
+ ) : null}
700
+
701
+ <Box
702
+ sx={{
703
+ mt: 2,
704
+ display: 'flex',
705
+ alignItems: 'center',
706
+ justifyContent: 'center',
707
+ gap: 1,
708
+ }}
709
+ >
710
+ {!showLandmarks ? (
711
+ <Button
712
+ variant="contained"
713
+ onClick={handleCapture}
714
+ disabled={permissionState === 'denied'}
715
+ sx={{
716
+ borderRadius: 2,
717
+ textTransform: 'none',
718
+ fontWeight: 800,
719
+ }}
720
+ >
721
+ Capture
722
+ </Button>
723
+ ) : (
724
+ <Button
725
+ variant="contained"
726
+ onClick={() => {
727
+ const tpl = lastStableTemplateRef.current;
728
+ if (tpl) {
729
+ verifyTemplate(tpl);
730
+ }
731
+ }}
732
+ disabled={
733
+ permissionState === 'denied' ||
734
+ !lastStableTemplateRef.current ||
735
+ verifyLoading
736
+ }
737
+ sx={{
738
+ borderRadius: 2,
739
+ textTransform: 'none',
740
+ fontWeight: 800,
741
+ }}
742
+ >
743
+ Verify
744
+ </Button>
745
+ )}
746
+ <Button
747
+ variant="text"
748
+ onClick={onClose}
749
+ sx={{
750
+ borderRadius: 2,
751
+ textTransform: 'none',
752
+ }}
753
+ >
754
+ Cancel
755
+ </Button>
756
+ </Box>
757
+
758
+ <Typography sx={{ mt: 1.5, fontSize: 11, opacity: 0.75, textAlign: 'center' }}>
759
+ Center your face inside the frame.
760
+ </Typography>
761
+ </Box>
762
+ </Box>
763
+ </Modal>
764
+ );
765
+ }