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,70 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export type CameraPermissionState = 'unknown' | 'granted' | 'denied' | 'prompt';
|
|
4
|
+
|
|
5
|
+
export type UseFaceCameraPermissionArgs = {
|
|
6
|
+
/** When false, the hook stays idle and returns 'unknown'. */
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type UseFaceCameraPermissionResult = {
|
|
11
|
+
permissionState: CameraPermissionState;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* useFaceCameraPermission
|
|
16
|
+
*
|
|
17
|
+
* Reads camera permission state using `navigator.permissions` when available.
|
|
18
|
+
* - Some browsers (notably Safari) don't support `navigator.permissions` for camera.
|
|
19
|
+
* - When unsupported (or errors), returns 'unknown'.
|
|
20
|
+
*/
|
|
21
|
+
export function useFaceCameraPermission(
|
|
22
|
+
args: UseFaceCameraPermissionArgs
|
|
23
|
+
): UseFaceCameraPermissionResult {
|
|
24
|
+
const { enabled } = args;
|
|
25
|
+
const [permissionState, setPermissionState] =
|
|
26
|
+
React.useState<CameraPermissionState>('unknown');
|
|
27
|
+
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (!enabled) {
|
|
30
|
+
setPermissionState('unknown');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let cancelled = false;
|
|
35
|
+
|
|
36
|
+
async function checkPermission() {
|
|
37
|
+
try {
|
|
38
|
+
const perms: any = (navigator as any)?.permissions;
|
|
39
|
+
if (!perms?.query) {
|
|
40
|
+
if (!cancelled) setPermissionState('unknown');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const status = await perms.query({ name: 'camera' as any });
|
|
45
|
+
if (cancelled) return;
|
|
46
|
+
|
|
47
|
+
setPermissionState((status?.state as CameraPermissionState) ?? 'unknown');
|
|
48
|
+
|
|
49
|
+
if (status && typeof status.onchange !== 'undefined') {
|
|
50
|
+
status.onchange = () => {
|
|
51
|
+
if (cancelled) return;
|
|
52
|
+
setPermissionState((status?.state as CameraPermissionState) ?? 'unknown');
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
if (!cancelled) setPermissionState('unknown');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
checkPermission();
|
|
61
|
+
|
|
62
|
+
return () => {
|
|
63
|
+
cancelled = true;
|
|
64
|
+
};
|
|
65
|
+
}, [enabled]);
|
|
66
|
+
|
|
67
|
+
return { permissionState };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default useFaceCameraPermission;
|
package/src/gui/components/organisms/IdentityNoise/FaceRecognition/modules/useFaceLandmarker.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
// We keep a module-level singleton so multiple FaceRecognition instances
|
|
6
|
+
// share one heavy MediaPipe model + wasm runtime.
|
|
7
|
+
let faceLandmarkerPromise: Promise<any | null> | null = null;
|
|
8
|
+
let faceLandmarkerSingleton: any | null = null;
|
|
9
|
+
|
|
10
|
+
// Note: The wasm + model URLs are pinned to specific versions for stability.
|
|
11
|
+
// They can be self-hosted later if desired.
|
|
12
|
+
async function getFaceLandmarker(): Promise<any | null> {
|
|
13
|
+
if (faceLandmarkerSingleton) return faceLandmarkerSingleton;
|
|
14
|
+
if (faceLandmarkerPromise) return faceLandmarkerPromise;
|
|
15
|
+
|
|
16
|
+
faceLandmarkerPromise = (async () => {
|
|
17
|
+
try {
|
|
18
|
+
const vision = await import('@mediapipe/tasks-vision');
|
|
19
|
+
const { FaceLandmarker, FilesetResolver } = vision as any;
|
|
20
|
+
|
|
21
|
+
const wasmBasePath = 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.14/wasm';
|
|
22
|
+
const filesetResolver = await FilesetResolver.forVisionTasks(wasmBasePath);
|
|
23
|
+
|
|
24
|
+
const modelPath =
|
|
25
|
+
'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task';
|
|
26
|
+
|
|
27
|
+
const faceLandmarker = await FaceLandmarker.createFromOptions(filesetResolver, {
|
|
28
|
+
baseOptions: {
|
|
29
|
+
modelAssetPath: modelPath,
|
|
30
|
+
},
|
|
31
|
+
runningMode: 'VIDEO',
|
|
32
|
+
numFaces: 1,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
faceLandmarkerSingleton = faceLandmarker;
|
|
36
|
+
return faceLandmarker;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
})();
|
|
41
|
+
|
|
42
|
+
return faceLandmarkerPromise;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type UseFaceLandmarkerOptions = {
|
|
46
|
+
/** Whether the consumer wants the landmarker available. */
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type UseFaceLandmarkerResult = {
|
|
51
|
+
faceLandmarker: any | null;
|
|
52
|
+
faceLandmarkerLoading: boolean;
|
|
53
|
+
faceLandmarkerError: boolean;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* useFaceLandmarker
|
|
58
|
+
*
|
|
59
|
+
* Loads MediaPipe's FaceLandmarker lazily and exposes loading/error state.
|
|
60
|
+
* Uses a module-level singleton so we only pay model+wasm cost once.
|
|
61
|
+
*/
|
|
62
|
+
export function useFaceLandmarker(options: UseFaceLandmarkerOptions): UseFaceLandmarkerResult {
|
|
63
|
+
const { enabled } = options;
|
|
64
|
+
|
|
65
|
+
const [faceLandmarker, setFaceLandmarker] = React.useState<any | null>(null);
|
|
66
|
+
const [faceLandmarkerError, setFaceLandmarkerError] = React.useState<boolean>(false);
|
|
67
|
+
const [faceLandmarkerLoading, setFaceLandmarkerLoading] = React.useState<boolean>(false);
|
|
68
|
+
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
if (!enabled) {
|
|
71
|
+
// We intentionally do not clear the module singleton; we only detach local state.
|
|
72
|
+
setFaceLandmarker(null);
|
|
73
|
+
setFaceLandmarkerLoading(false);
|
|
74
|
+
setFaceLandmarkerError(false);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let cancelled = false;
|
|
79
|
+
setFaceLandmarkerLoading(true);
|
|
80
|
+
setFaceLandmarkerError(false);
|
|
81
|
+
|
|
82
|
+
getFaceLandmarker().then((fl) => {
|
|
83
|
+
if (cancelled) return;
|
|
84
|
+
if (fl) {
|
|
85
|
+
setFaceLandmarker(fl);
|
|
86
|
+
setFaceLandmarkerLoading(false);
|
|
87
|
+
} else {
|
|
88
|
+
setFaceLandmarker(null);
|
|
89
|
+
setFaceLandmarkerLoading(false);
|
|
90
|
+
setFaceLandmarkerError(true);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return () => {
|
|
95
|
+
cancelled = true;
|
|
96
|
+
};
|
|
97
|
+
}, [enabled]);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
faceLandmarker,
|
|
101
|
+
faceLandmarkerLoading,
|
|
102
|
+
faceLandmarkerError,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default useFaceLandmarker;
|
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export type FacePoint = { x: number; y: number };
|
|
4
|
+
|
|
5
|
+
export type UseFaceOverlayArgs = {
|
|
6
|
+
/** Whether the overlay loop should run. Typically: open && showLandmarks */
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
/** Ref to the overlay canvas */
|
|
9
|
+
canvasRef: React.RefObject<HTMLCanvasElement | null>;
|
|
10
|
+
/** Getter for the underlying video element (from react-webcam) */
|
|
11
|
+
getVideoEl: () => HTMLVideoElement | undefined;
|
|
12
|
+
|
|
13
|
+
/** MediaPipe FaceLandmarker instance (or null). */
|
|
14
|
+
faceLandmarker: any | null;
|
|
15
|
+
/** Whether landmarker is currently loading. */
|
|
16
|
+
faceLandmarkerLoading?: boolean;
|
|
17
|
+
/** Whether landmarker failed to load. */
|
|
18
|
+
faceLandmarkerError?: boolean;
|
|
19
|
+
|
|
20
|
+
/** UI variant affects fit mode. inline => cover, modal => contain */
|
|
21
|
+
variant?: 'modal' | 'inline';
|
|
22
|
+
/** Mirror preview + overlay mapping. Default true */
|
|
23
|
+
mirrorPreview?: boolean;
|
|
24
|
+
|
|
25
|
+
/** Draw connection strokes between mesh points. Default: true when real mesh is present */
|
|
26
|
+
showMeshConnections?: boolean;
|
|
27
|
+
/** Draw density for mesh points. 1 = full 468 points, 2 = every other, etc. Default 1 */
|
|
28
|
+
meshStep?: number;
|
|
29
|
+
|
|
30
|
+
/** Detection throttle in ms. Lower = more responsive. Default 80. */
|
|
31
|
+
detectThrottleMs?: number;
|
|
32
|
+
/** How many consecutive misses before declaring no-face. Default 6 (lenient defaults bump this). */
|
|
33
|
+
noFaceMissesToClear?: number;
|
|
34
|
+
/** Grace window (ms) to keep last points after last seen face. Default 450 (lenient defaults bump this). */
|
|
35
|
+
noFaceGraceMs?: number;
|
|
36
|
+
/** Minimum time (ms) to hold a detected face before clearing. Helps avoid badge flicker. */
|
|
37
|
+
minFaceHoldMs?: number;
|
|
38
|
+
|
|
39
|
+
/** Optional: called whenever face presence changes */
|
|
40
|
+
onHasFaceChange?: (hasFace: boolean) => void;
|
|
41
|
+
/** Optional: called with latest landmark points (normalized 0..1, video space). null when none */
|
|
42
|
+
onPoints?: (points: FacePoint[] | null) => void;
|
|
43
|
+
/** Optional: called with latest blendshapes categories (if available). null when none */
|
|
44
|
+
onBlendshapes?: (
|
|
45
|
+
blendshapes: Array<{ categoryName: string; score: number }> | null
|
|
46
|
+
) => void;
|
|
47
|
+
/** Optional: called with an anchor point suitable for HUD placement (normalized) */
|
|
48
|
+
onHudAnchor?: (anchor: FacePoint | null) => void;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type UseFaceOverlayResult = {
|
|
52
|
+
hasFace: boolean;
|
|
53
|
+
/** Latest real points from MediaPipe if available; otherwise null */
|
|
54
|
+
lastPoints: FacePoint[] | null;
|
|
55
|
+
/** Latest blendshapes if available */
|
|
56
|
+
lastBlendshapes: Array<{ categoryName: string; score: number }> | null;
|
|
57
|
+
/** Latest HUD anchor (normalized) */
|
|
58
|
+
hudAnchor: FacePoint | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Lightweight subset of MediaPipe FaceMesh connections (contours)
|
|
62
|
+
const MESH_CONNECTIONS: Array<[number, number]> = [
|
|
63
|
+
// face oval
|
|
64
|
+
[10, 338],
|
|
65
|
+
[338, 297],
|
|
66
|
+
[297, 332],
|
|
67
|
+
[332, 284],
|
|
68
|
+
[284, 251],
|
|
69
|
+
[251, 389],
|
|
70
|
+
[389, 356],
|
|
71
|
+
[356, 454],
|
|
72
|
+
[454, 323],
|
|
73
|
+
[323, 361],
|
|
74
|
+
[361, 288],
|
|
75
|
+
[288, 397],
|
|
76
|
+
[397, 365],
|
|
77
|
+
[365, 379],
|
|
78
|
+
[379, 378],
|
|
79
|
+
[378, 400],
|
|
80
|
+
[400, 377],
|
|
81
|
+
[377, 152],
|
|
82
|
+
[152, 148],
|
|
83
|
+
[148, 176],
|
|
84
|
+
[176, 149],
|
|
85
|
+
[149, 150],
|
|
86
|
+
[150, 136],
|
|
87
|
+
[136, 172],
|
|
88
|
+
[172, 58],
|
|
89
|
+
[58, 132],
|
|
90
|
+
[132, 93],
|
|
91
|
+
[93, 234],
|
|
92
|
+
[234, 127],
|
|
93
|
+
[127, 162],
|
|
94
|
+
[162, 21],
|
|
95
|
+
[21, 54],
|
|
96
|
+
[54, 103],
|
|
97
|
+
[103, 67],
|
|
98
|
+
[67, 109],
|
|
99
|
+
[109, 10],
|
|
100
|
+
// lips outer (approx)
|
|
101
|
+
[61, 146],
|
|
102
|
+
[146, 91],
|
|
103
|
+
[91, 181],
|
|
104
|
+
[181, 84],
|
|
105
|
+
[84, 17],
|
|
106
|
+
[17, 314],
|
|
107
|
+
[314, 405],
|
|
108
|
+
[405, 321],
|
|
109
|
+
[321, 375],
|
|
110
|
+
[375, 291],
|
|
111
|
+
[291, 61],
|
|
112
|
+
// nose ridge-ish
|
|
113
|
+
[1, 2],
|
|
114
|
+
[2, 98],
|
|
115
|
+
[1, 5],
|
|
116
|
+
[5, 4],
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
function detectPlaceholderLandmarksFromVideo(video: HTMLVideoElement): { points: FacePoint[] } | null {
|
|
120
|
+
if (!video.videoWidth || !video.videoHeight) return null;
|
|
121
|
+
// Simple normalized points roughly for eyes, nose, mouth
|
|
122
|
+
const points: FacePoint[] = [
|
|
123
|
+
{ x: 0.35, y: 0.4 },
|
|
124
|
+
{ x: 0.65, y: 0.4 },
|
|
125
|
+
{ x: 0.5, y: 0.5 },
|
|
126
|
+
{ x: 0.4, y: 0.6 },
|
|
127
|
+
{ x: 0.6, y: 0.6 },
|
|
128
|
+
{ x: 0.3, y: 0.5 },
|
|
129
|
+
{ x: 0.7, y: 0.5 },
|
|
130
|
+
{ x: 0.5, y: 0.3 },
|
|
131
|
+
{ x: 0.45, y: 0.45 },
|
|
132
|
+
{ x: 0.55, y: 0.45 },
|
|
133
|
+
{ x: 0.5, y: 0.7 },
|
|
134
|
+
];
|
|
135
|
+
return { points };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* useFaceOverlay
|
|
140
|
+
*
|
|
141
|
+
* Owns the requestAnimationFrame loop that:
|
|
142
|
+
* - keeps canvas resolution in sync
|
|
143
|
+
* - runs MediaPipe detectForVideo (throttled)
|
|
144
|
+
* - draws mesh points + optional contour connections
|
|
145
|
+
* - publishes latest points/blendshapes/hud anchor
|
|
146
|
+
*/
|
|
147
|
+
export function useFaceOverlay(args: UseFaceOverlayArgs): UseFaceOverlayResult {
|
|
148
|
+
const {
|
|
149
|
+
enabled,
|
|
150
|
+
canvasRef,
|
|
151
|
+
getVideoEl,
|
|
152
|
+
faceLandmarker,
|
|
153
|
+
faceLandmarkerLoading = false,
|
|
154
|
+
faceLandmarkerError = false,
|
|
155
|
+
variant = 'modal',
|
|
156
|
+
mirrorPreview = true,
|
|
157
|
+
showMeshConnections,
|
|
158
|
+
meshStep = 1,
|
|
159
|
+
detectThrottleMs = 80,
|
|
160
|
+
// Smoother defaults: keep face “locked” longer to avoid flicker on brief misses.
|
|
161
|
+
noFaceMissesToClear = 24,
|
|
162
|
+
noFaceGraceMs = 1500,
|
|
163
|
+
minFaceHoldMs = 1200,
|
|
164
|
+
onHasFaceChange,
|
|
165
|
+
onPoints,
|
|
166
|
+
onBlendshapes,
|
|
167
|
+
onHudAnchor,
|
|
168
|
+
} = args;
|
|
169
|
+
|
|
170
|
+
const rafRef = React.useRef<number | null>(null);
|
|
171
|
+
const lastDetectTimeRef = React.useRef<number>(0);
|
|
172
|
+
const lastFaceSeenAtRef = React.useRef<number>(0);
|
|
173
|
+
const missCountRef = React.useRef<number>(0);
|
|
174
|
+
const lastLogAtRef = React.useRef<number>(0);
|
|
175
|
+
const lastFaceMarkedAtRef = React.useRef<number>(0);
|
|
176
|
+
|
|
177
|
+
const lastRealPointsRef = React.useRef<FacePoint[] | null>(null);
|
|
178
|
+
const lastBlendshapesRef = React.useRef<Array<{ categoryName: string; score: number }> | null>(
|
|
179
|
+
null
|
|
180
|
+
);
|
|
181
|
+
const lastHudAnchorRef = React.useRef<FacePoint | null>(null);
|
|
182
|
+
|
|
183
|
+
const [hasFace, setHasFace] = React.useState<boolean>(false);
|
|
184
|
+
|
|
185
|
+
// Keep callbacks stable without re-wiring the RAF loop every render.
|
|
186
|
+
const callbacksRef = React.useRef({ onHasFaceChange, onPoints, onBlendshapes, onHudAnchor });
|
|
187
|
+
React.useEffect(() => {
|
|
188
|
+
callbacksRef.current = { onHasFaceChange, onPoints, onBlendshapes, onHudAnchor };
|
|
189
|
+
}, [onHasFaceChange, onPoints, onBlendshapes, onHudAnchor]);
|
|
190
|
+
|
|
191
|
+
const setHasFaceSafe = React.useCallback(
|
|
192
|
+
(next: boolean, force?: boolean) => {
|
|
193
|
+
setHasFace((prev) => {
|
|
194
|
+
// Min-hold: if we already have a face, don't drop it immediately unless forced.
|
|
195
|
+
if (!next && prev && !force) {
|
|
196
|
+
const now = performance.now();
|
|
197
|
+
const heldFor = now - lastFaceMarkedAtRef.current;
|
|
198
|
+
if (heldFor < Number(minFaceHoldMs ?? 0)) {
|
|
199
|
+
return prev;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (prev !== next) callbacksRef.current.onHasFaceChange?.(next);
|
|
204
|
+
if (next) lastFaceMarkedAtRef.current = performance.now();
|
|
205
|
+
return next;
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
[minFaceHoldMs]
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
React.useEffect(() => {
|
|
212
|
+
if (!enabled) {
|
|
213
|
+
if (rafRef.current !== null) {
|
|
214
|
+
cancelAnimationFrame(rafRef.current);
|
|
215
|
+
rafRef.current = null;
|
|
216
|
+
}
|
|
217
|
+
setHasFaceSafe(false, true);
|
|
218
|
+
lastFaceSeenAtRef.current = 0;
|
|
219
|
+
missCountRef.current = 0;
|
|
220
|
+
lastLogAtRef.current = 0;
|
|
221
|
+
lastRealPointsRef.current = null;
|
|
222
|
+
lastBlendshapesRef.current = null;
|
|
223
|
+
lastHudAnchorRef.current = null;
|
|
224
|
+
callbacksRef.current.onPoints?.(null);
|
|
225
|
+
callbacksRef.current.onBlendshapes?.(null);
|
|
226
|
+
callbacksRef.current.onHudAnchor?.(null);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const canvasEl = canvasRef.current;
|
|
231
|
+
if (!canvasEl) return;
|
|
232
|
+
|
|
233
|
+
let cancelled = false;
|
|
234
|
+
|
|
235
|
+
async function draw() {
|
|
236
|
+
if (cancelled) return;
|
|
237
|
+
|
|
238
|
+
const canvasNow = canvasRef.current;
|
|
239
|
+
if (!canvasNow) return;
|
|
240
|
+
|
|
241
|
+
const videoNow = getVideoEl();
|
|
242
|
+
|
|
243
|
+
// Sync canvas backing store size with CSS size for sharpness
|
|
244
|
+
const rect = canvasNow.getBoundingClientRect();
|
|
245
|
+
if (rect.width && rect.height && (canvasNow.width !== rect.width || canvasNow.height !== rect.height)) {
|
|
246
|
+
canvasNow.width = rect.width;
|
|
247
|
+
canvasNow.height = rect.height;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const ctx = canvasNow.getContext('2d');
|
|
251
|
+
if (!ctx) {
|
|
252
|
+
rafRef.current = requestAnimationFrame(draw);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
ctx.clearRect(0, 0, canvasNow.width, canvasNow.height);
|
|
257
|
+
|
|
258
|
+
const vw = videoNow?.videoWidth ?? 0;
|
|
259
|
+
const vh = videoNow?.videoHeight ?? 0;
|
|
260
|
+
const readyState = videoNow?.readyState ?? 0;
|
|
261
|
+
const videoIsReady = !!videoNow && vw > 0 && vh > 0 && readyState >= 2;
|
|
262
|
+
|
|
263
|
+
if (!videoIsReady) {
|
|
264
|
+
rafRef.current = requestAnimationFrame(draw);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!videoNow || faceLandmarkerLoading) {
|
|
269
|
+
rafRef.current = requestAnimationFrame(draw);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// --- Compute video-to-canvas mapping (cover/contain) ---
|
|
274
|
+
const cw = canvasNow.width;
|
|
275
|
+
const ch = canvasNow.height;
|
|
276
|
+
const fitMode = variant === 'inline' ? 'cover' : 'contain';
|
|
277
|
+
const scale = fitMode === 'cover' ? Math.max(cw / vw, ch / vh) : Math.min(cw / vw, ch / vh);
|
|
278
|
+
const drawW = vw * scale;
|
|
279
|
+
const drawH = vh * scale;
|
|
280
|
+
const offsetX = (cw - drawW) / 2;
|
|
281
|
+
const offsetY = (ch - drawH) / 2;
|
|
282
|
+
|
|
283
|
+
const isMirrored = mirrorPreview;
|
|
284
|
+
|
|
285
|
+
// --- Detection (throttled) ---
|
|
286
|
+
const now = performance.now();
|
|
287
|
+
const elapsed = now - lastDetectTimeRef.current;
|
|
288
|
+
const throttleMs = Math.max(16, Number(detectThrottleMs ?? 80));
|
|
289
|
+
|
|
290
|
+
if (!faceLandmarkerError && faceLandmarker && elapsed > throttleMs) {
|
|
291
|
+
lastDetectTimeRef.current = now;
|
|
292
|
+
try {
|
|
293
|
+
const mpResult = faceLandmarker.detectForVideo(videoNow, now);
|
|
294
|
+
const has = !!(mpResult && mpResult.faceLandmarks && mpResult.faceLandmarks.length > 0);
|
|
295
|
+
|
|
296
|
+
if (has) {
|
|
297
|
+
missCountRef.current = 0;
|
|
298
|
+
lastFaceSeenAtRef.current = now;
|
|
299
|
+
lastFaceMarkedAtRef.current = now;
|
|
300
|
+
|
|
301
|
+
const rawPoints = mpResult.faceLandmarks[0];
|
|
302
|
+
const points: FacePoint[] = rawPoints.map((p: any) => ({ x: p.x, y: p.y }));
|
|
303
|
+
lastRealPointsRef.current = points;
|
|
304
|
+
|
|
305
|
+
const forehead = points[10] || points[9] || points[8] || { x: 0.5, y: 0.12 };
|
|
306
|
+
lastHudAnchorRef.current = { x: forehead.x, y: forehead.y };
|
|
307
|
+
|
|
308
|
+
const bs = (mpResult as any)?.faceBlendshapes?.[0]?.categories;
|
|
309
|
+
if (Array.isArray(bs) && bs.length) {
|
|
310
|
+
lastBlendshapesRef.current = bs.map((c: any) => ({
|
|
311
|
+
categoryName: String(c.categoryName ?? ''),
|
|
312
|
+
score: Number(c.score ?? 0),
|
|
313
|
+
}));
|
|
314
|
+
} else {
|
|
315
|
+
lastBlendshapesRef.current = null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
setHasFaceSafe(true);
|
|
319
|
+
callbacksRef.current.onPoints?.(points);
|
|
320
|
+
callbacksRef.current.onBlendshapes?.(lastBlendshapesRef.current);
|
|
321
|
+
callbacksRef.current.onHudAnchor?.(lastHudAnchorRef.current);
|
|
322
|
+
} else {
|
|
323
|
+
// Hysteresis: don't drop face immediately on a single miss.
|
|
324
|
+
missCountRef.current += 1;
|
|
325
|
+
|
|
326
|
+
const lastSeen = lastFaceSeenAtRef.current || 0;
|
|
327
|
+
const withinGrace = lastSeen > 0 && (now - lastSeen) < Number(noFaceGraceMs ?? 450);
|
|
328
|
+
const missesNeeded = Math.max(1, Number(noFaceMissesToClear ?? 6));
|
|
329
|
+
|
|
330
|
+
if (!withinGrace && missCountRef.current >= missesNeeded) {
|
|
331
|
+
setHasFaceSafe(false);
|
|
332
|
+
lastRealPointsRef.current = null;
|
|
333
|
+
lastBlendshapesRef.current = null;
|
|
334
|
+
lastHudAnchorRef.current = null;
|
|
335
|
+
callbacksRef.current.onPoints?.(null);
|
|
336
|
+
callbacksRef.current.onBlendshapes?.(null);
|
|
337
|
+
callbacksRef.current.onHudAnchor?.(null);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
} catch (err) {
|
|
341
|
+
// If detect fails intermittently, keep last points for a grace window.
|
|
342
|
+
missCountRef.current += 1;
|
|
343
|
+
const lastSeen = lastFaceSeenAtRef.current || 0;
|
|
344
|
+
const withinGrace = lastSeen > 0 && (now - lastSeen) < Number(noFaceGraceMs ?? 450);
|
|
345
|
+
const missesNeeded = Math.max(1, Number(noFaceMissesToClear ?? 6));
|
|
346
|
+
|
|
347
|
+
// Lightweight debug log (at most 1/sec)
|
|
348
|
+
if (now - lastLogAtRef.current > 1000) {
|
|
349
|
+
lastLogAtRef.current = now;
|
|
350
|
+
// eslint-disable-next-line no-console
|
|
351
|
+
console.warn('[useFaceOverlay] detectForVideo failed:', (err as any)?.message || err);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!withinGrace && missCountRef.current >= missesNeeded) {
|
|
355
|
+
setHasFaceSafe(false);
|
|
356
|
+
lastRealPointsRef.current = null;
|
|
357
|
+
lastBlendshapesRef.current = null;
|
|
358
|
+
lastHudAnchorRef.current = null;
|
|
359
|
+
callbacksRef.current.onPoints?.(null);
|
|
360
|
+
callbacksRef.current.onBlendshapes?.(null);
|
|
361
|
+
callbacksRef.current.onHudAnchor?.(null);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// --- Choose points to draw ---
|
|
367
|
+
let pointsToDraw: FacePoint[] | null = null;
|
|
368
|
+
if (faceLandmarkerError || !faceLandmarker) {
|
|
369
|
+
pointsToDraw = detectPlaceholderLandmarksFromVideo(videoNow)?.points ?? null;
|
|
370
|
+
} else {
|
|
371
|
+
pointsToDraw = lastRealPointsRef.current;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (pointsToDraw) {
|
|
375
|
+
ctx.save();
|
|
376
|
+
|
|
377
|
+
// Read theme-like CSS vars if present
|
|
378
|
+
const styles = getComputedStyle(canvasNow);
|
|
379
|
+
const primary =
|
|
380
|
+
styles.getPropertyValue('--gui-primary')?.trim() ||
|
|
381
|
+
styles.getPropertyValue('--color-primary')?.trim() ||
|
|
382
|
+
'rgba(116, 202, 255, 1)';
|
|
383
|
+
const secondary = styles.getPropertyValue('--gui-secondary')?.trim() || primary;
|
|
384
|
+
|
|
385
|
+
const grad = ctx.createLinearGradient(0, 0, canvasNow.width, 0);
|
|
386
|
+
grad.addColorStop(0, primary);
|
|
387
|
+
grad.addColorStop(1, secondary);
|
|
388
|
+
|
|
389
|
+
ctx.globalAlpha = 0.95;
|
|
390
|
+
ctx.strokeStyle = grad;
|
|
391
|
+
ctx.fillStyle = 'transparent';
|
|
392
|
+
ctx.lineWidth = 1.75;
|
|
393
|
+
ctx.shadowBlur = 10;
|
|
394
|
+
ctx.shadowColor = primary;
|
|
395
|
+
|
|
396
|
+
const mapPoint = (pt: FacePoint) => {
|
|
397
|
+
let vx = pt.x * vw;
|
|
398
|
+
const vy = pt.y * vh;
|
|
399
|
+
if (isMirrored) vx = (1 - pt.x) * vw;
|
|
400
|
+
return {
|
|
401
|
+
px: offsetX + vx * scale,
|
|
402
|
+
py: offsetY + vy * scale,
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const hasRealMesh = pointsToDraw.length > 200;
|
|
407
|
+
const connectionsEnabled = showMeshConnections !== undefined ? showMeshConnections : hasRealMesh;
|
|
408
|
+
|
|
409
|
+
if (hasRealMesh && connectionsEnabled) {
|
|
410
|
+
ctx.save();
|
|
411
|
+
ctx.globalAlpha = 0.35;
|
|
412
|
+
ctx.shadowBlur = 0;
|
|
413
|
+
ctx.lineWidth = 1.25;
|
|
414
|
+
|
|
415
|
+
ctx.beginPath();
|
|
416
|
+
for (const [a, b] of MESH_CONNECTIONS) {
|
|
417
|
+
const pa = pointsToDraw[a];
|
|
418
|
+
const pb = pointsToDraw[b];
|
|
419
|
+
if (!pa || !pb) continue;
|
|
420
|
+
const A = mapPoint(pa);
|
|
421
|
+
const B = mapPoint(pb);
|
|
422
|
+
ctx.moveTo(A.px, A.py);
|
|
423
|
+
ctx.lineTo(B.px, B.py);
|
|
424
|
+
}
|
|
425
|
+
ctx.stroke();
|
|
426
|
+
ctx.restore();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const step = hasRealMesh ? Math.max(1, meshStep || 1) : 1;
|
|
430
|
+
const r = hasRealMesh ? 2.25 : 4;
|
|
431
|
+
|
|
432
|
+
for (let i = 0; i < pointsToDraw.length; i += step) {
|
|
433
|
+
const { px, py } = mapPoint(pointsToDraw[i]);
|
|
434
|
+
ctx.beginPath();
|
|
435
|
+
ctx.arc(px, py, r, 0, 2 * Math.PI);
|
|
436
|
+
ctx.stroke();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
ctx.restore();
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
rafRef.current = requestAnimationFrame(draw);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
rafRef.current = requestAnimationFrame(draw);
|
|
446
|
+
|
|
447
|
+
return () => {
|
|
448
|
+
cancelled = true;
|
|
449
|
+
if (rafRef.current !== null) {
|
|
450
|
+
cancelAnimationFrame(rafRef.current);
|
|
451
|
+
rafRef.current = null;
|
|
452
|
+
}
|
|
453
|
+
setHasFaceSafe(false);
|
|
454
|
+
lastFaceSeenAtRef.current = 0;
|
|
455
|
+
missCountRef.current = 0;
|
|
456
|
+
lastLogAtRef.current = 0;
|
|
457
|
+
lastRealPointsRef.current = null;
|
|
458
|
+
lastBlendshapesRef.current = null;
|
|
459
|
+
lastHudAnchorRef.current = null;
|
|
460
|
+
callbacksRef.current.onPoints?.(null);
|
|
461
|
+
callbacksRef.current.onBlendshapes?.(null);
|
|
462
|
+
callbacksRef.current.onHudAnchor?.(null);
|
|
463
|
+
};
|
|
464
|
+
}, [
|
|
465
|
+
enabled,
|
|
466
|
+
canvasRef,
|
|
467
|
+
getVideoEl,
|
|
468
|
+
faceLandmarker,
|
|
469
|
+
faceLandmarkerLoading,
|
|
470
|
+
faceLandmarkerError,
|
|
471
|
+
variant,
|
|
472
|
+
mirrorPreview,
|
|
473
|
+
showMeshConnections,
|
|
474
|
+
meshStep,
|
|
475
|
+
detectThrottleMs,
|
|
476
|
+
noFaceMissesToClear,
|
|
477
|
+
noFaceGraceMs,
|
|
478
|
+
setHasFaceSafe,
|
|
479
|
+
]);
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
hasFace,
|
|
483
|
+
lastPoints: lastRealPointsRef.current,
|
|
484
|
+
lastBlendshapes: lastBlendshapesRef.current,
|
|
485
|
+
hudAnchor: lastHudAnchorRef.current,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export default useFaceOverlay;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
FaceTemplate,
|
|
4
|
+
Blendshape,
|
|
5
|
+
Point2D,
|
|
6
|
+
UseFaceTemplateBurstOptions,
|
|
7
|
+
} from './useFaceTemplateBurst';
|
|
8
|
+
import { useFaceTemplateBurst } from './useFaceTemplateBurst';
|
|
9
|
+
|
|
10
|
+
// Re-export types (optional, but handy for external imports)
|
|
11
|
+
export type { FaceTemplate, Blendshape, Point2D };
|
|
12
|
+
|
|
13
|
+
// Backwards-friendly alias: keep old hook name, same options.
|
|
14
|
+
export type UseFaceTemplateOptions = UseFaceTemplateBurstOptions;
|
|
15
|
+
|
|
16
|
+
export type UseFaceTemplateResult = {
|
|
17
|
+
stableTemplate: FaceTemplate | null;
|
|
18
|
+
reset: () => void;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* useFaceTemplate
|
|
23
|
+
*
|
|
24
|
+
* Backwards-friendly wrapper. Internally uses a burst sampler that emits
|
|
25
|
+
* a stable template every N samples.
|
|
26
|
+
*/
|
|
27
|
+
export function useFaceTemplate(options: UseFaceTemplateOptions): UseFaceTemplateResult {
|
|
28
|
+
const { stableTemplate, reset } = useFaceTemplateBurst(options);
|
|
29
|
+
return { stableTemplate, reset };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default useFaceTemplate;
|