Oblivion
Oblivion
Docs
Go back
Component by
OV
Oblivion
Create ImageData AnalysisResearch
TSX
ai-narrative-input
"use client"

import React, { useState } from "react"
import { motion, AnimatePresence } from "framer-motion"
import { Paperclip, Globe, Plus, Send, Zap } from "lucide-react"
import { cn } from "@/lib/utils"



export const componentMeta = {
  name: "AI Narrative Input",
  description: "A premium, physics-based AI prompt input with magnetic actions and fluid glassmorphism.",
  props: {
    placeholder: { type: "string", description: "The text area placeholder", default: '"Ask me anything..."' },
    onSend: { type: "function", description: "Callback when the prompt is submitted" }
  }
}

export default function AiNarrativeInput({
  placeholder = "Ask me anything...",
  onSend
}: {
  placeholder?: string
  onSend?: (prompt: string) => void
}) {
  const [prompt, setPrompt] = useState("")
  const [isFocused, setIsFocused] = useState(false)

  const handleSend = () => {
    if (prompt.trim()) {
      onSend?.(prompt)
      setPrompt("")
    }
  }

  return (
    <div className="flex flex-col w-full max-w-sm gap-4 font-sans antialiased">
      {/* Container with Glassmorphism Border */}
      <div 
        className={cn(
          "relative group p-[1.5px] rounded-2xl transition-all duration-500",
          isFocused 
            ? "bg-gradient-to-br from-zinc-400 via-zinc-600 to-zinc-400 shadow-[0_0_20px_rgba(255,255,255,0.1)]" 
            : "bg-gradient-to-br from-zinc-600 to-zinc-900 shadow-xl"
        )}
      >
        {/* Animated Corner Light */}
        <div className="absolute top-[-10px] left-[-10px] w-12 h-12 bg-white/20 blur-xl pointer-events-none rounded-full" />

        <div className="relative flex flex-col bg-black/90 backdrop-blur-xl rounded-[15px] overflow-hidden border border-white/5">
          
          <textarea
            value={prompt}
            onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setPrompt(e.target.value)}
            onFocus={() => setIsFocused(true)}
            onBlur={() => setIsFocused(false)}
            placeholder={placeholder}
            className="w-full h-24 bg-transparent border-none text-white text-sm p-4 resize-none outline-none placeholder:text-zinc-500 placeholder:transition-colors focus:placeholder:text-zinc-700 transition-all duration-300"
          />

          <div className="flex items-center justify-between p-3 pt-0">
            {/* Tool Buttons */}
            <div className="flex gap-2">
              {[Paperclip, Plus, Globe].map((Icon, i) => (
                <motion.button
                  key={i}
                  whileHover={{ y: -3, color: "#fff" }}
                  whileTap={{ scale: 0.9 }}
                  className="p-2 text-zinc-600 transition-colors"
                >
                  <Icon size={18} />
                </motion.button>
              ))}
            </div>

            {/* Submit Button */}
            <motion.button
              onClick={handleSend}
              whileHover={{ scale: 1.05 }}
              whileTap={{ scale: 0.95 }}
              className={cn(
                "group relative flex items-center justify-center w-10 h-10 rounded-xl bg-zinc-800 border border-white/10 transition-all duration-300",
                prompt ? "bg-white text-black shadow-[0_0_15px_rgba(255,255,255,0.3)]" : "text-zinc-600"
              )}
            >
              <AnimatePresence mode="wait">
                <motion.div
                  key={prompt ? "send" : "zap"}
                  initial={{ opacity: 0, scale: 0.5, rotate: -45 }}
                  animate={{ opacity: 1, scale: 1, rotate: 0 }}
                  exit={{ opacity: 0, scale: 0.5, rotate: 45 }}
                  transition={{ duration: 0.2 }}
                >
                  {prompt ? <Send size={18} /> : <Zap size={18} />}
                </motion.div>
              </AnimatePresence>
            </motion.button>
          </div>
        </div>
      </div>

      {/* Tags section - Refined Oblivion Style */}
      <div className="flex flex-wrap gap-2 px-1">
        {["Create Image", "Data Analysis", "Research"].map((tag) => (
          <motion.span
            key={tag}
            whileHover={{ 
              scale: 1.05, 
              backgroundColor: "#222", 
              borderColor: "#444" 
            }}
            className="px-3 py-1 text-[10px] font-bold uppercase tracking-wider text-zinc-400 bg-zinc-900 border border-zinc-800 rounded-lg cursor-pointer transition-all active:scale-95"
          >
            {tag}
          </motion.span>
        ))}
      </div>
    </div>
  )
}
Save to favoritesCopy to Figma
React

AI Narrative Input

A premium, physics-based AI prompt input with magnetic actions and fluid glassmorphism.

Installation

$ npx @cosmoo/oblivion add ai-narrative-input
Dependenciesnpm install framer-motion lucide-react

Usage Example

tsx
import AiNarrativeInput from "@/components/oblivion/ai-narrative-input"

export default function Demo() {
  return (
    <div className="p-10 flex justify-center">
      <AiNarrativeInput />
    </div>
  )
}