From 0826930037b44a9e214985272bbc18c30490dd1e Mon Sep 17 00:00:00 2001 From: Daniel Ruppin Date: Tue, 3 Mar 2026 13:23:00 +0100 Subject: [PATCH 1/7] Start of reworking one-ai page --- .../ArchFlowComponents.tsx | 214 +++++ .../ArchitectureSection/ArchModelSelector.tsx | 67 ++ .../ArchitectureViewer.tsx | 144 ++++ src/components/ArchitectureSection/data.ts | 89 +++ src/components/ArchitectureSection/index.tsx | 151 ++++ src/components/ArchitectureSection/types.ts | 19 + src/components/BenefitsSection/index.tsx | 237 ++++++ src/components/ComparisonDemo/index.tsx | 94 +++ src/components/ComparisonSection/index.tsx | 221 +++++ src/components/GetStarted/index.tsx | 70 ++ src/components/HeroBackground/index.tsx | 2 +- .../NeuralNetwork/Neural3DNetwork.tsx | 72 +- .../SoftwareWindow/Neural3DCanvas.tsx | 14 +- src/components/HomeHero/index.tsx | 54 +- src/components/InventionSection/index.tsx | 126 +++ src/components/OneAiHero/SectorSelector.tsx | 75 ++ src/components/OneAiHero/index.tsx | 309 +++++++ .../OneAiHero/useSectorStateMachine.ts | 103 +++ src/components/VideoShowcase/index.tsx | 2 + src/pages/index.tsx | 18 + src/pages/one-ai.tsx | 754 +----------------- 21 files changed, 2074 insertions(+), 761 deletions(-) create mode 100644 src/components/ArchitectureSection/ArchFlowComponents.tsx create mode 100644 src/components/ArchitectureSection/ArchModelSelector.tsx create mode 100644 src/components/ArchitectureSection/ArchitectureViewer.tsx create mode 100644 src/components/ArchitectureSection/data.ts create mode 100644 src/components/ArchitectureSection/index.tsx create mode 100644 src/components/ArchitectureSection/types.ts create mode 100644 src/components/BenefitsSection/index.tsx create mode 100644 src/components/ComparisonDemo/index.tsx create mode 100644 src/components/ComparisonSection/index.tsx create mode 100644 src/components/GetStarted/index.tsx create mode 100644 src/components/InventionSection/index.tsx create mode 100644 src/components/OneAiHero/SectorSelector.tsx create mode 100644 src/components/OneAiHero/index.tsx create mode 100644 src/components/OneAiHero/useSectorStateMachine.ts diff --git a/src/components/ArchitectureSection/ArchFlowComponents.tsx b/src/components/ArchitectureSection/ArchFlowComponents.tsx new file mode 100644 index 00000000..7dd46cad --- /dev/null +++ b/src/components/ArchitectureSection/ArchFlowComponents.tsx @@ -0,0 +1,214 @@ +import React from "react"; +import type { LayerBlock, MainPathSegment, ResidualBlock } from "./types"; +import { isResidualBlock } from "./types"; + +export function LayerCard({ block }: { block: LayerBlock }) { + return ( +
+
+ + {block.name} + +
+ Input + + {block.inputShape} + +
+
+
+ + {block.type} + +
+ Output + + {block.outputShape} + +
+
+
+ ); +} + +export function CompactLayerCard({ block }: { block: LayerBlock }) { + return ( +
+
+ + {block.name} + +
+ Input + + {block.inputShape} + +
+
+
+ + {block.type} + +
+ Output + + {block.outputShape} + +
+
+
+ ); +} + +export function ArrowDown() { + return ( +
+
+ + + +
+ ); +} + +export function MiniMergeIndicator() { + return ( +
+
+
+ + + +
+
+
+ ); +} + +export function MiniForkIndicator() { + return ( +
+
+
+
+
+ + + +
+
+
+
+ ); +} + +export function LinearArchFlow({ layers }: { layers: LayerBlock[] }) { + return ( +
+ {layers.map((block, idx) => ( + + + {idx < layers.length - 1 && } + + ))} +
+ ); +} + +export function SequentialFlow({ layers }: { layers: LayerBlock[] }) { + return ( + <> + {layers.map((block, idx) => ( + +
+ {idx < layers.length - 1 && } +
+ ))} + + ); +} + +export function CompactSequentialFlow({ layers }: { layers: LayerBlock[] }) { + return ( + <> + {layers.map((block, idx) => ( + +
+ {idx < layers.length - 1 && } +
+ ))} + + ); +} + +export function ResidualBlockFlow({ block }: { block: ResidualBlock }) { + const isIdentitySkip = block.skipBranch.length === 0; + + return ( +
+ + +
+
+ +
+
+ {isIdentitySkip ? ( +
+ identity +
+
+ ) : ( + <> + +
+
+
+ + )} +
+
+ + + +
+
+ ); +} + +export function MainPathFlow({ segments }: { segments: MainPathSegment[] }) { + return ( +
+ {segments.map((segment, segIdx) => ( + + {segIdx > 0 && } + {isResidualBlock(segment) ? ( + + ) : ( + + )} + + ))} +
+ ); +} diff --git a/src/components/ArchitectureSection/ArchModelSelector.tsx b/src/components/ArchitectureSection/ArchModelSelector.tsx new file mode 100644 index 00000000..38027701 --- /dev/null +++ b/src/components/ArchitectureSection/ArchModelSelector.tsx @@ -0,0 +1,67 @@ +import React, { useEffect, useRef, useState } from "react"; +import Translate from "@docusaurus/Translate"; +import { archModels } from "./data"; + +export function ArchModelSelector({ models, activeModel, onSelect }: { + models: typeof archModels; + activeModel: number; + onSelect: (i: number) => void; +}) { + const containerRef = useRef(null); + const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]); + const [indicator, setIndicator] = useState({ left: 0, width: 0 }); + + useEffect(() => { + const btn = buttonRefs.current[activeModel]; + const container = containerRef.current; + if (btn && container) { + const containerRect = container.getBoundingClientRect(); + const btnRect = btn.getBoundingClientRect(); + setIndicator({ + left: btnRect.left - containerRect.left, + width: btnRect.width, + }); + } + }, [activeModel]); + + return ( +
+
0 ? 1 : 0, + backgroundColor: "color-mix(in srgb, var(--ifm-color-primary) 12%, transparent)", + borderColor: "color-mix(in srgb, var(--ifm-color-primary) 35%, transparent)", + }} + /> + + {models.map((m, index) => { + const isActive = index === activeModel; + const Icon = m.icon; + return ( + + ); + })} +
+ ); +} diff --git a/src/components/ArchitectureSection/ArchitectureViewer.tsx b/src/components/ArchitectureSection/ArchitectureViewer.tsx new file mode 100644 index 00000000..9d3509b7 --- /dev/null +++ b/src/components/ArchitectureSection/ArchitectureViewer.tsx @@ -0,0 +1,144 @@ +import React, { useEffect, useRef, useState } from "react"; +import { createPortal } from "react-dom"; + +function ArchViewerControls({ scale, zoomIn, zoomOut, onExpand }: { scale: number; zoomIn: () => void; zoomOut: () => void; onExpand: () => void }) { + return ( +
+ + + +
+ ); +} + +export function ArchitectureViewer({ children, onExpand }: { children: React.ReactNode; onExpand: () => void }) { + const [scale, setScale] = useState(0.75); + const contentRef = useRef(null); + const [contentHeight, setContentHeight] = useState(0); + const zoomIn = () => setScale(s => Math.min(+(s + 0.1).toFixed(1), 2)); + const zoomOut = () => setScale(s => Math.max(+(s - 0.1).toFixed(1), 0.3)); + + useEffect(() => { + const el = contentRef.current; + if (!el) return; + const ro = new ResizeObserver((entries) => { + for (const entry of entries) { + setContentHeight(entry.contentRect.height); + } + }); + ro.observe(el); + return () => ro.disconnect(); + }, []); + + return ( +
+
+
+
+ {children} +
+
+
+ +
+ ); +} + +export function ArchitectureModal({ children, onClose }: { children: React.ReactNode; onClose: () => void }) { + const [scale, setScale] = useState(0.75); + const contentRef = useRef(null); + const [contentHeight, setContentHeight] = useState(0); + const zoomIn = () => setScale(s => Math.min(+(s + 0.1).toFixed(1), 2)); + const zoomOut = () => setScale(s => Math.max(+(s - 0.1).toFixed(1), 0.3)); + + useEffect(() => { + const el = contentRef.current; + if (!el) return; + const ro = new ResizeObserver((entries) => { + for (const entry of entries) { + setContentHeight(entry.contentRect.height); + } + }); + ro.observe(el); + return () => ro.disconnect(); + }, []); + + useEffect(() => { + const handleEsc = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; + const scrollY = window.scrollY; + document.addEventListener("keydown", handleEsc); + document.body.style.position = "fixed"; + document.body.style.top = `-${scrollY}px`; + document.body.style.left = "0"; + document.body.style.right = "0"; + document.body.style.overflow = "hidden"; + return () => { + document.removeEventListener("keydown", handleEsc); + document.body.style.position = ""; + document.body.style.top = ""; + document.body.style.left = ""; + document.body.style.right = ""; + document.body.style.overflow = ""; + window.scrollTo(0, scrollY); + }; + }, [onClose]); + + return createPortal( +
+
+
e.stopPropagation()} + > +
+
+
+ {children} +
+
+
+
+ + + +
+
+
, + document.body + ); +} diff --git a/src/components/ArchitectureSection/data.ts b/src/components/ArchitectureSection/data.ts new file mode 100644 index 00000000..e3d97c83 --- /dev/null +++ b/src/components/ArchitectureSection/data.ts @@ -0,0 +1,89 @@ +import { TbGridPattern, TbArrowsSplit } from "react-icons/tb"; +import type { LayerBlock, MainPathSegment } from "./types"; + +export const linearArch: LayerBlock[] = [ + { name: "input_1", type: "InputLayer", inputShape: "[(None, 36, 36, 1)]", outputShape: "[(None, 36, 36, 1)]" }, + { name: "quantize_layer", type: "QuantizeLayer", inputShape: "[(None, 36, 36, 1)]", outputShape: "[(None, 36, 36, 1)]" }, + { name: "quant_input_conv_1 (input_conv_1)", type: "QuantizeWrapperV2 (Conv2D)", inputShape: "[(None, 36, 36, 1)]", outputShape: "[(None, 36, 36, 12)]", trainable: true }, + { name: "quant_input_max_pooling_1 (input_max_pooling_1)", type: "QuantizeWrapperV2 (MaxPooling2D)", inputShape: "[(None, 36, 36, 12)]", outputShape: "[(None, 18, 18, 12)]" }, + { name: "quant_down_conv_1 (down_conv_1)", type: "QuantizeWrapperV2 (Conv2D)", inputShape: "[(None, 18, 18, 12)]", outputShape: "[(None, 18, 18, 16)]", trainable: true }, + { name: "quant_down_max_pooling_1 (down_max_pooling_1)", type: "QuantizeWrapperV2 (MaxPooling2D)", inputShape: "[(None, 18, 18, 16)]", outputShape: "[(None, 9, 9, 16)]" }, + { name: "quant_down_conv_2 (down_conv_2)", type: "QuantizeWrapperV2 (Conv2D)", inputShape: "[(None, 9, 9, 16)]", outputShape: "[(None, 9, 9, 24)]", trainable: true }, + { name: "down_max_pooling_2", type: "MaxPooling2D", inputShape: "[(None, 9, 9, 24)]", outputShape: "[(None, 4, 4, 24)]" }, + { name: "quant_output_1_flatten_1 (output_1_flatten_1)", type: "QuantizeWrapperV2 (Flatten)", inputShape: "[(None, 4, 4, 24)]", outputShape: "[(None, 384)]" }, + { name: "quant_output_1 (output_1)", type: "QuantizeWrapperV2 (Dense)", inputShape: "[(None, 384)]", outputShape: "[(None, 1)]", trainable: true }, +]; + +export const multiInputShared: LayerBlock = { + name: "input_1", type: "InputLayer", inputShape: "[(None, 540, 640, 3, 2)]", outputShape: "[(None, 540, 640, 3, 2)]" +}; + +export const multiInputMainSegments: MainPathSegment[] = [ + [ + { name: "tf.reshape", type: "TFOpLambda", inputShape: "[(None, 540, 640, 3, 2)]", outputShape: "[(None, 540, 640, 6)]" }, + { name: "input_conv_1", type: "Conv2D", inputShape: "[(None, 540, 640, 6)]", outputShape: "[(None, 540, 640, 12)]", trainable: true }, + { name: "input_normalization_2", type: "BatchNormalization", inputShape: "[(None, 540, 640, 12)]", outputShape: "[(None, 540, 640, 12)]", trainable: true }, + { name: "input_activation_1", type: "ReLU", inputShape: "[(None, 540, 640, 12)]", outputShape: "[(None, 540, 640, 12)]" }, + { name: "input_max_pooling_1", type: "MaxPooling2D", inputShape: "[(None, 540, 640, 12)]", outputShape: "[(None, 270, 320, 12)]" }, + ], + { + mainBranch: [ + { name: "down_conv_1", type: "Conv2D", inputShape: "[(None, 270, 320, 12)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "down_normalization_2", type: "BatchNormalization", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "down_activation_1", type: "ReLU", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]" }, + { name: "down_conv_2", type: "Conv2D", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "down_normalization_4", type: "BatchNormalization", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + ], + skipBranch: [ + { name: "down_conv_3", type: "Conv2D", inputShape: "[(None, 270, 320, 12)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + ], + merge: { name: "down_add_1", type: "Add", inputShape: "[(None, 135, 160, 16)x2]", outputShape: "[(None, 135, 160, 16)]" }, + }, + [ + { name: "down_activation_2", type: "ReLU", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]" }, + ], + { + mainBranch: [ + { name: "down_conv_4", type: "Conv2D", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "down_normalization_6", type: "BatchNormalization", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "down_activation_3", type: "ReLU", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]" }, + { name: "down_conv_5", type: "Conv2D", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "down_normalization_8", type: "BatchNormalization", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + ], + skipBranch: [], + merge: { name: "down_add_2", type: "Add", inputShape: "[(None, 135, 160, 16)x2]", outputShape: "[(None, 135, 160, 16)]" }, + }, + [ + { name: "down_activation_4", type: "ReLU", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]" }, + ], +]; + +export const multiInputBranch: LayerBlock[] = [ + { name: "tf.split", type: "TFOpLambda", inputShape: "[(None, 540, 640, 3, 2)]", outputShape: "[(None, 540, 640, 3, 1)x2]" }, + { name: "tf.compat.v1.squeeze", type: "TFOpLambda", inputShape: "[(None, 540, 640, 3, 1)]", outputShape: "[(None, 540, 640, 3)]" }, + { name: "tf.compat.v1.squeeze_1", type: "TFOpLambda", inputShape: "[(None, 540, 640, 3, 1)]", outputShape: "[(None, 540, 640, 3)]" }, + { name: "in_connect_substract_1", type: "Subtract", inputShape: "[(None, 540, 640, 3)x2]", outputShape: "[(None, 540, 640, 3)]" }, + { name: "in_connect_avg_pooling_1", type: "AveragePooling2D", inputShape: "[(None, 540, 640, 3)]", outputShape: "[(None, 270, 320, 3)]" }, + { name: "in_connect_conv_1", type: "Conv2D", inputShape: "[(None, 270, 320, 3)]", outputShape: "[(None, 270, 320, 12)]", trainable: true }, + { name: "in_connect_normalization_2", type: "BatchNormalization", inputShape: "[(None, 270, 320, 12)]", outputShape: "[(None, 270, 320, 12)]", trainable: true }, + { name: "in_connect_activation_1", type: "ReLU", inputShape: "[(None, 270, 320, 12)]", outputShape: "[(None, 270, 320, 12)]" }, + { name: "in_connect_conv_2", type: "Conv2D", inputShape: "[(None, 270, 320, 12)]", outputShape: "[(None, 270, 320, 24)]", trainable: true }, + { name: "in_connect_normalization_4", type: "BatchNormalization", inputShape: "[(None, 270, 320, 24)]", outputShape: "[(None, 270, 320, 24)]", trainable: true }, + { name: "in_connect_activation_2", type: "ReLU", inputShape: "[(None, 270, 320, 24)]", outputShape: "[(None, 270, 320, 24)]" }, + { name: "in_connect_avg_pooling_2", type: "AveragePooling2D", inputShape: "[(None, 270, 320, 24)]", outputShape: "[(None, 135, 160, 24)]" }, +]; + +export const multiInputMerged: LayerBlock[] = [ + { name: "in_connect_concat_1", type: "Concatenate", inputShape: "[(None,135,160,16), (None,135,160,24)]", outputShape: "[(None, 135, 160, 40)]" }, + { name: "output_1_conv_1", type: "Conv2D", inputShape: "[(None, 135, 160, 40)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "output_1_conv_2", type: "Conv2D", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "output_1_normalization_2", type: "BatchNormalization", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]", trainable: true }, + { name: "output_1_activation_1", type: "ReLU", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 16)]" }, + { name: "output_1_binary_conv_1", type: "Conv2D", inputShape: "[(None, 135, 160, 16)]", outputShape: "[(None, 135, 160, 1)]", trainable: true }, + { name: "output_1", type: "Reshape", inputShape: "[(None, 135, 160, 1)]", outputShape: "[(None, 135, 160, 1, 1)]" }, +]; + +export const archModels = [ + { label: "Image Classification", labelId: "oneai.architecture.linear.label", image: "/img/ai/one_ai_plugin/benefits/3.webp", icon: TbGridPattern }, + { label: "Multi-Input", labelId: "oneai.architecture.multi.label", image: "/img/ai/one_ai_plugin/benefits/4.webp", icon: TbArrowsSplit }, +]; diff --git a/src/components/ArchitectureSection/index.tsx b/src/components/ArchitectureSection/index.tsx new file mode 100644 index 00000000..3feb78c1 --- /dev/null +++ b/src/components/ArchitectureSection/index.tsx @@ -0,0 +1,151 @@ +import React, { useState } from "react"; +import Translate from "@docusaurus/Translate"; +import useBaseUrl from "@docusaurus/useBaseUrl"; +import HeroBackground from "../HeroBackground"; +import { LayerCard, ArrowDown, LinearArchFlow, MainPathFlow } from "./ArchFlowComponents"; +import { ArchitectureViewer, ArchitectureModal } from "./ArchitectureViewer"; +import { ArchModelSelector } from "./ArchModelSelector"; +import { + archModels, + linearArch, + multiInputShared, + multiInputMainSegments, + multiInputBranch, + multiInputMerged, +} from "./data"; + +export default function ArchitectureSection() { + const [activeModel, setActiveModel] = useState(0); + const [modalOpen, setModalOpen] = useState(false); + + const models = archModels; + + const currentModel = models[activeModel]; + + const viewerContent = ( +
+ {activeModel === 0 ? ( + + ) : ( + <> +
+ +
+
+
+
+
+
+ + + +
+
+
+
+
+
+ Main Path + +
+
+ In-Connect Branch + {multiInputBranch.map((block, idx) => ( + +
+ {idx < multiInputBranch.length - 1 && } +
+ ))} +
+
+
+
+
+
+
+
+ + + +
+
+
+
+ {multiInputMerged.map((block, idx) => ( + +
+ + {idx < multiInputMerged.length - 1 && } +
+
+ ))} +
+ + )} +
+ ); + + return ( + +
+
+

+ + Example AI Architectures + +

+

+ + Custom neural networks generated by ONE AI — optimized for your specific application + +

+
+
+ +
+
+
+
+ +
+ {currentModel.label} +
+
+ setModalOpen(true)}> + {viewerContent} + +
+
+
+ + {modalOpen && ( + setModalOpen(false)}> + {viewerContent} + + )} + + +
+ ); +} diff --git a/src/components/ArchitectureSection/types.ts b/src/components/ArchitectureSection/types.ts new file mode 100644 index 00000000..50ab535d --- /dev/null +++ b/src/components/ArchitectureSection/types.ts @@ -0,0 +1,19 @@ +export interface LayerBlock { + name: string; + type: string; + inputShape: string; + outputShape: string; + trainable?: boolean; +} + +export interface ResidualBlock { + mainBranch: LayerBlock[]; + skipBranch: LayerBlock[]; + merge: LayerBlock; +} + +export type MainPathSegment = LayerBlock[] | ResidualBlock; + +export function isResidualBlock(segment: MainPathSegment): segment is ResidualBlock { + return !Array.isArray(segment) && 'mainBranch' in segment; +} diff --git a/src/components/BenefitsSection/index.tsx b/src/components/BenefitsSection/index.tsx new file mode 100644 index 00000000..3bcd7e44 --- /dev/null +++ b/src/components/BenefitsSection/index.tsx @@ -0,0 +1,237 @@ +import React, { useState } from "react"; +import Translate from "@docusaurus/Translate"; +import useBaseUrl from "@docusaurus/useBaseUrl"; + +export default function BenefitsSection() { + const [activeIndex, setActiveIndex] = useState(null); + + const benefits = [ + { + title: "Use Smaller Datasets", + titleId: "oneai.benefits.smaller.title", + description: "With ONE AI, you are rewarded for having a smaller dataset. This results in a leaner and faster AI model. Smaller datasets prevent memorization and help the AI focus on the actual application. Even with as few as 20 images, ONE AI can often create highly effective models and significantly reduce development time.", + descriptionId: "oneai.benefits.smaller.description" + }, + { + title: "Do Less Manual Labeling", + titleId: "oneai.benefits.labeling.title", + description: "Because ONE AI achieves great results with smaller datasets, expanding your dataset becomes much faster. The first model can automatically label new data, while ONE AI adapts the model to the growing dataset. This saves a lot of manual work.", + descriptionId: "oneai.benefits.labeling.description" + }, + { + title: "No Model Selection or Parameter Tuning", + titleId: "oneai.benefits.tuning.title", + description: "ONE AI automatically builds the best possible model for your specific use case. You do not need to search for the right foundation model, test endless parameters, or manually fine-tune your AI. ONE AI takes care of everything.", + descriptionId: "oneai.benefits.tuning.description" + }, + { + title: "Create AI with Multiple Input Images", + titleId: "oneai.benefits.multiple.title", + description: "Many applications benefit from comparing multiple images or using reference images to improve accuracy. With ONE AI, you can easily provide multiple input images, and the system automatically adjusts the AI architecture to make use of this context.", + descriptionId: "oneai.benefits.multiple.description" + }, + { + title: "Deploy AI on Any Hardware or Platform", + titleId: "oneai.benefits.export.title", + description: "The ONE AI Capture Tool can be used directly as a ready-to-use interface on any operating system. You can also export your AI as a platform-independent model, C++ project, executable, or HDL code for FPGAs. This saves a lot of integration time.", + descriptionId: "oneai.benefits.export.description" + }, + { + title: "Get AI for Your Performance Needs", + titleId: "oneai.benefits.performance.title", + description: "ONE AI automatically considers your target hardware when creating models. It ensures that your desired frame rate and resource usage are always met. You do not need to waste time on trial and error since ONE AI optimizes performance for you.", + descriptionId: "oneai.benefits.performance.description" + }, + { + title: "No Coding Required", + titleId: "oneai.benefits.coding.title", + description: "All AI creation features are fully integrated into ONE AI, so no programming is required. You simply provide your application knowledge, and ONE AI handles the complex parts. In the end, you get fully working software and AI models ready to use or integrate.", + descriptionId: "oneai.benefits.coding.description" + }, + { + title: "Get Better Results in Less Time", + titleId: "oneai.benefits.results.title", + description: "In most cases, AI models predicted by ONE AI in just 0.7 seconds are more reliable and accurate than manually developed ones. Traditional AI development rarely makes sense anymore, especially since ONE AI can handle very specific requirements that used to require manual engineering.", + descriptionId: "oneai.benefits.results.description" + } + ]; + + const toggleAccordion = (index: number) => { + setActiveIndex(activeIndex === index ? null : index); + }; + + const hotspots = [ + [ + { x: 35, y: 30, targetIndex: 1 }, + { x: 6, y: 33, targetIndex: 2 }, + { x: 90, y: 8, targetIndex: 3 }, + { x: 9, y: 13, targetIndex: 4 }, + { x: 11, y: 28, targetIndex: 5 }, + { x: 6, y: 23, targetIndex: 6 }, + ], + [ + { x: 64, y: 27, targetIndex: 1 }, + { x: 6, y: 33, targetIndex: 2 }, + { x: 9, y: 13, targetIndex: 4 }, + { x: 11, y: 28, targetIndex: 5 }, + { x: 6, y: 23, targetIndex: 6 }, + { x: 87, y: 19, targetIndex: 0 }, + { x: 90, y: 8, targetIndex: 3 }, + ], + [ + { x: 6, y: 8, targetIndex: 0 }, + { x: 70, y: 50, targetIndex: 2 }, + { x: 55, y: 21, targetIndex: 7 }, + { x: 11, y: 13, targetIndex: 4 }, + { x: 11, y: 28, targetIndex: 5 }, + { x: 6, y: 23, targetIndex: 6 }, + ], + [ + { x: 90, y: 8, targetIndex: 0 }, + { x: 35, y: 30, targetIndex: 1 }, + { x: 6, y: 33, targetIndex: 2 }, + { x: 9, y: 13, targetIndex: 4 }, + { x: 11, y: 28, targetIndex: 5 }, + { x: 6, y: 23, targetIndex: 6 }, + ], + [ + { x: 6, y: 8, targetIndex: 0 }, + { x: 4, y: 33, targetIndex: 2 }, + { x: 84, y: 16, targetIndex: 4 }, + { x: 8, y: 28, targetIndex: 5 }, + { x: 4, y: 23, targetIndex: 6 }, + ], + [ + { x: 6, y: 8, targetIndex: 0 }, + { x: 6, y: 33, targetIndex: 2 }, + { x: 9, y: 13, targetIndex: 4 }, + { x: 25, y: 55, targetIndex: 5 }, + { x: 6, y: 23, targetIndex: 6 }, + ], + [ + { x: 6, y: 8, targetIndex: 0 }, + { x: 6, y: 33, targetIndex: 2 }, + { x: 9, y: 13, targetIndex: 4 }, + { x: 11, y: 28, targetIndex: 5 }, + { x: 30, y: 25, targetIndex: 6 }, + ], + [ + { x: 49, y: 21, targetIndex: 2 }, + { x: 9, y: 13, targetIndex: 4 }, + { x: 11, y: 28, targetIndex: 5 }, + { x: 6, y: 23, targetIndex: 6 }, + ] + ]; + + return ( +
+
+
+

+ + Your Benefits as Developer: + +

+ +
+
+ {benefits.map((benefit, index) => { + const isOpen = activeIndex === index; + return ( +
toggleAccordion(index)} + className={`px-3 cursor-pointer transition-all duration-300 ease-in-out ${ + isOpen + ? 'bg-[var(--ifm-color-primary)]' + : 'bg-white/50 hover:bg-white dark:bg-[#2a2a2a] dark:hover:bg-[#333]' + }`} + > +
+ + + {benefit.title} + + + + + +
+ +
+

+ + {benefit.description} + +

+
+
+ ); + })} +
+ +
+
+ ONE AI Benefit Illustration { + e.currentTarget.src = '/img/ai/Capture.png'; + }} + /> + + {hotspots[activeIndex !== null ? activeIndex : 0]?.map((hotspot, idx) => ( +
toggleAccordion(hotspot.targetIndex)} + > +
+
+
+ +
+ + {benefits[hotspot.targetIndex].title} + +
+
+ ))} +
+
+
+
+
+
+ ); +} diff --git a/src/components/ComparisonDemo/index.tsx b/src/components/ComparisonDemo/index.tsx new file mode 100644 index 00000000..514abe7d --- /dev/null +++ b/src/components/ComparisonDemo/index.tsx @@ -0,0 +1,94 @@ +import React, { useEffect, useRef } from "react"; +import Translate from "@docusaurus/Translate"; +import useBaseUrl from "@docusaurus/useBaseUrl"; + +export default function ComparisonDemo() { + const videoRef1 = useRef(null); + const videoRef2 = useRef(null); + + useEffect(() => { + videoRef1.current?.play().catch(() => {}); + videoRef2.current?.play().catch(() => {}); + }, []); + + return ( +
+
+

+ + Real-World Performance + +

+

+ Moving Objects · Small Objects · Big Image Size +

+ +
+
+
+
+
+

ONE AI

+

Optimized CNN Architecture

+
+
+ 456 + Detections +
+
+ 24 + FPS* +
+
+ 0.04 M + Parameters +
+
+
+
+ +
+
+
+
+

YOLO26n

+
+
+ 379 + Detections +
+
+ 2 + FPS* +
+
+ 2.4 M + Parameters +
+
+
+
+
+ +

+ *One Core of Intel Ultra 7 CPU +

+
+
+ ); +} diff --git a/src/components/ComparisonSection/index.tsx b/src/components/ComparisonSection/index.tsx new file mode 100644 index 00000000..d3f8e3e1 --- /dev/null +++ b/src/components/ComparisonSection/index.tsx @@ -0,0 +1,221 @@ +import React, { useRef, useState } from "react"; +import Translate from "@docusaurus/Translate"; +import useBaseUrl from "@docusaurus/useBaseUrl"; +import { useColorMode } from "@docusaurus/theme-common"; + +export default function ComparisonSection() { + const { colorMode } = useColorMode(); + const isDarkMode = colorMode === "dark"; + const [activeUseCase, setActiveUseCase] = useState(0); + const touchStartX = useRef(null); + + const useCases = [ + { + title: "High Speed HDL", + titleId: "oneai.usecase.hdl.title", + subtitle: "ONE AI implements efficient AI on any FPGA with our open source AI to HDL libraries", + subtitleId: "oneai.usecase.hdl.subtitle", + backgroundImage: "/img/ai/one_ai_plugin/use_cases/chip/defect_1.png", + displayImage: isDarkMode ? "/img/ai/one_ai_plugin/use_cases/chip/integration.svg" : "/img/ai/one_ai_plugin/use_cases/chip/integration-light.svg", + description: "Next to the vendor tools for AI integration we offer a version for parallel AI integration on FPGAs that allows integration just like parallel image processing that doesn't need an additional processor and adds no overhead to the system. Together with our partner Altera we show how Altera's MAX® 10 with ONE AI and our HDL generator can now outperform Nvidia's Jetson Orin Nano with:", + descriptionId: "oneai.usecase.hdl.description", + metrics: [ + { value: "72×", label: "Faster Detection", labelId: "homepage.metric.latency" }, + { value: "24×", label: "Less Errors", labelId: "homepage.metric.errors" }, + { value: "20×", label: "Lower Power", labelId: "homepage.metric.power" }, + { value: "6×", label: "Lower Cost", labelId: "homepage.metric.cost" }, + ], + whitepaper: "/docs/one-ai/documentation/integration/fpga-deployment", + linkText: "More Details", + linkTextId: "oneai.usecase.link" + }, + { + title: "Prebuild UI", + titleId: "oneai.usecase.ui.title", + subtitle: "Integration on any system with operating system and graphical interface", + subtitleId: "oneai.usecase.ui.subtitle", + backgroundImage: "/img/ai/one_ai_plugin/use_cases/capture/preview.png", + displayImage: isDarkMode ? "/img/ai/one_ai_plugin/use_cases/capture/integration.svg" : "/img/ai/one_ai_plugin/use_cases/capture/integration-light.svg", + description: "Eliminate months of development time. Integrate AI with our pre-build UI that supports monitoring, remote control and continuous improvement. Already in production with leading production companies and ready for your quality control or automation task with:", + descriptionId: "oneai.usecase.ui.description", + metrics: [ + { value: "1-Click", label: "Deployment", labelId: "oneai.metric.deployment" }, + { value: "< 1 Day", label: "Development Time", labelId: "oneai.metric.devtime" }, + ], + whitepaper: "/docs/one-ai/documentation/camera-tool", + linkText: "More Details", + linkTextId: "oneai.usecase.link" + }, + { + title: "Direct Integration with SDK", + titleId: "oneai.usecase.sdk.title", + subtitle: "Deploy AI in Any Application", + subtitleId: "oneai.usecase.sdk.subtitle", + backgroundImage: "/img/ai/one_ai_plugin/demos/overlap-difference/image_000118_test.png", + displayImage: isDarkMode ? "/img/ai/one_ai_plugin/use_cases/pcb/integration.svg" : "/img/ai/one_ai_plugin/use_cases/pcb/integration-light.svg", + description: "ONE AI can create generic ONNX or Tensorflow Lite models that can be integrated directly with your application using our SDK. One example AI model,that you can integrate, detects small objects on complex backgrounds and outperformes YOLOv8 with:", + descriptionId: "oneai.usecase.sdk.description", + metrics: [ + { value: "95.7", label: "F1 Score", labelId: "homepage.metric.f1score" }, + { value: "8×", label: "Smaller Model", labelId: "homepage.metric.modelsize" }, + { value: "10×", label: "Fewer Errors", labelId: "homepage.metric.fewererrors" }, + ], + whitepaper: "/docs/one-ai/documentation/integration/deployment-overview", + linkText: "More Details", + linkTextId: "oneai.usecase.link" + }, + { + title: "C++ Project or Executable", + titleId: "oneai.usecase.cpp.title", + subtitle: "Deploy AI with any CPU, TPU, GPU or MCU", + subtitleId: "oneai.usecase.cpp.subtitle", + backgroundImage: "/img/ai/one_ai_plugin/use_cases/pcb/pcb_1.png", + displayImage: isDarkMode ? "/img/ai/one_ai_plugin/use_cases/pcb/integration.svg" : "/img/ai/one_ai_plugin/use_cases/pcb/integration-light.svg", + description: "ONE AI can create Tensorflow Lite based C++ projects or precompiled executables with API that run efficient with any kind of processor or AI accelerator. One example is a PCB quality control where the AI by ONE AI beat not only standard image processing and universal AI models by speed and accuracy, but also the AI model from the scientists with:", + descriptionId: "oneai.usecase.cpp.description", + metrics: [ + { value: "98.4", label: "F1 Score", labelId: "oneai.metric.f1score" }, + { value: "750 %", label: "Speed Increase", labelId: "oneai.metric.speed" }, + ], + whitepaper: "/docs/one-ai/documentation/export", + linkText: "More Details", + linkTextId: "oneai.usecase.link" + } + ]; + + const currentUseCase = useCases[activeUseCase]; + + const handlePrev = () => { + setActiveUseCase(activeUseCase - 1 < 0 ? useCases.length - 1 : activeUseCase - 1); + }; + + const handleNext = () => { + setActiveUseCase(activeUseCase + 1 >= useCases.length ? 0 : activeUseCase + 1); + }; + + const handleTouchStart = (e: React.TouchEvent) => { + touchStartX.current = e.touches[0].clientX; + }; + + const handleTouchEnd = (e: React.TouchEvent) => { + if (touchStartX.current === null) return; + const diff = touchStartX.current - e.changedTouches[0].clientX; + if (Math.abs(diff) > 50) { + if (diff > 0) handleNext(); + else handlePrev(); + } + touchStartX.current = null; + }; + + return ( +
+
+
+

+ + Deployed with ONE Click + +

+ +
+
+
+
+
+ +
+

+ + {currentUseCase.title} + +

+

+ + {currentUseCase.subtitle} + +

+

+ + {currentUseCase.description} + +

+ +
+ {currentUseCase.metrics.map((metric, metricIdx) => ( +
+ + {metric.value} + + + + {metric.label} + + +
+ ))} +
+ + +
+
+ +
+ + + +
+ {useCases.map((_, index) => ( +
setActiveUseCase(index)} + className={`w-2 h-2 rounded-full cursor-pointer transition-all duration-300 ${ + index === activeUseCase ? "bg-[var(--ifm-color-primary)]" : "bg-gray-400 dark:bg-white/40 hover:bg-gray-500 dark:hover:bg-white/60" + }`} + /> + ))} +
+ + + +
+
+
+
+
+ ); +} diff --git a/src/components/GetStarted/index.tsx b/src/components/GetStarted/index.tsx new file mode 100644 index 00000000..4955968a --- /dev/null +++ b/src/components/GetStarted/index.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import Link from "@docusaurus/Link"; +import Translate from "@docusaurus/Translate"; +import HeroBackground from "@site/src/components/HeroBackground"; + +export default function GetStarted() { + return ( + +
+
+

+ + Get Started in 3 Simple Steps: + +

+ +
+
+ 1.{" "} + + + Download + + {" "} + + ONE WARE Studio with the ONE AI Extension. + +
+
+ 2.{" "} + + + Sign up + + + + , and verify your account. + +
+
+ 3.{" "} + + Build your first AI models for free. + +
+
+ +
+ + Quick Start Guide + +
+ +
+

+ Or{" "} + + Contact sales + {" "} + to request a docker container for local AI training +

+
+
+
+
+ ); +} diff --git a/src/components/HeroBackground/index.tsx b/src/components/HeroBackground/index.tsx index a4a40bd5..f1ea7660 100644 --- a/src/components/HeroBackground/index.tsx +++ b/src/components/HeroBackground/index.tsx @@ -105,7 +105,7 @@ export default function HeroBackground({ }} /> -
+
{children}
diff --git a/src/components/HomeHero/NeuralNetwork/Neural3DNetwork.tsx b/src/components/HomeHero/NeuralNetwork/Neural3DNetwork.tsx index 63402979..9380b358 100644 --- a/src/components/HomeHero/NeuralNetwork/Neural3DNetwork.tsx +++ b/src/components/HomeHero/NeuralNetwork/Neural3DNetwork.tsx @@ -41,6 +41,10 @@ interface Neural3DNetworkProps { trainingNodeCount?: number; physicsUpdateInterval?: number; performanceTier?: 'high' | 'low'; + maxNodes?: number; + maxTrainingNodes?: number; + skipTrainingCleanup?: boolean; + enableRotation?: boolean; } export const Neural3DNetwork = memo(function Neural3DNetwork({ @@ -59,8 +63,20 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ onRebuildComplete, currentFps = 50, isSmallScreen: isSmallScreenProp, - performanceTier = 'high' + performanceTier = 'high', + maxNodes, + maxTrainingNodes, + skipTrainingCleanup = false, + enableRotation, }: Neural3DNetworkProps) { + const nodeCounts = useMemo(() => { + const defaults = NODE_COUNTS[performanceTier]; + return { + base: maxNodes != null ? Math.min(maxNodes, defaults.base) : defaults.base, + training: maxTrainingNodes != null ? maxTrainingNodes : maxNodes != null ? Math.min(maxNodes, defaults.training) : defaults.training, + }; + }, [performanceTier, maxNodes, maxTrainingNodes]); + const groupRef = useRef(null); const connectionPairsRef = useRef>([]); @@ -108,7 +124,10 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ useEffect(() => { if (isCollapsingToCore) { + setOrangeNodesAdded(false); + setPurpleNodesAdded(false); setRedNodesAdded(false); + hasTriggeredTrainingComplete.current = false; } }, [isCollapsingToCore]); @@ -139,6 +158,15 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ const performanceTierRef = useRef(performanceTier); performanceTierRef.current = performanceTier; + const nodeCountsRef = useRef(nodeCounts); + nodeCountsRef.current = nodeCounts; + + const skipTrainingCleanupRef = useRef(skipTrainingCleanup); + skipTrainingCleanupRef.current = skipTrainingCleanup; + + const enableRotationRef = useRef(enableRotation); + enableRotationRef.current = enableRotation; + useEffect(() => { if (blueprintRef.current.length > 0) return; @@ -158,7 +186,7 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ scale: 0, }); - const totalNodes = NODE_COUNTS[performanceTier].base; + const totalNodes = nodeCounts.base; const radius = 3.5; const goldenRatio = (1 + Math.sqrt(5)) / 2; @@ -278,20 +306,22 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ isRebuildingBaseRef.current = true; rebuildBaseStartTimeRef.current = performance.now(); hasTriggeredRebuildComplete.current = false; - rebuildBuiltCountRef.current = 0; + rebuildBuiltCountRef.current = 1; - const currentNodes = nodesRef.current; - for (let i = 0; i < currentNodes.length; i++) { - currentNodes[i].scale = i === 0 ? 1 : 0; - } + // Clear all connections and pairs — animation will rebuild from scratch + connectionPairsRef.current = []; + connectionIdCounterRef.current = 0; + animationOffsetsRef.current.clear(); - const baseNodes: NeuralNode[] = []; for (let i = 0; i < blueprintRef.current.length; i++) { - if (blueprintRef.current[i].color === '#00FFD1') { - baseNodes.push(blueprintRef.current[i]); - } + blueprintRef.current[i].connections = []; + blueprintRef.current[i].scale = 0; } - nodesRef.current = baseNodes; + + // Start with only the core node + const core = blueprintRef.current[0]; + core.scale = 1; + nodesRef.current = [core]; } else if (!isRebuildingBase) { isRebuildingBaseRef.current = false; @@ -366,7 +396,8 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ const delta = Math.min(state.clock.getDelta(), 0.1); if (groupRef.current) { - if (performanceTierRef.current !== 'low') { + const shouldRotate = enableRotationRef.current ?? (performanceTierRef.current !== 'low'); + if (shouldRotate) { groupRef.current.rotation.y = time * 0.18; } let targetY = isExpanded ? 0.8 : 0; @@ -447,7 +478,7 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ const elapsed = performance.now() - buildBaseStartTimeRef.current; const progress = Math.min(elapsed / duration, 1); - const tierNodeCount = NODE_COUNTS[performanceTierRef.current].base + 1; + const tierNodeCount = nodeCountsRef.current.base + 1; const totalBlueprint = Math.min(tierNodeCount, blueprintRef.current.length); const targetCount = Math.max(1, Math.ceil(progress * totalBlueprint)); @@ -580,7 +611,7 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ const elapsed = performance.now() - rebuildBaseStartTimeRef.current; const progress = Math.min(elapsed / duration, 1); - const tierNodeCount = NODE_COUNTS[performanceTierRef.current].base + 1; + const tierNodeCount = nodeCountsRef.current.base + 1; const totalBlueprint = Math.min(tierNodeCount, blueprintRef.current.length); const targetCount = Math.max(1, Math.ceil(progress * totalBlueprint)); @@ -632,8 +663,9 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ setOrangeNodesAdded(true); isTrainingRef.current = true; trainingStartTimeRef.current = performance.now(); + hasTriggeredTrainingComplete.current = false; - const totalNew = NODE_COUNTS[performanceTier].training; + const totalNew = nodeCounts.training; const radius = 4.5; for (let i = 0; i < totalNew; i++) { @@ -674,6 +706,7 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ trainingColorRef.current = 'purple'; hasTriggeredTrainingComplete.current = false; + if (!skipTrainingCleanupRef.current) { const baseNodeIds = new Set(); let writeIdx = 0; for (let i = 0; i < nodesRef.current.length; i++) { @@ -721,8 +754,9 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ } node.connections.length = connWriteIdx; } + } - const totalNew = NODE_COUNTS[performanceTier].training; + const totalNew = nodeCounts.training; const radius = 4.5; for (let i = 0; i < totalNew; i++) { @@ -762,6 +796,7 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ trainingColorRef.current = 'red'; hasTriggeredTrainingComplete.current = false; + if (!skipTrainingCleanupRef.current) { const baseNodeIds = new Set(); let writeIdx = 0; for (let i = 0; i < nodesRef.current.length; i++) { @@ -809,8 +844,9 @@ export const Neural3DNetwork = memo(function Neural3DNetwork({ } node.connections.length = connWriteIdx; } + } - const totalNew = NODE_COUNTS[performanceTier].training; + const totalNew = nodeCounts.training; const radius = 4.5; for (let i = 0; i < totalNew; i++) { diff --git a/src/components/HomeHero/SoftwareWindow/Neural3DCanvas.tsx b/src/components/HomeHero/SoftwareWindow/Neural3DCanvas.tsx index f29f05c7..e8d3154d 100644 --- a/src/components/HomeHero/SoftwareWindow/Neural3DCanvas.tsx +++ b/src/components/HomeHero/SoftwareWindow/Neural3DCanvas.tsx @@ -19,6 +19,10 @@ interface Neural3DCanvasProps { currentFps: number; isSmallScreen: boolean; performanceTier: PerformanceTier; + maxNodes?: number; + maxTrainingNodes?: number; + skipTrainingCleanup?: boolean; + enableRotation?: boolean; } export default function Neural3DCanvas({ @@ -36,6 +40,10 @@ export default function Neural3DCanvas({ currentFps, isSmallScreen, performanceTier, + maxNodes, + maxTrainingNodes, + skipTrainingCleanup, + enableRotation, }: Neural3DCanvasProps) { return ( - - diff --git a/src/components/HomeHero/index.tsx b/src/components/HomeHero/index.tsx index 0fb0e051..b9cdee8d 100644 --- a/src/components/HomeHero/index.tsx +++ b/src/components/HomeHero/index.tsx @@ -199,7 +199,7 @@ export default function HomeHero() { return (
@@ -390,6 +391,38 @@ export default function HomeHero() {
+ +
document.getElementById("video")?.scrollIntoView({ behavior: "smooth" })} + > + + Scroll dummy text + +
+
+
+
+
+ ); +} diff --git a/src/components/ChatHero/index.tsx b/src/components/ChatHero/index.tsx new file mode 100644 index 00000000..89c699d6 --- /dev/null +++ b/src/components/ChatHero/index.tsx @@ -0,0 +1,263 @@ +import React, { useState, useEffect } from "react"; +import Translate from "@docusaurus/Translate"; +import { useColorMode } from "@docusaurus/theme-common"; +import SignUpModal, { ModalTrigger } from "./SignUpModal"; +import HeroBackground from "../HeroBackground"; + +const STORAGE_KEY = "chathero_prompt"; +const STORAGE_EXPIRY_DAYS = 90; + +function getStoredPrompt(): string { + if (typeof window === "undefined") return ""; + try { + const stored = localStorage.getItem(STORAGE_KEY); + if (!stored) return ""; + const { value, expiry } = JSON.parse(stored); + if (Date.now() > expiry) { + localStorage.removeItem(STORAGE_KEY); + return ""; + } + return value || ""; + } catch { + return ""; + } +} + +function savePrompt(value: string) { + if (typeof window === "undefined") return; + try { + const expiry = Date.now() + STORAGE_EXPIRY_DAYS * 24 * 60 * 60 * 1000; + localStorage.setItem(STORAGE_KEY, JSON.stringify({ value, expiry })); + } catch {} +} + +export default function ChatHero() { + const { colorMode } = useColorMode(); + const isDarkMode = colorMode === "dark"; + const [isModalOpen, setIsModalOpen] = useState(false); + const [modalTrigger, setModalTrigger] = useState("send"); + const [prompt, setPrompt] = useState(""); + + useEffect(() => { + setPrompt(getStoredPrompt()); + }, []); + + const handlePromptChange = (e: React.ChangeEvent) => { + const value = e.target.value; + setPrompt(value); + savePrompt(value); + }; + + const handleAttachClick = (e: React.MouseEvent) => { + e.stopPropagation(); + setModalTrigger("attach"); + setIsModalOpen(true); + }; + + const handleSendClick = (e: React.MouseEvent) => { + e.stopPropagation(); + setModalTrigger("send"); + setIsModalOpen(true); + }; + + const scrollToSection = (id: string) => { + const element = document.getElementById(id); + if (element) { + const navbarHeight = 60; + const elementPosition = element.getBoundingClientRect().top + window.scrollY; + window.scrollTo({ + top: elementPosition - navbarHeight, + behavior: "smooth", + }); + } + }; + + return ( + +
+
+

+ Create your Custom AI +

+ +

+ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore. + +

+ +
+
+
+