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.
- package/package.json +3 -2
- package/src/GUI.tsx +46 -0
- package/src/QRouter/QRegistry.tsx +53 -0
- package/src/QRouter/QRouter.stories.tsx +31 -0
- package/src/QRouter/QRouter.tsx +57 -0
- package/src/gui/Theme/GuiProvider.tsx +111 -0
- package/src/gui/Theme/Icon/Icon.resolver.tsx +29 -0
- package/src/gui/Theme/Icon/Icon.tsx +43 -0
- package/src/gui/Theme/Layout/Content/Content.resolver.tsx +0 -0
- package/src/gui/Theme/Layout/Content/Content.stories.tsx +88 -0
- package/src/gui/Theme/Layout/Content/Content.tsx +53 -0
- package/src/gui/Theme/Layout/Content/Content.types.tsx +40 -0
- package/src/gui/Theme/Layout/Footer/Footer.resolver.tsx +45 -0
- package/src/gui/Theme/Layout/Footer/Footer.stories.tsx +205 -0
- package/src/gui/Theme/Layout/Footer/Footer.tsx +337 -0
- package/src/gui/Theme/Layout/Footer/Footer.types.ts +40 -0
- package/src/gui/Theme/Layout/Layout/Layout.resolver.tsx +37 -0
- package/src/gui/Theme/Layout/Layout/Layout.stories.tsx +289 -0
- package/src/gui/Theme/Layout/Layout/Layout.tsx +117 -0
- package/src/gui/Theme/Layout/Layout/Layout.types.ts +57 -0
- package/src/gui/Theme/Layout/Layout/useLayoutBreakpoints.ts +9 -0
- package/src/gui/Theme/Layout/Namespace/Namespace.stories.tsx +105 -0
- package/src/gui/Theme/Layout/Namespace/Namespace.tsx +26 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/LeftSidebar.resolver.tsx +87 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/LeftSidebar.stories.tsx +199 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/LeftSidebar.tsx +311 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/LeftSidebar.types.ts +41 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/SidebarToggleButton.tsx +53 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarAction/LeftSidebarAction.resolver.tsx +19 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarAction/LeftSidebarAction.tsx +107 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarLink/LeftSidebarLink.resolver.tsx +0 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarLink/LeftSidebarLink.tsx +134 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarLink/LeftSidebarLink.types.ts +15 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarMenu/LeftSidebarMenu.tsx +142 -0
- package/src/gui/Theme/Layout/Sidebars/LeftSidebar/components/LeftSidebarToggleButton/LeftSidebarToggleButton.tsx +23 -0
- package/src/gui/Theme/Layout/Sidebars/RightSidebar/RightSidebar.resolver.tsx +35 -0
- package/src/gui/Theme/Layout/Sidebars/RightSidebar/RightSidebar.stories.tsx +239 -0
- package/src/gui/Theme/Layout/Sidebars/RightSidebar/RightSidebar.tsx +319 -0
- package/src/gui/Theme/Layout/Sidebars/RightSidebar/RightSidebar.types.ts +17 -0
- package/src/gui/Theme/Layout/Sidebars/RightSidebar/components/RightSidebarAction/RightSidebarAction.tsx +102 -0
- package/src/gui/Theme/Layout/Sidebars/RightSidebar/components/RightSidebarLink/RightSidebarLink.tsx +132 -0
- package/src/gui/Theme/Layout/Sidebars/RightSidebar/components/RightSidebarMenu/RightSidebarMenu.tsx +140 -0
- package/src/gui/Theme/Layout/Sidebars/RightSidebar/components/RightSidebarToggleButton/RightSidebarToggleButton.tsx +22 -0
- package/src/gui/Theme/Layout/StickyOptions/StickyOptionsTop.stories.tsx +469 -0
- package/src/gui/Theme/Layout/StickyOptions/StickyOptionsTop.tsx +489 -0
- package/src/gui/Theme/Layout/TopBar/TopBar.resolver.tsx +86 -0
- package/src/gui/Theme/Layout/TopBar/TopBar.stories.tsx +350 -0
- package/src/gui/Theme/Layout/TopBar/TopBar.tsx +292 -0
- package/src/gui/Theme/Layout/TopBar/TopBar.types.ts +39 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarAction/TopBarAction.stories.tsx +83 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarAction/TopBarAction.tsx +18 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarAction/TopBarAction.types.ts +4 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarLink/TopBarLink.stories.tsx +189 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarLink/TopBarLink.tsx +30 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarLink/TopBarLink.types.ts +9 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarMenu/TopBarMenu.resolver.tsx +14 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarMenu/TopBarMenu.stories.tsx +56 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarMenu/TopBarMenu.tsx +123 -0
- package/src/gui/Theme/Layout/TopBar/components/TopBarMenu/TopBarMenu.types.ts +44 -0
- package/src/gui/Theme/catalog/CherryByte/CherryByte.png +0 -0
- package/src/gui/Theme/catalog/CherryByte/dark.tokens.ts +47 -0
- package/src/gui/Theme/catalog/CherryByte/light.tokens.ts +47 -0
- package/src/gui/Theme/catalog/CherryByte/manifest.ts +24 -0
- package/src/gui/Theme/catalog/GhostShell/dark.tokens.ts +43 -0
- package/src/gui/Theme/catalog/GhostShell/ghost.png +0 -0
- package/src/gui/Theme/catalog/GhostShell/light.tokens.ts +39 -0
- package/src/gui/Theme/catalog/GhostShell/manifest.ts +24 -0
- package/src/gui/Theme/catalog/LunaHex/LunaHex.png +0 -0
- package/src/gui/Theme/catalog/LunaHex/dark.tokens.ts +34 -0
- package/src/gui/Theme/catalog/LunaHex/light.tokens.ts +74 -0
- package/src/gui/Theme/catalog/LunaHex/manifest.ts +24 -0
- package/src/gui/Theme/catalog/MUI/MUI.png +0 -0
- package/src/gui/Theme/catalog/MUI/dark.tokens.ts +58 -0
- package/src/gui/Theme/catalog/MUI/light.tokens.ts +74 -0
- package/src/gui/Theme/catalog/MUI/manifest.ts +24 -0
- package/src/gui/Theme/catalog/PrinceOfDarkness/dark.tokens.ts +48 -0
- package/src/gui/Theme/catalog/PrinceOfDarkness/light.tokens.ts +47 -0
- package/src/gui/Theme/catalog/PrinceOfDarkness/manifest.ts +24 -0
- package/src/gui/Theme/catalog/PrinceOfDarkness/prince.png +0 -0
- package/src/gui/Theme/catalog/PrinceOfDarkness/princeOfDarkness.png +0 -0
- package/src/gui/Theme/catalog/Seafoam/dark.tokens.ts +49 -0
- package/src/gui/Theme/catalog/Seafoam/light.tokens.ts +47 -0
- package/src/gui/Theme/catalog/Seafoam/manifest.ts +24 -0
- package/src/gui/Theme/catalog/Seafoam/seaFoam.png +0 -0
- package/src/gui/Theme/catalog/neurons/dark.tokens.ts +58 -0
- package/src/gui/Theme/catalog/neurons/light.tokens.ts +74 -0
- package/src/gui/Theme/catalog/neurons/manifest.ts +24 -0
- package/src/gui/Theme/catalog/neurons/neurons.me.png +0 -0
- package/src/gui/Theme/fromTokens.ts +272 -0
- package/src/gui/Theme/gui.css +31 -0
- package/src/gui/Theme/index.ts +17 -0
- package/src/gui/Theme/styles/buildShadows.ts +83 -0
- package/src/gui/Theme/styles/theme.tokens.ts +108 -0
- package/src/gui/Theme/utils/catalog.ts +61 -0
- package/src/gui/Theme/utils/persistence.ts +66 -0
- package/src/gui/Theme/utils/themeUtils.ts +34 -0
- package/src/gui/components/atoms/AppBar/AppBar.resolver.tsx +46 -0
- package/src/gui/components/atoms/AppBar/AppBar.stories.tsx +251 -0
- package/src/gui/components/atoms/AppBar/AppBar.tsx +107 -0
- package/src/gui/components/atoms/AppBar/AppBar.types.ts +28 -0
- package/src/gui/components/atoms/Avatar/Avatar.resolver.tsx +61 -0
- package/src/gui/components/atoms/Avatar/Avatar.stories.tsx +36 -0
- package/src/gui/components/atoms/Avatar/Avatar.tsx +14 -0
- package/src/gui/components/atoms/Box/Box.resolver.tsx +171 -0
- package/src/gui/components/atoms/Box/Box.stories.tsx +263 -0
- package/src/gui/components/atoms/Box/Box.tsx +15 -0
- package/src/gui/components/atoms/Button/Button.resolver.tsx +103 -0
- package/src/gui/components/atoms/Button/Button.stories.tsx +219 -0
- package/src/gui/components/atoms/Button/Button.tsx +40 -0
- package/src/gui/components/atoms/Card/Card.resolver.tsx +63 -0
- package/src/gui/components/atoms/Card/Card.stories.tsx +54 -0
- package/src/gui/components/atoms/Card/Card.tsx +13 -0
- package/src/gui/components/atoms/CardActions/CardActions.resolver.tsx +59 -0
- package/src/gui/components/atoms/CardActions/CardActions.stories.tsx +32 -0
- package/src/gui/components/atoms/CardActions/CardActions.tsx +14 -0
- package/src/gui/components/atoms/CardContent/CardContent.resolver.tsx +60 -0
- package/src/gui/components/atoms/CardContent/CardContent.stories.tsx +34 -0
- package/src/gui/components/atoms/CardContent/CardContent.tsx +13 -0
- package/src/gui/components/atoms/CardHeader/CardHeader.resolver.tsx +68 -0
- package/src/gui/components/atoms/CardHeader/CardHeader.stories.tsx +40 -0
- package/src/gui/components/atoms/CardHeader/CardHeader.tsx +12 -0
- package/src/gui/components/atoms/Collapse/Collapse.resolver.tsx +85 -0
- package/src/gui/components/atoms/Collapse/Collapse.stories.tsx +130 -0
- package/src/gui/components/atoms/Collapse/Collapse.tsx +17 -0
- package/src/gui/components/atoms/Divider/Divider.resolver.tsx +95 -0
- package/src/gui/components/atoms/Divider/Divider.stories.tsx +108 -0
- package/src/gui/components/atoms/Divider/Divider.tsx +14 -0
- package/src/gui/components/atoms/Drawer/Drawer.resolver.tsx +116 -0
- package/src/gui/components/atoms/Drawer/Drawer.stories.tsx +223 -0
- package/src/gui/components/atoms/Drawer/Drawer.tsx +25 -0
- package/src/gui/components/atoms/Grid/Grid.resolver.tsx +33 -0
- package/src/gui/components/atoms/Grid/Grid.stories.tsx +136 -0
- package/src/gui/components/atoms/Grid/Grid.tsx +15 -0
- package/src/gui/components/atoms/Grid/Grid.types.ts +9 -0
- package/src/gui/components/atoms/IconButton/IconButton.resolver.tsx +137 -0
- package/src/gui/components/atoms/IconButton/IconButton.stories.tsx +134 -0
- package/src/gui/components/atoms/IconButton/IconButton.tsx +22 -0
- package/src/gui/components/atoms/Link/Link.resolver.tsx +74 -0
- package/src/gui/components/atoms/Link/Link.stories.tsx +157 -0
- package/src/gui/components/atoms/Link/Link.tsx +36 -0
- package/src/gui/components/atoms/List/List.resolver.tsx +94 -0
- package/src/gui/components/atoms/List/List.stories.tsx +137 -0
- package/src/gui/components/atoms/List/List.tsx +20 -0
- package/src/gui/components/atoms/ListItem/ListItem.resolver.tsx +88 -0
- package/src/gui/components/atoms/ListItem/ListItem.stories.tsx +151 -0
- package/src/gui/components/atoms/ListItem/ListItem.tsx +19 -0
- package/src/gui/components/atoms/ListItemButton/ListItemButton.resolver.tsx +214 -0
- package/src/gui/components/atoms/ListItemButton/ListItemButton.stories.tsx +155 -0
- package/src/gui/components/atoms/ListItemButton/ListItemButton.tsx +15 -0
- package/src/gui/components/atoms/ListItemIcon/ListItemIcon.resolver.tsx +102 -0
- package/src/gui/components/atoms/ListItemIcon/ListItemIcon.stories.tsx +132 -0
- package/src/gui/components/atoms/ListItemIcon/ListItemIcon.tsx +11 -0
- package/src/gui/components/atoms/ListItemText/ListItemText.resolver.tsx +112 -0
- package/src/gui/components/atoms/ListItemText/ListItemText.stories.tsx +156 -0
- package/src/gui/components/atoms/ListItemText/ListItemText.tsx +15 -0
- package/src/gui/components/atoms/Menu/Menu.resolver.tsx +112 -0
- package/src/gui/components/atoms/Menu/Menu.stories.tsx +162 -0
- package/src/gui/components/atoms/Menu/Menu.tsx +17 -0
- package/src/gui/components/atoms/MenuItem/MenuItem.resolver.tsx +183 -0
- package/src/gui/components/atoms/MenuItem/MenuItem.stories.tsx +134 -0
- package/src/gui/components/atoms/MenuItem/MenuItem.tsx +14 -0
- package/src/gui/components/atoms/Paper/Paper.resolver.tsx +98 -0
- package/src/gui/components/atoms/Paper/Paper.stories.tsx +184 -0
- package/src/gui/components/atoms/Paper/Paper.tsx +15 -0
- package/src/gui/components/atoms/Section/Section.resolver.tsx +10 -0
- package/src/gui/components/atoms/Section/Section.stories.tsx +189 -0
- package/src/gui/components/atoms/Section/Section.tsx +76 -0
- package/src/gui/components/atoms/Section/Section.types.tsx +24 -0
- package/src/gui/components/atoms/Stack/Stack.resolver.tsx +94 -0
- package/src/gui/components/atoms/Stack/Stack.stories.tsx +160 -0
- package/src/gui/components/atoms/Stack/Stack.tsx +15 -0
- package/src/gui/components/atoms/Surface/Surface.resolver.tsx +37 -0
- package/src/gui/components/atoms/Surface/Surface.tsx +49 -0
- package/src/gui/components/atoms/Surface/Surface.types.ts +20 -0
- package/src/gui/components/atoms/Switch/Switch.resolver.tsx +53 -0
- package/src/gui/components/atoms/Switch/Switch.stories.tsx +236 -0
- package/src/gui/components/atoms/Switch/Switch.tsx +22 -0
- package/src/gui/components/atoms/Table/Body/TableBody.tsx +7 -0
- package/src/gui/components/atoms/Table/Cell/TableCell.tsx +18 -0
- package/src/gui/components/atoms/Table/Head/TableHead.tsx +9 -0
- package/src/gui/components/atoms/Table/Row/TableRow.tsx +20 -0
- package/src/gui/components/atoms/Table/Table.resolver.tsx +77 -0
- package/src/gui/components/atoms/Table/Table.stories.tsx +173 -0
- package/src/gui/components/atoms/Table/Table.tsx +20 -0
- package/src/gui/components/atoms/TextField/TextField.stories.tsx +25 -0
- package/src/gui/components/atoms/TextField/TextField.tsx +15 -0
- package/src/gui/components/atoms/Toolbar/Toolbar.resolver.tsx +60 -0
- package/src/gui/components/atoms/Toolbar/Toolbar.stories.tsx +155 -0
- package/src/gui/components/atoms/Toolbar/Toolbar.tsx +16 -0
- package/src/gui/components/atoms/Tooltip/Tooltip.resolver.tsx +142 -0
- package/src/gui/components/atoms/Tooltip/Tooltip.stories.tsx +117 -0
- package/src/gui/components/atoms/Tooltip/Tooltip.tsx +70 -0
- package/src/gui/components/atoms/Typography/Typography.resolver.tsx +158 -0
- package/src/gui/components/atoms/Typography/Typography.stories.tsx +222 -0
- package/src/gui/components/atoms/Typography/Typography.tsx +27 -0
- package/src/gui/components/atoms/Window/Nodes/node.ts +0 -0
- package/src/gui/components/atoms/Window/code/block/node.tsx +0 -0
- package/src/gui/components/atoms/Window/code/hugging.face.api.ts +11 -0
- package/src/gui/components/atoms/Window/connectors/index.ts +19 -0
- package/src/gui/components/atoms/Window/window.stories.tsx +20 -0
- package/src/gui/components/atoms/Window/window.tsx +636 -0
- package/src/gui/components/atoms/atoms.tsx +151 -0
- package/src/gui/components/atoms/index.ts +2 -0
- package/src/gui/components/generics/Cards/Gridme.jsx +52 -0
- package/src/gui/components/generics/Cards/LilBox.jsx +65 -0
- package/src/gui/components/generics/Cards/ModuleCard.jsx +106 -0
- package/src/gui/components/generics/Chats/FullChatBot.jsx +223 -0
- package/src/gui/components/generics/Code/CodeBlock.jsx +33 -0
- package/src/gui/components/generics/EmojiCursor/EmojiCursor.stories.tsx +153 -0
- package/src/gui/components/generics/EmojiCursor/EmojiCursor.tsx +23 -0
- package/src/gui/components/generics/Feedback/Callout.jsx +92 -0
- package/src/gui/components/generics/Layout/GridX.jsx +29 -0
- package/src/gui/components/generics/Layout/Hero2.jsx +132 -0
- package/src/gui/components/generics/Layout/PageContainer.jsx +29 -0
- package/src/gui/components/generics/Layout/PageDivider.jsx +20 -0
- package/src/gui/components/generics/Layout/Section.jsx +43 -0
- package/src/gui/components/generics/Layout/SectionHeader.jsx +21 -0
- package/src/gui/components/generics/Media/Img.jsx +58 -0
- package/src/gui/components/generics/Media/VideoEmbed.jsx +51 -0
- package/src/gui/components/generics/Organization/TableOfContents.jsx +51 -0
- package/src/gui/components/generics/Organization/Tabs.jsx +45 -0
- package/src/gui/components/generics/Text/TextList.jsx +41 -0
- package/src/gui/components/generics/Text/TextParagraph.jsx +28 -0
- package/src/gui/components/generics/Text/TextQuote.jsx +23 -0
- package/src/gui/components/generics/Text/TextTitle.jsx +44 -0
- package/src/gui/components/molecules/Dialog/Dialog.stories.tsx +18 -0
- package/src/gui/components/molecules/Dialog/Dialog.tsx +5 -0
- package/src/gui/components/molecules/Hero/Hero.stories.tsx +140 -0
- package/src/gui/components/molecules/Hero/Hero.tsx +152 -0
- package/src/gui/components/molecules/Hero/Hero.types.tsx +18 -0
- package/src/gui/components/molecules/Modal/Modal.resolver.tsx +38 -0
- package/src/gui/components/molecules/Modal/Modal.stories.tsx +82 -0
- package/src/gui/components/molecules/Modal/Modal.tsx +110 -0
- package/src/gui/components/molecules/Modal/Modal.types.ts +29 -0
- package/src/gui/components/molecules/Page/Page.stories.tsx +135 -0
- package/src/gui/components/molecules/Page/Page.tsx +94 -0
- package/src/gui/components/molecules/Theme/ThemeModeToggle/ThemeModeToggle.resolver.tsx +58 -0
- package/src/gui/components/molecules/Theme/ThemeModeToggle/ThemeModeToggle.stories.tsx +133 -0
- package/src/gui/components/molecules/Theme/ThemeModeToggle/ThemeModeToggle.tsx +101 -0
- package/src/gui/components/molecules/Theme/ThemeModeToggle/ThemeModeToggle.types.ts +29 -0
- package/src/gui/components/molecules/Theme/ThemesCatalog/ThemesCatalog.resolver.tsx +15 -0
- package/src/gui/components/molecules/Theme/ThemesCatalog/ThemesCatalog.stories.tsx +88 -0
- package/src/gui/components/molecules/Theme/ThemesCatalog/ThemesCatalog.tsx +167 -0
- package/src/gui/components/molecules/Theme/ThemesCatalog/ThemesCatalog.types.ts +34 -0
- package/src/gui/components/molecules/molecules.ts +49 -0
- package/src/gui/components/organisms/Blockchain/Blocks/BlocksTable.tsx +119 -0
- package/src/gui/components/organisms/Blockchain/Usernames/Identities.stories.tsx +20 -0
- package/src/gui/components/organisms/Blockchain/Usernames/QR.tsx +566 -0
- package/src/gui/components/organisms/Blockchain/Usernames/Usernames.tsx +448 -0
- package/src/gui/components/organisms/Blockchain/Usernames/identities.tsx +710 -0
- package/src/gui/components/organisms/Blockchain/blockchain.stories.tsx +17 -0
- package/src/gui/components/organisms/Blockchain/blockchain.tsx +368 -0
- package/src/gui/components/organisms/Blockchain/scripts/connection.ts +82 -0
- package/src/gui/components/organisms/Blockchain/scripts/match_face.ts +143 -0
- package/src/gui/components/organisms/HighLighter/HighLighter.stories.tsx +168 -0
- package/src/gui/components/organisms/HighLighter/HighLighter.tsx +420 -0
- package/src/gui/components/organisms/HighLighter/HighLightsDrawer.tsx +197 -0
- package/src/gui/components/organisms/IdentityNoise/FaceRecognition/FaceRecognition.stories.tsx +312 -0
- package/src/gui/components/organisms/IdentityNoise/FaceRecognition/FaceRecognition.tsx +765 -0
- package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceCameraPermission.ts +70 -0
- package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceLandmarker.ts +106 -0
- package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceOverlay.ts +489 -0
- package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceTemplate.ts +32 -0
- package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceTemplateBurst.ts +178 -0
- package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/verifyTemplate.ts +136 -0
- package/src/gui/components/organisms/IdentityNoise/IdentityNoise.tsx +403 -0
- package/src/gui/components/organisms/IdentityNoise/IndentityNoise.stories.tsx +15 -0
- package/src/gui/components/organisms/IdentityNoise/Noise/Noise.stories.tsx +206 -0
- package/src/gui/components/organisms/IdentityNoise/Noise/Noise.tsx +394 -0
- package/src/gui/components/organisms/IdentityNoise/Triad/QR.tsx +566 -0
- package/src/gui/components/organisms/IdentityNoise/Triad/Tiad.stories.tsx +6 -0
- package/src/gui/components/organisms/IdentityNoise/Triad/Triad.tsx +917 -0
- package/src/gui/components/organisms/IdentityNoise/Triad/handleCleak.ts +0 -0
- package/src/gui/components/organisms/IdentityNoise/Triad/identity.ts +31 -0
- package/src/gui/components/organisms/IdentityNoise/Triad/me/fundamentals/vectors/vectors.tsx +252 -0
- package/src/gui/components/organisms/IdentityNoise/Triad/me/me.stories.tsx +314 -0
- package/src/gui/components/organisms/IdentityNoise/Triad/me/me.tsx +0 -0
- package/src/gui/components/organisms/organisms.ts +15 -0
- package/src/gui/contexts/InsetsContext.tsx +40 -0
- package/src/gui/contexts/LeftSidebarContext.tsx +20 -0
- package/src/gui/contexts/RightSidebarContext.tsx +25 -0
- package/src/gui/contexts/ThemeContext.ts +34 -0
- package/src/gui/contexts/index.ts +4 -0
- package/src/gui/hooks/index.ts +11 -0
- package/src/gui/hooks/resolveColorToken.ts +39 -0
- package/src/gui/hooks/useCodeGen.ts +12 -0
- package/src/gui/hooks/useGuiMediaQuery.ts +18 -0
- package/src/gui/hooks/useGuiTheme.ts +18 -0
- package/src/gui/hooks/useInsets.ts +26 -0
- package/src/gui/hooks/useIsMobile.ts +13 -0
- package/src/gui/hooks/useIsTouchDevice.ts +25 -0
- package/src/gui/hooks/useLeftSidebar.ts +10 -0
- package/src/gui/hooks/useRightSidebar.ts +12 -0
- package/src/gui/hooks/useViewportKey.ts +19 -0
- package/src/gui/hooks/useViewportProp.ts +17 -0
- package/src/gui/registry/GuiRegistry.ts +19 -0
- package/src/gui/registry/factory.ts +12 -0
- package/src/gui/registry/index.ts +3 -0
- package/src/gui/registry/types.ts +6 -0
- package/src/gui/utils/nodeID.ts +11 -0
- package/src/registry/GuiRegistry.ts +19 -0
- package/src/stories/01.Home.mdx +22 -0
- package/src/stories/02.Understanding.This.GUI.mdx +149 -0
- package/src/stories/Theme/Palette.stories.tsx +86 -0
- package/src/stories/Theme/ThemeViewer.stories.tsx +91 -0
- package/src/stories/Theme/Typography.stories.jsx +211 -0
- package/src/stories/assets/this.GUI.png +0 -0
- package/src/types/gui.d.ts +67 -0
- package/src/types/theme.d.ts +191 -0
- 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
|
+
}
|