mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-09 03:18:04 +00:00
feat: deep-research on user profile and tiptap integration (#672)
deep-research on user profile add novel integration tiptap 3.x integration
This commit is contained in:
parent
641db19e35
commit
59c294b299
59 changed files with 1805 additions and 365 deletions
66
.github/workflows/claude-code-review.yml
vendored
66
.github/workflows/claude-code-review.yml
vendored
|
|
@ -3,21 +3,9 @@ name: Claude Code Review
|
|||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
# Optional: Only run on specific file changes
|
||||
# paths:
|
||||
# - "src/**/*.ts"
|
||||
# - "src/**/*.tsx"
|
||||
# - "src/**/*.js"
|
||||
# - "src/**/*.jsx"
|
||||
|
||||
jobs:
|
||||
claude-review:
|
||||
# Optional: Filter by PR author
|
||||
# if: |
|
||||
# github.event.pull_request.user.login == 'external-contributor' ||
|
||||
# github.event.pull_request.user.login == 'new-developer' ||
|
||||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
@ -38,17 +26,53 @@ jobs:
|
|||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
use_sticky_comment: true
|
||||
prompt: |
|
||||
Please review this pull request and provide feedback on:
|
||||
- Code quality and best practices
|
||||
- Potential bugs or issues
|
||||
- Performance considerations
|
||||
- Security concerns
|
||||
- Test coverage
|
||||
You are reviewing a PR for supermemory - a Turbo monorepo with multiple apps and packages.
|
||||
|
||||
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
|
||||
## Repository Structure Context
|
||||
|
||||
**Apps (apps/):**
|
||||
- `web` - Next.js web application (no tests)
|
||||
- `mcp` - Model Context Protocol server on Cloudflare Workers (no tests)
|
||||
- `browser-extension` - WXT-based browser extension (no tests)
|
||||
- `raycast-extension` - Raycast app extension (no tests)
|
||||
- `docs` - Mintlify documentation site
|
||||
|
||||
**Published Packages (packages/) - with tests:**
|
||||
- `tools` - AI SDK memory tools (Vitest)
|
||||
- `ai-sdk` - supermemory AI SDK wrapper (Vitest)
|
||||
- `openai-sdk-python` - Python OpenAI integration (pytest)
|
||||
- `pipecat-sdk-python` - Python Pipecat integration (pytest)
|
||||
|
||||
**Internal Packages (packages/) - no tests:**
|
||||
- `ui` - Shared React/Radix UI components
|
||||
- `lib` - Shared utilities
|
||||
- `hooks` - Custom React hooks
|
||||
- `memory-graph` - D3-based graph visualization
|
||||
- `validation` - Zod schemas
|
||||
|
||||
## Review Instructions
|
||||
|
||||
1. First, run `gh pr diff` to see what files changed
|
||||
|
||||
2. Based on the changes, review for:
|
||||
- **Code quality**: Follow Biome linting rules (double quotes, tabs, no default exports)
|
||||
- **Type safety**: Ensure proper TypeScript usage
|
||||
- **Security**: Check for injection vulnerabilities, credential exposure, unsafe patterns
|
||||
- **Performance**: Look for unnecessary re-renders (React), N+1 queries, memory leaks
|
||||
|
||||
3. **Test coverage** - ONLY review if changes touch these packages:
|
||||
- `packages/tools/**` or `packages/ai-sdk/**` → Check for Vitest tests
|
||||
- `packages/openai-sdk-python/**` or `packages/pipecat-sdk-python/**` → Check for pytest tests
|
||||
- Skip test coverage review for apps and other packages (they have no test setup)
|
||||
|
||||
4. **Package-specific concerns:**
|
||||
- Published packages (tools, ai-sdk, memory-graph, *-python): Check for breaking API changes
|
||||
- UI package: Check accessibility (a11y) and component API consistency
|
||||
- Web app: Check for proper data fetching patterns (TanStack Query), Zustand state management
|
||||
- MCP server: Check Hono routing and Cloudflare Workers compatibility
|
||||
|
||||
5. Be concise and actionable. Focus on issues that matter, not style nitpicks (Biome handles that).
|
||||
|
||||
Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
|
||||
|
||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||
# or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line for available options
|
||||
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
NEXT_PUBLIC_BACKEND_URL=https://api.supermemory.ai
|
||||
NEXT_PUBLIC_POSTHOG_KEY=
|
||||
NEXT_PUBLIC_POSTHOG_KEY=
|
||||
EXA_API_KEY=
|
||||
XAI_API_KEY=
|
||||
|
|
@ -14,7 +14,7 @@ import { InitialHeader } from "@/components/initial-header"
|
|||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useState, useEffect } from "react"
|
||||
import { motion } from "motion/react"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { Logo } from "@ui/assets/Logo"
|
||||
|
||||
|
|
@ -30,7 +30,11 @@ function AnimatedGradientBackground() {
|
|||
}}
|
||||
transition={{
|
||||
y: { duration: 0.75, ease: "easeOut" },
|
||||
opacity: { duration: 8, repeat: Number.POSITIVE_INFINITY, ease: "easeInOut" },
|
||||
opacity: {
|
||||
duration: 8,
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
ease: "easeInOut",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<motion.div
|
||||
|
|
@ -42,7 +46,11 @@ function AnimatedGradientBackground() {
|
|||
}}
|
||||
transition={{
|
||||
y: { duration: 0.75, ease: "easeOut" },
|
||||
opacity: { duration: 8, repeat: Number.POSITIVE_INFINITY, ease: "easeInOut" },
|
||||
opacity: {
|
||||
duration: 8,
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
ease: "easeInOut",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<motion.div
|
||||
|
|
|
|||
81
apps/web/app/api/onboarding/research/route.ts
Normal file
81
apps/web/app/api/onboarding/research/route.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { xai } from "@ai-sdk/xai"
|
||||
import { generateText } from "ai"
|
||||
|
||||
interface ResearchRequest {
|
||||
xUrl: string
|
||||
name?: string
|
||||
email?: string
|
||||
}
|
||||
|
||||
// prompt to get user context from X/Twitter profile
|
||||
function finalPrompt(xUrl: string, userContext: string) {
|
||||
return `You are researching a user based on their X/Twitter profile to help personalize their experience.
|
||||
|
||||
X/Twitter Profile URL: ${xUrl}${userContext}
|
||||
|
||||
Please analyze this X/Twitter profile and provide a comprehensive but concise summary of the user. Include:
|
||||
- Professional background and current role (if available)
|
||||
- Key interests and topics they engage with
|
||||
- Notable projects, achievements, or affiliations
|
||||
- Their expertise areas
|
||||
- Any other relevant information that helps understand who they are
|
||||
|
||||
Format the response as clear, readable paragraphs. Focus on factual information from their profile. If certain information is not available, skip that section rather than speculating.`
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const { xUrl, name, email }: ResearchRequest = await req.json()
|
||||
|
||||
if (!xUrl?.trim()) {
|
||||
return Response.json(
|
||||
{ error: "X/Twitter URL is required" },
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
|
||||
const lowerUrl = xUrl.toLowerCase()
|
||||
if (!lowerUrl.includes("x.com") && !lowerUrl.includes("twitter.com")) {
|
||||
return Response.json(
|
||||
{ error: "URL must be an X/Twitter profile link" },
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
|
||||
const contextParts: string[] = []
|
||||
if (name) contextParts.push(`Name: ${name}`)
|
||||
if (email) contextParts.push(`Email: ${email}`)
|
||||
const userContext =
|
||||
contextParts.length > 0
|
||||
? `\n\nAdditional context about the user:\n${contextParts.join("\n")}`
|
||||
: ""
|
||||
|
||||
const { text } = await generateText({
|
||||
model: xai("grok-4-1-fast-reasoning"),
|
||||
prompt: finalPrompt(xUrl, userContext),
|
||||
providerOptions: {
|
||||
xai: {
|
||||
searchParameters: {
|
||||
mode: "on",
|
||||
sources: [
|
||||
{
|
||||
type: "web",
|
||||
safeSearch: true,
|
||||
},
|
||||
{
|
||||
type: "x",
|
||||
includedXHandles: [lowerUrl.replace("https://x.com/", "").replace("https://twitter.com/", "")],
|
||||
postFavoriteCount: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return Response.json({ text })
|
||||
} catch (error) {
|
||||
console.error("Research API error:", error)
|
||||
return Response.json({ error: "Internal server error" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
|
@ -6,18 +6,18 @@ import { useState, useEffect } from "react"
|
|||
import { useAuth } from "@lib/auth-context"
|
||||
import { cn } from "@lib/utils"
|
||||
|
||||
import { InputStep } from "./welcome/input-step"
|
||||
import { GreetingStep } from "./welcome/greeting-step"
|
||||
import { WelcomeStep } from "./welcome/welcome-step"
|
||||
import { ContinueStep } from "./welcome/continue-step"
|
||||
import { FeaturesStep } from "./welcome/features-step"
|
||||
import { MemoriesStep } from "./welcome/memories-step"
|
||||
import { RelatableQuestion } from "./setup/relatable-question"
|
||||
import { IntegrationsStep } from "./setup/integrations-step"
|
||||
import { InputStep } from "../../../components/new/onboarding/welcome/input-step"
|
||||
import { GreetingStep } from "../../../components/new/onboarding/welcome/greeting-step"
|
||||
import { WelcomeStep } from "../../../components/new/onboarding/welcome/welcome-step"
|
||||
import { ContinueStep } from "../../../components/new/onboarding/welcome/continue-step"
|
||||
import { FeaturesStep } from "../../../components/new/onboarding/welcome/features-step"
|
||||
import { ProfileStep } from "../../../components/new/onboarding/welcome/profile-step"
|
||||
import { RelatableQuestion } from "../../../components/new/onboarding/setup/relatable-question"
|
||||
import { IntegrationsStep } from "../../../components/new/onboarding/setup/integrations-step"
|
||||
|
||||
import { InitialHeader } from "@/components/initial-header"
|
||||
import { SetupHeader } from "./setup/header"
|
||||
import { ChatSidebar } from "./setup/chat-sidebar"
|
||||
import { SetupHeader } from "../../../components/new/onboarding/setup/header"
|
||||
import { ChatSidebar } from "../../../components/new/onboarding/setup/chat-sidebar"
|
||||
import { Logo } from "@ui/assets/Logo"
|
||||
import NovaOrb from "@/components/nova/nova-orb"
|
||||
import { AnimatedGradientBackground } from "@/components/new/animated-gradient-background"
|
||||
|
|
@ -152,7 +152,7 @@ export default function OnboardingPage() {
|
|||
case "features":
|
||||
return <FeaturesStep key="features" />
|
||||
case "memories":
|
||||
return <MemoriesStep key="memories" onSubmit={setMemoryFormData} />
|
||||
return <ProfileStep key="profile" onSubmit={setMemoryFormData} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { motion } from "motion/react"
|
|||
import NovaOrb from "@/components/nova/nova-orb"
|
||||
import { useState, useEffect, useRef } from "react"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import Account from "@/components/new/settings/account"
|
||||
import Integrations from "@/components/new/settings/integrations"
|
||||
import ConnectionsMCP from "@/components/new/settings/connections-mcp"
|
||||
|
|
@ -175,7 +175,11 @@ export default function SettingsPage() {
|
|||
return (
|
||||
<div className="h-screen flex flex-col overflow-hidden">
|
||||
<header className="flex justify-between items-center px-6 py-3 shrink-0">
|
||||
<button type="button" onClick={() => router.push("/new")} className="cursor-pointer">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push("/new")}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Logo className="h-7" />
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { Check, Loader, Trash2, Zap } from "lucide-react"
|
|||
import { useEffect, useState } from "react"
|
||||
import { toast } from "sonner"
|
||||
import type { z } from "zod"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { Button } from "@ui/components/button"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useState, useEffect } from "react"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { FileIcon } from "lucide-react"
|
||||
import { useHotkeys } from "react-hotkeys-hook"
|
||||
|
||||
|
|
@ -19,7 +19,12 @@ interface FileContentProps {
|
|||
isOpen?: boolean
|
||||
}
|
||||
|
||||
export function FileContent({ onSubmit, onDataChange, isSubmitting, isOpen }: FileContentProps) {
|
||||
export function FileContent({
|
||||
onSubmit,
|
||||
onDataChange,
|
||||
isSubmitting,
|
||||
isOpen,
|
||||
}: FileContentProps) {
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||||
const [title, setTitle] = useState("")
|
||||
|
|
@ -33,8 +38,16 @@ export function FileContent({ onSubmit, onDataChange, isSubmitting, isOpen }: Fi
|
|||
}
|
||||
}
|
||||
|
||||
const updateData = (newFile: File | null, newTitle: string, newDescription: string) => {
|
||||
onDataChange?.({ file: newFile, title: newTitle, description: newDescription })
|
||||
const updateData = (
|
||||
newFile: File | null,
|
||||
newTitle: string,
|
||||
newDescription: string,
|
||||
) => {
|
||||
onDataChange?.({
|
||||
file: newFile,
|
||||
title: newTitle,
|
||||
description: newDescription,
|
||||
})
|
||||
}
|
||||
|
||||
const handleFileChange = (file: File | null) => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState, useEffect, useMemo, useCallback } from "react"
|
||||
import { Dialog, DialogContent, DialogTitle } from "@repo/ui/components/dialog"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import {
|
||||
FileTextIcon,
|
||||
GlobeIcon,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState, useEffect } from "react"
|
||||
import { cn } from "@lib/utils"
|
||||
import { Button } from "@ui/components/button"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { useHotkeys } from "react-hotkeys-hook"
|
||||
import { Image as ImageIcon, Loader2 } from "lucide-react"
|
||||
import { toast } from "sonner"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { useHotkeys } from "react-hotkeys-hook"
|
||||
import { TextEditor } from "../text-editor"
|
||||
|
||||
interface NoteContentProps {
|
||||
onSubmit?: (content: string) => void
|
||||
|
|
@ -31,11 +31,6 @@ export function NoteContent({
|
|||
onContentChange?.(newContent)
|
||||
}
|
||||
|
||||
useHotkeys("mod+enter", handleSubmit, {
|
||||
enabled: isOpen && canSubmit,
|
||||
enableOnFormTags: ["TEXTAREA"],
|
||||
})
|
||||
|
||||
// Reset content when modal closes
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
|
|
@ -45,12 +40,12 @@ export function NoteContent({
|
|||
}, [isOpen, onContentChange])
|
||||
|
||||
return (
|
||||
<textarea
|
||||
value={content}
|
||||
onChange={(e) => handleContentChange(e.target.value)}
|
||||
placeholder="Write your note here..."
|
||||
disabled={isSubmitting}
|
||||
className="w-full h-full p-4 mb-4! rounded-[14px] bg-[#14161A] shadow-inside-out resize-none disabled:opacity-50 outline-none"
|
||||
/>
|
||||
<div className="p-4 overflow-y-auto flex-1 w-full h-full mb-4! bg-[#14161A] shadow-inside-out rounded-[14px]">
|
||||
<TextEditor
|
||||
content={undefined}
|
||||
onContentChange={handleContentChange}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
SquarePenIcon,
|
||||
} from "lucide-react"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import ChatInput from "./input"
|
||||
import ChatModelSelector from "./model-selector"
|
||||
import { GradientLogo, LogoBgGradient } from "@ui/assets/Logo"
|
||||
|
|
@ -110,7 +110,6 @@ export function ChatSidebar({
|
|||
},
|
||||
},
|
||||
}),
|
||||
maxSteps: 10,
|
||||
onFinish: async (result) => {
|
||||
if (result.message.role !== "assistant") return
|
||||
|
||||
|
|
@ -285,14 +284,20 @@ export function ChatSidebar({
|
|||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const activeElement = document.activeElement as HTMLElement | null
|
||||
const isInEditableContext =
|
||||
activeElement?.tagName === "INPUT" ||
|
||||
activeElement?.tagName === "TEXTAREA" ||
|
||||
activeElement?.isContentEditable ||
|
||||
activeElement?.closest('[contenteditable="true"]')
|
||||
|
||||
if (
|
||||
e.key.toLowerCase() === "t" &&
|
||||
!e.metaKey &&
|
||||
!e.ctrlKey &&
|
||||
!e.altKey &&
|
||||
isChatOpen &&
|
||||
document.activeElement?.tagName !== "INPUT" &&
|
||||
document.activeElement?.tagName !== "TEXTAREA"
|
||||
!isInEditableContext
|
||||
) {
|
||||
e.preventDefault()
|
||||
handleNewChat()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useAuth } from "@lib/auth-context"
|
|||
import { Avatar, AvatarFallback, AvatarImage } from "@ui/components/avatar"
|
||||
import type { UIMessage } from "@ai-sdk/react"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
|
||||
interface MemoryResult {
|
||||
documentId?: string
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { ChevronUpIcon } from "lucide-react"
|
||||
import NovaOrb from "@/components/nova/nova-orb"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { useRef, useState } from "react"
|
||||
import { motion } from "motion/react"
|
||||
import { SendButton, StopButton } from "./actions"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
||||
import type { UIMessage } from "@ai-sdk/react"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
|
||||
interface MemoryResult {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState } from "react"
|
||||
import { cn } from "@lib/utils"
|
||||
import { Button } from "@ui/components/button"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
import { models, type ModelId, modelNames } from "@/lib/models"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState } from "react"
|
||||
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
|
||||
import type { z } from "zod"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { PDF } from "@ui/assets/icons"
|
||||
import { FileText, Image, Video } from "lucide-react"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
|
||||
import type { z } from "zod"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
|
||||
type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
|
||||
import type { z } from "zod"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { ClaudeDesktopIcon, MCPIcon } from "@ui/assets/icons"
|
||||
|
||||
|
|
@ -13,11 +13,16 @@ export function McpPreview({ document }: { document: DocumentWithMemories }) {
|
|||
return (
|
||||
<div className="bg-[#0B1017] p-3 rounded-[18px] space-y-2">
|
||||
<div className="flex items-center justify-between gap-1">
|
||||
<p className={cn(dmSansClassName(), "text-[12px] font-semibold flex items-center gap-1")}>
|
||||
<ClaudeDesktopIcon className="size-3" />
|
||||
<p
|
||||
className={cn(
|
||||
dmSansClassName(),
|
||||
"text-[12px] font-semibold flex items-center gap-1",
|
||||
)}
|
||||
>
|
||||
<ClaudeDesktopIcon className="size-3" />
|
||||
Claude Desktop
|
||||
</p>
|
||||
<MCPIcon className="size-6" />
|
||||
<MCPIcon className="size-6" />
|
||||
</div>
|
||||
<div className="space-y-[6px]">
|
||||
{document.title && (
|
||||
|
|
|
|||
|
|
@ -2,12 +2,54 @@
|
|||
|
||||
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
|
||||
import type { z } from "zod"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { useMemo } from "react"
|
||||
|
||||
type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema>
|
||||
type DocumentWithMemories = DocumentsResponse["documents"][0]
|
||||
|
||||
type TipTapNode = {
|
||||
type: string
|
||||
text?: string
|
||||
content?: TipTapNode[]
|
||||
attrs?: Record<string, unknown>
|
||||
}
|
||||
|
||||
function extractTextFromTipTapContent(content: string): string {
|
||||
try {
|
||||
const json = JSON.parse(content) as TipTapNode
|
||||
return extractTextFromNode(json)
|
||||
} catch {
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
function extractTextFromNode(node: TipTapNode): string {
|
||||
if (node.type === "text" && node.text) {
|
||||
return node.text
|
||||
}
|
||||
|
||||
if (!node.content) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const texts: string[] = []
|
||||
for (const child of node.content) {
|
||||
const text = extractTextFromNode(child)
|
||||
if (text) {
|
||||
texts.push(text)
|
||||
}
|
||||
}
|
||||
|
||||
const blockTypes = ["paragraph", "heading", "listItem", "blockquote", "codeBlock"]
|
||||
if (blockTypes.includes(node.type)) {
|
||||
return `${texts.join("")}\n`
|
||||
}
|
||||
|
||||
return texts.join("")
|
||||
}
|
||||
|
||||
function NoteIcon() {
|
||||
return (
|
||||
<svg
|
||||
|
|
@ -79,6 +121,11 @@ function NoteIcon() {
|
|||
}
|
||||
|
||||
export function NotePreview({ document }: { document: DocumentWithMemories }) {
|
||||
const previewText = useMemo(() => {
|
||||
if (!document.content) return ""
|
||||
return extractTextFromTipTapContent(document.content).trim()
|
||||
}, [document.content])
|
||||
|
||||
return (
|
||||
<div className="bg-[#0B1017] p-3 rounded-[18px] space-y-2">
|
||||
<div className="flex items-center gap-1">
|
||||
|
|
@ -98,9 +145,9 @@ export function NotePreview({ document }: { document: DocumentWithMemories }) {
|
|||
{document.title}
|
||||
</p>
|
||||
)}
|
||||
{document.content && (
|
||||
{previewText && (
|
||||
<p className="text-[10px] text-[#737373] line-clamp-4">
|
||||
{document.content}
|
||||
{previewText}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
TweetSkeleton,
|
||||
} from "react-tweet"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
|
||||
export function TweetPreview({
|
||||
data,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState } from "react"
|
||||
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
|
||||
import type { z } from "zod"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
|
||||
type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
|
||||
import type { z } from "zod"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { extractYouTubeVideoId } from "../utils"
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ const PDFIcon = ({ className }: { className: string }) => {
|
|||
width="8.25216"
|
||||
height="10.2522"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend
|
||||
|
|
@ -158,7 +158,7 @@ const TextDocumentIcon = ({ className }: { className: string }) => {
|
|||
width="18.0253"
|
||||
height="13.8376"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend
|
||||
|
|
|
|||
|
|
@ -2,19 +2,24 @@
|
|||
|
||||
import { Dialog, DialogContent, DialogTitle } from "@repo/ui/components/dialog"
|
||||
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
|
||||
import { ArrowUpRightIcon, XIcon } from "lucide-react"
|
||||
import { ArrowUpRightIcon, XIcon, Loader2 } from "lucide-react"
|
||||
import type { z } from "zod"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { cn } from "@lib/utils"
|
||||
import dynamic from "next/dynamic"
|
||||
import { Title } from "./title"
|
||||
import { Summary as DocumentSummary } from "./summary"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { GraphListMemories, type MemoryEntry } from "./graph-list-memories"
|
||||
import { YoutubeVideo } from "./content/yt-video"
|
||||
import { TweetContent } from "./content/tweet"
|
||||
import { isTwitterUrl } from "@/utils/url-helpers"
|
||||
import { isTwitterUrl } from "@/lib/url-helpers"
|
||||
import { NotionDoc } from "./content/notion-doc"
|
||||
import { TextEditor } from "../text-editor"
|
||||
import { useState, useEffect, useCallback, useMemo } from "react"
|
||||
import { motion, AnimatePresence } from "motion/react"
|
||||
import { Button } from "@repo/ui/components/button"
|
||||
import { useDocumentMutations } from "@/hooks/use-document-mutations"
|
||||
|
||||
// Dynamically importing to prevent DOMMatrix error
|
||||
const PdfViewer = dynamic(
|
||||
|
|
@ -43,7 +48,49 @@ export function DocumentModal({
|
|||
isOpen,
|
||||
onClose,
|
||||
}: DocumentModalProps) {
|
||||
console.log(_document)
|
||||
const { updateMutation } = useDocumentMutations()
|
||||
|
||||
const { initialEditorContent, initialEditorString } = useMemo(() => {
|
||||
const content = _document?.content as string | null | undefined
|
||||
return {
|
||||
initialEditorContent: content ?? undefined,
|
||||
initialEditorString: content ?? "",
|
||||
}
|
||||
}, [_document?.content])
|
||||
|
||||
const [draftContentString, setDraftContentString] =
|
||||
useState(initialEditorString)
|
||||
const [editorResetNonce, setEditorResetNonce] = useState(0)
|
||||
const [lastSavedContent, setLastSavedContent] = useState<string | null>(null)
|
||||
|
||||
const resetEditor = useCallback(() => {
|
||||
setDraftContentString(initialEditorString)
|
||||
setEditorResetNonce((n) => n + 1)
|
||||
setLastSavedContent(null)
|
||||
}, [initialEditorString])
|
||||
|
||||
useEffect(() => {
|
||||
setDraftContentString(initialEditorString)
|
||||
setEditorResetNonce((n) => n + 1)
|
||||
setLastSavedContent(null)
|
||||
}, [initialEditorString])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) resetEditor()
|
||||
}, [isOpen, resetEditor])
|
||||
|
||||
const hasUnsavedChanges =
|
||||
draftContentString !== initialEditorString &&
|
||||
draftContentString !== lastSavedContent
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
if (!_document?.id) return
|
||||
updateMutation.mutate(
|
||||
{ documentId: _document.id, content: draftContentString },
|
||||
{ onSuccess: (_data, variables) => setLastSavedContent(variables.content) },
|
||||
)
|
||||
}, [_document?.id, draftContentString, updateMutation])
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent
|
||||
|
|
@ -79,7 +126,7 @@ export function DocumentModal({
|
|||
</a>
|
||||
)}
|
||||
<DialogPrimitive.Close
|
||||
className="bg-[#0D121A] w-7 h-7 flex items-center justify-center focus:ring-ring rounded-full transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]"
|
||||
className="bg-[#0D121A] w-7 h-7 flex items-center justify-center focus:ring-ring rounded-full transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none cursor-pointer [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]"
|
||||
data-slot="dialog-close"
|
||||
>
|
||||
<XIcon stroke="#737373" />
|
||||
|
|
@ -91,7 +138,7 @@ export function DocumentModal({
|
|||
<div
|
||||
id="document-preview"
|
||||
className={cn(
|
||||
"bg-[#14161A] rounded-[14px] overflow-hidden flex flex-col shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]",
|
||||
"bg-[#14161A] rounded-[14px] overflow-hidden flex flex-col shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)] relative",
|
||||
)}
|
||||
>
|
||||
{(_document?.type === "tweet" ||
|
||||
|
|
@ -104,9 +151,66 @@ export function DocumentModal({
|
|||
/>
|
||||
)}
|
||||
{_document?.type === "text" && (
|
||||
<div className="p-4 overflow-y-auto flex-1">
|
||||
{_document.content}
|
||||
</div>
|
||||
<>
|
||||
<div className="p-4 overflow-y-auto flex-1 scrollbar-thin">
|
||||
<TextEditor
|
||||
key={`${_document.id}-${editorResetNonce}`}
|
||||
content={initialEditorContent}
|
||||
onContentChange={setDraftContentString}
|
||||
onSubmit={handleSave}
|
||||
/>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{hasUnsavedChanges && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 20 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-3 px-4 py-2 bg-[#1B1F24] rounded-full shadow-[0_4px_20px_rgba(0,0,0,0.4),inset_1px_1px_1px_rgba(255,255,255,0.1)]"
|
||||
>
|
||||
<span className="text-sm text-[#737373]">
|
||||
Unsaved changes
|
||||
</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={resetEditor}
|
||||
disabled={updateMutation.isPending}
|
||||
className="text-[#737373]/80 hover:text-white rounded-full px-3"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="insideOut"
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={updateMutation.isPending}
|
||||
className="hover:text-white rounded-full px-4"
|
||||
>
|
||||
{updateMutation.isPending ? (
|
||||
<>
|
||||
<Loader2 className="size-4 animate-spin mr-1" />
|
||||
Saving...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Save
|
||||
<span
|
||||
className={cn(
|
||||
"bg-[#21212180] border border-[#73737333] text-[#737373] rounded-sm px-1 py-0.5 text-[10px] flex items-center justify-center",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
⌘+Enter
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)}
|
||||
{_document?.type === "pdf" && <PdfViewer url={_document.url} />}
|
||||
{_document?.type === "notion_doc" && (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import type { DocumentTypeEnum } from "@repo/validation/schemas"
|
||||
import type { z } from "zod"
|
||||
import { getDocumentIcon } from "@/components/new/document-modal/document-icon"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from "lucide-react"
|
||||
import { Button } from "@ui/components/button"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { Tabs, TabsList, TabsTrigger } from "@ui/components/tabs"
|
||||
import { useProjectName } from "@/hooks/use-project-name"
|
||||
import {
|
||||
|
|
@ -259,10 +259,12 @@ export function Header({ onAddMemory, onOpenMCP }: HeaderProps) {
|
|||
<Settings className="h-4 w-4" />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => {
|
||||
authClient.signOut()
|
||||
router.push("/login/new")
|
||||
}}>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
authClient.signOut()
|
||||
router.push("/login/new")
|
||||
}}
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
Logout
|
||||
</DropdownMenuItem>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { dmSans125ClassName, dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSans125ClassName, dmSansClassName } from "@/lib/fonts"
|
||||
import { Dialog, DialogContent, DialogFooter } from "@repo/ui/components/dialog"
|
||||
import { cn } from "@lib/utils"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import Image from "next/image"
|
|||
import { toast } from "sonner"
|
||||
import { analytics } from "@/lib/analytics"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmMonoClassName, dmSansClassName } from "@/utils/fonts"
|
||||
import { dmMonoClassName, dmSansClassName } from "@/lib/fonts"
|
||||
import { SyncLogoIcon } from "@ui/assets/icons"
|
||||
|
||||
const clients = {
|
||||
|
|
@ -78,7 +78,14 @@ export function MCPSteps({ variant = "full" }: MCPStepsProps) {
|
|||
>
|
||||
<div
|
||||
className="absolute left-4 top-0 w-px bg-[#1E293B] z-10"
|
||||
style={{ height: activeStep === 3 ? isEmbedded ? "100%" : "calc(100% - 4rem)" : "100%" }}
|
||||
style={{
|
||||
height:
|
||||
activeStep === 3
|
||||
? isEmbedded
|
||||
? "100%"
|
||||
: "calc(100% - 4rem)"
|
||||
: "100%",
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-start space-x-4 z-20">
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -6,11 +6,8 @@ import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
|
|||
import { useInfiniteQuery } from "@tanstack/react-query"
|
||||
import { useCallback, memo, useMemo, useState, useRef } from "react"
|
||||
import type { z } from "zod"
|
||||
import {
|
||||
Masonry,
|
||||
useInfiniteLoader,
|
||||
} from "masonic"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { Masonry, useInfiniteLoader } from "masonic"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { SuperLoader } from "@/components/superloader"
|
||||
import { cn } from "@lib/utils"
|
||||
import { Button } from "@ui/components/button"
|
||||
|
|
@ -151,9 +148,7 @@ export function MemoriesGrid({ isChatOpen }: { isChatOpen: boolean }) {
|
|||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="h-full"
|
||||
>
|
||||
<div className="h-full">
|
||||
<Button
|
||||
className={cn(
|
||||
dmSansClassName(),
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ import { motion, AnimatePresence } from "motion/react"
|
|||
import NovaOrb from "@/components/nova/nova-orb"
|
||||
import { Button } from "@ui/components/button"
|
||||
import { PanelRightCloseIcon, SendIcon } from "lucide-react"
|
||||
import { collectValidUrls } from "@/utils/url-helpers"
|
||||
import { collectValidUrls } from "@/lib/url-helpers"
|
||||
import { $fetch } from "@lib/api"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { useAuth } from "@lib/auth-context"
|
||||
|
||||
interface ChatSidebarProps {
|
||||
formData: {
|
||||
|
|
@ -20,6 +21,7 @@ interface ChatSidebarProps {
|
|||
}
|
||||
|
||||
export function ChatSidebar({ formData }: ChatSidebarProps) {
|
||||
const { user } = useAuth()
|
||||
const [message, setMessage] = useState("")
|
||||
const [isChatOpen, setIsChatOpen] = useState(true)
|
||||
const [messages, setMessages] = useState<
|
||||
|
|
@ -127,9 +129,60 @@ export function ChatSidebar({ formData }: ChatSidebarProps) {
|
|||
useEffect(() => {
|
||||
if (!formData) return
|
||||
|
||||
const urls = collectValidUrls(formData.linkedin, formData.otherLinks)
|
||||
const formDataMessages: typeof messages = []
|
||||
|
||||
console.log("urls", urls)
|
||||
if (formData.twitter) {
|
||||
formDataMessages.push({
|
||||
message: formData.twitter,
|
||||
url: formData.twitter,
|
||||
title: "X/Twitter",
|
||||
description: formData.twitter,
|
||||
type: "formData" as const,
|
||||
})
|
||||
}
|
||||
|
||||
if (formData.linkedin) {
|
||||
formDataMessages.push({
|
||||
message: formData.linkedin,
|
||||
url: formData.linkedin,
|
||||
title: "LinkedIn",
|
||||
description: formData.linkedin,
|
||||
type: "formData" as const,
|
||||
})
|
||||
}
|
||||
|
||||
if (formData.otherLinks.length > 0) {
|
||||
formData.otherLinks.forEach((link) => {
|
||||
formDataMessages.push({
|
||||
message: link,
|
||||
url: link,
|
||||
title: "Link",
|
||||
description: link,
|
||||
type: "formData" as const,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (formData.description?.trim()) {
|
||||
formDataMessages.push({
|
||||
message: formData.description,
|
||||
title: "Likes",
|
||||
description: formData.description,
|
||||
type: "formData" as const,
|
||||
})
|
||||
}
|
||||
|
||||
setMessages(formDataMessages)
|
||||
|
||||
const hasContent =
|
||||
formData.twitter ||
|
||||
formData.linkedin ||
|
||||
formData.otherLinks.length > 0 ||
|
||||
formData.description?.trim()
|
||||
|
||||
if (!hasContent) return
|
||||
|
||||
const urls = collectValidUrls(formData.linkedin, formData.otherLinks)
|
||||
|
||||
const processContent = async () => {
|
||||
setIsLoading(true)
|
||||
|
|
@ -137,17 +190,73 @@ export function ChatSidebar({ formData }: ChatSidebarProps) {
|
|||
try {
|
||||
const documentIds: string[] = []
|
||||
|
||||
// Step 1: Fetch content from Exa if URLs exist
|
||||
if (formData.description?.trim()) {
|
||||
try {
|
||||
const descDocResponse = await $fetch("@post/documents", {
|
||||
body: {
|
||||
content: formData.description,
|
||||
containerTags: ["sm_project_default"],
|
||||
metadata: {
|
||||
sm_source: "consumer",
|
||||
description_source: "user_input",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (descDocResponse.data?.id) {
|
||||
documentIds.push(descDocResponse.data.id)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Error creating description document:", error)
|
||||
}
|
||||
}
|
||||
|
||||
if (formData.twitter) {
|
||||
try {
|
||||
const researchResponse = await fetch("/api/onboarding/research", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
xUrl: formData.twitter,
|
||||
name: user?.name,
|
||||
email: user?.email,
|
||||
}),
|
||||
})
|
||||
|
||||
if (researchResponse.ok) {
|
||||
const { text } = await researchResponse.json()
|
||||
|
||||
if (text?.trim()) {
|
||||
const xDocResponse = await $fetch("@post/documents", {
|
||||
body: {
|
||||
content: text,
|
||||
containerTags: ["sm_project_default"],
|
||||
metadata: {
|
||||
sm_source: "consumer",
|
||||
onboarding_source: "x_research",
|
||||
x_url: formData.twitter,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (xDocResponse.data?.id) {
|
||||
documentIds.push(xDocResponse.data.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Error fetching X research:", error)
|
||||
}
|
||||
}
|
||||
|
||||
if (urls.length > 0) {
|
||||
const response = await fetch("/api/exa/fetch-content", {
|
||||
const response = await fetch("/api/onboarding/extract-content", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ urls }),
|
||||
})
|
||||
const { results } = await response.json()
|
||||
console.log("results", results)
|
||||
|
||||
// Create documents from Exa results
|
||||
for (const result of results) {
|
||||
try {
|
||||
const docResponse = await $fetch("@post/documents", {
|
||||
|
|
@ -171,95 +280,17 @@ export function ChatSidebar({ formData }: ChatSidebarProps) {
|
|||
}
|
||||
}
|
||||
|
||||
// Step 2: Create document from description if it exists
|
||||
if (formData.description?.trim()) {
|
||||
try {
|
||||
const descDocResponse = await $fetch("@post/documents", {
|
||||
body: {
|
||||
content: formData.description,
|
||||
containerTags: ["sm_project_default"],
|
||||
metadata: {
|
||||
sm_source: "consumer",
|
||||
description_source: "user_input",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (descDocResponse.data?.id) {
|
||||
documentIds.push(descDocResponse.data.id)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Error creating description document:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Poll for memories or show form data
|
||||
if (documentIds.length > 0) {
|
||||
await pollForMemories(documentIds)
|
||||
} else {
|
||||
// No documents created, show form data or waiting
|
||||
const formDataMessages = []
|
||||
|
||||
if (formData.twitter) {
|
||||
formDataMessages.push({
|
||||
message: `Twitter: ${formData.twitter}`,
|
||||
url: formData.twitter,
|
||||
title: "Twitter Profile",
|
||||
description: `Twitter: ${formData.twitter}`,
|
||||
type: "formData" as const,
|
||||
})
|
||||
}
|
||||
|
||||
if (formData.linkedin) {
|
||||
formDataMessages.push({
|
||||
message: `LinkedIn: ${formData.linkedin}`,
|
||||
url: formData.linkedin,
|
||||
title: "LinkedIn Profile",
|
||||
description: `LinkedIn: ${formData.linkedin}`,
|
||||
type: "formData" as const,
|
||||
})
|
||||
}
|
||||
|
||||
if (formData.otherLinks.length > 0) {
|
||||
formData.otherLinks.forEach((link) => {
|
||||
formDataMessages.push({
|
||||
message: `Link: ${link}`,
|
||||
url: link,
|
||||
title: "Other Link",
|
||||
description: `Link: ${link}`,
|
||||
type: "formData" as const,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const waitingMessage = {
|
||||
message: "Waiting for your input",
|
||||
url: "",
|
||||
title: "",
|
||||
description: "Waiting for your input",
|
||||
type: "waiting" as const,
|
||||
}
|
||||
|
||||
setMessages([...formDataMessages, waitingMessage])
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Error processing content:", error)
|
||||
|
||||
const waitingMessage = {
|
||||
message: "Waiting for your input",
|
||||
url: "",
|
||||
title: "",
|
||||
description: "Waiting for your input",
|
||||
type: "waiting" as const,
|
||||
}
|
||||
|
||||
setMessages([waitingMessage])
|
||||
}
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
processContent()
|
||||
}, [formData, pollForMemories])
|
||||
}, [formData, pollForMemories, user])
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="wait">
|
||||
|
|
@ -330,6 +361,39 @@ export function ChatSidebar({ formData }: ChatSidebarProps) {
|
|||
)}
|
||||
<div className="w-px flex-1 bg-[#293952]/40" />
|
||||
</div>
|
||||
{msg.type === "formData" && (
|
||||
<div className="bg-[#293952]/40 rounded-lg p-2 px-3 space-y-1 flex-1">
|
||||
{msg.title && (
|
||||
<h3
|
||||
className="text-sm font-medium"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(90deg, #369BFD 0%, #36FDFD 30%, #36FDB5 100%)",
|
||||
WebkitBackgroundClip: "text",
|
||||
WebkitTextFillColor: "transparent",
|
||||
backgroundClip: "text",
|
||||
}}
|
||||
>
|
||||
{msg.title}
|
||||
</h3>
|
||||
)}
|
||||
{msg.url && (
|
||||
<a
|
||||
href={msg.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs text-blue-400 hover:underline break-all block"
|
||||
>
|
||||
{msg.url}
|
||||
</a>
|
||||
)}
|
||||
{msg.title === "Likes" && msg.description && (
|
||||
<p className="text-xs text-white/70 mt-1">
|
||||
{msg.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{msg.type === "memory" && (
|
||||
<div className="space-y-2 w-full max-h-60 overflow-y-auto scrollbar-thin">
|
||||
{msg.memories?.map((memory) => (
|
||||
|
|
@ -383,7 +447,7 @@ export function ChatSidebar({ formData }: ChatSidebarProps) {
|
|||
{isLoading && (
|
||||
<div className="flex items-center gap-2 text-foreground/50">
|
||||
<NovaOrb size={28} className="blur-none!" />
|
||||
<span className="text-sm">Fetching your memories...</span>
|
||||
<span className="text-sm">Extracting memories...</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
import { useState } from "react"
|
||||
import { Button } from "@ui/components/button"
|
||||
import { MCPDetailView } from "@/components/new/mcp-modal/mcp-detail-view"
|
||||
import { XBookmarksDetailView } from "@/components/x-bookmarks-detail-view"
|
||||
import { XBookmarksDetailView } from "@/components/new/onboarding/x-bookmarks-detail-view"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { useOnboardingStorage } from "@hooks/use-onboarding-storage"
|
||||
|
||||
const integrationCards = [
|
||||
|
|
@ -164,7 +164,9 @@ export function IntegrationsStep() {
|
|||
<Button
|
||||
variant="link"
|
||||
className="text-white hover:text-gray-300 hover:no-underline cursor-pointer"
|
||||
onClick={() => router.push("/new/onboarding?flow=setup&step=relatable")}
|
||||
onClick={() =>
|
||||
router.push("/new/onboarding?flow=setup&step=relatable")
|
||||
}
|
||||
>
|
||||
← Back
|
||||
</Button>
|
||||
|
|
@ -5,7 +5,7 @@ import { motion, AnimatePresence } from "motion/react"
|
|||
import { Button } from "@ui/components/button"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
|
||||
const relatableOptions = [
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { Button } from "@ui/components/button"
|
||||
import { motion } from "motion/react"
|
||||
|
|
@ -2,7 +2,7 @@ import { motion } from "motion/react"
|
|||
import { Button } from "@ui/components/button"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
|
||||
export function FeaturesStep() {
|
||||
const router = useRouter()
|
||||
|
|
@ -3,9 +3,15 @@ import { Button } from "@ui/components/button"
|
|||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import {
|
||||
parseXHandle,
|
||||
parseLinkedInHandle,
|
||||
toXProfileUrl,
|
||||
toLinkedInProfileUrl,
|
||||
} from "@/lib/url-helpers"
|
||||
|
||||
interface MemoriesStepProps {
|
||||
interface ProfileStepProps {
|
||||
onSubmit: (data: {
|
||||
twitter: string
|
||||
linkedin: string
|
||||
|
|
@ -19,7 +25,7 @@ type ValidationError = {
|
|||
linkedin: string | null
|
||||
}
|
||||
|
||||
export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
||||
export function ProfileStep({ onSubmit }: ProfileStepProps) {
|
||||
const router = useRouter()
|
||||
const [otherLinks, setOtherLinks] = useState([""])
|
||||
const [twitterHandle, setTwitterHandle] = useState("")
|
||||
|
|
@ -43,63 +49,43 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
setOtherLinks(updated)
|
||||
}
|
||||
|
||||
const validateTwitterLink = (value: string): string | null => {
|
||||
if (!value.trim()) return null
|
||||
const validateTwitterHandle = (handle: string): string | null => {
|
||||
if (!handle.trim()) return null
|
||||
|
||||
const normalized = value.trim().toLowerCase()
|
||||
const isXDomain =
|
||||
normalized.includes("x.com") || normalized.includes("twitter.com")
|
||||
|
||||
if (!isXDomain) {
|
||||
return "share your X profile link"
|
||||
// Basic validation: handle should be alphanumeric, underscore, or hyphen
|
||||
// X/Twitter handles can contain letters, numbers, and underscores, max 15 chars
|
||||
const handlePattern = /^[a-zA-Z0-9_]{1,15}$/
|
||||
if (!handlePattern.test(handle.trim())) {
|
||||
return "Enter your handle or profile link"
|
||||
}
|
||||
|
||||
// Check if it's a profile link (not a status/tweet link)
|
||||
const profilePattern =
|
||||
/^(https?:\/\/)?(www\.)?(x\.com|twitter\.com)\/[^\/]+$/
|
||||
const statusPattern = /\/status\//i
|
||||
|
||||
if (statusPattern.test(normalized) || !profilePattern.test(normalized)) {
|
||||
return "share your X profile link"
|
||||
}
|
||||
|
||||
// Note: 404 validation would require a backend API endpoint
|
||||
// Format validation is handled above
|
||||
return null
|
||||
}
|
||||
|
||||
const validateLinkedInLink = (value: string): string | null => {
|
||||
if (!value.trim()) return null
|
||||
const validateLinkedInHandle = (handle: string): string | null => {
|
||||
if (!handle.trim()) return null
|
||||
|
||||
const normalized = value.trim().toLowerCase()
|
||||
const isLinkedInDomain = normalized.includes("linkedin.com")
|
||||
|
||||
if (!isLinkedInDomain) {
|
||||
return "share your Linkedin profile link"
|
||||
// Basic validation: LinkedIn handles are typically alphanumeric with hyphens
|
||||
// They can be quite long, so we'll be lenient
|
||||
const handlePattern = /^[a-zA-Z0-9-]+$/
|
||||
if (!handlePattern.test(handle.trim())) {
|
||||
return "Enter your handle or profile link"
|
||||
}
|
||||
|
||||
// Check if it's a profile link (should have /in/ or /pub/)
|
||||
const profilePattern =
|
||||
/^(https?:\/\/)?(www\.)?linkedin\.com\/(in|pub)\/[^\/]+/
|
||||
|
||||
if (!profilePattern.test(normalized)) {
|
||||
return "share your Linkedin profile link"
|
||||
}
|
||||
|
||||
// Note: 404 validation would require a backend API endpoint
|
||||
// Format validation is handled above
|
||||
return null
|
||||
}
|
||||
|
||||
const handleTwitterChange = (value: string) => {
|
||||
setTwitterHandle(value)
|
||||
const error = validateTwitterLink(value)
|
||||
const parsedHandle = parseXHandle(value)
|
||||
setTwitterHandle(parsedHandle)
|
||||
const error = validateTwitterHandle(parsedHandle)
|
||||
setErrors((prev) => ({ ...prev, twitter: error }))
|
||||
}
|
||||
|
||||
const handleLinkedInChange = (value: string) => {
|
||||
setLinkedinProfile(value)
|
||||
const error = validateLinkedInLink(value)
|
||||
const parsedHandle = parseLinkedInHandle(value)
|
||||
setLinkedinProfile(parsedHandle)
|
||||
const error = validateLinkedInHandle(parsedHandle)
|
||||
setErrors((prev) => ({ ...prev, linkedin: error }))
|
||||
}
|
||||
|
||||
|
|
@ -123,24 +109,33 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
X/Twitter
|
||||
</label>
|
||||
<div className="relative flex items-center">
|
||||
<input
|
||||
id="twitter-handle"
|
||||
type="text"
|
||||
placeholder="x.com/yourhandle"
|
||||
value={twitterHandle}
|
||||
onChange={(e) => handleTwitterChange(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (twitterHandle.trim()) {
|
||||
const error = validateTwitterLink(twitterHandle)
|
||||
setErrors((prev) => ({ ...prev, twitter: error }))
|
||||
}
|
||||
}}
|
||||
className={`w-full px-4 py-2 bg-[#070E1B] border rounded-xl text-white placeholder-onboarding focus:outline-none transition-colors h-[40px] ${
|
||||
<div
|
||||
className={`flex items-center border rounded-xl overflow-hidden h-[40px] w-full ${
|
||||
errors.twitter
|
||||
? "border-[#52596633] bg-[#290F0A]"
|
||||
: "border-onboarding/20"
|
||||
}`}
|
||||
/>
|
||||
>
|
||||
<div className="px-3 py-2 bg-[#070E1B] text-white/60 text-sm border-r border-onboarding/20 whitespace-nowrap">
|
||||
x.com/
|
||||
</div>
|
||||
<input
|
||||
id="twitter-handle"
|
||||
type="text"
|
||||
placeholder="handle"
|
||||
value={twitterHandle}
|
||||
onChange={(e) => handleTwitterChange(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (twitterHandle.trim()) {
|
||||
const error = validateTwitterHandle(twitterHandle)
|
||||
setErrors((prev) => ({ ...prev, twitter: error }))
|
||||
}
|
||||
}}
|
||||
className={`flex-1 px-4 py-2 bg-[#070E1B] text-white placeholder-onboarding focus:outline-none transition-colors ${
|
||||
errors.twitter ? "bg-[#290F0A]" : ""
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
{errors.twitter && (
|
||||
<div className="absolute left-full ml-3">
|
||||
<div
|
||||
|
|
@ -165,24 +160,33 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
LinkedIn
|
||||
</label>
|
||||
<div className="relative flex items-center">
|
||||
<input
|
||||
id="linkedin-profile"
|
||||
type="text"
|
||||
placeholder="linkedin.com/in/yourname"
|
||||
value={linkedinProfile}
|
||||
onChange={(e) => handleLinkedInChange(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (linkedinProfile.trim()) {
|
||||
const error = validateLinkedInLink(linkedinProfile)
|
||||
setErrors((prev) => ({ ...prev, linkedin: error }))
|
||||
}
|
||||
}}
|
||||
className={`w-full px-4 py-2 bg-[#070E1B] border rounded-xl text-white placeholder-onboarding focus:outline-none transition-colors h-[40px] ${
|
||||
<div
|
||||
className={`flex items-center border rounded-xl overflow-hidden h-[40px] w-full ${
|
||||
errors.linkedin
|
||||
? "border-[#52596633] bg-[#290F0A]"
|
||||
: "border-onboarding/20"
|
||||
}`}
|
||||
/>
|
||||
>
|
||||
<div className="px-3 py-2 bg-[#070E1B] text-white/60 text-sm border-r border-onboarding/20 whitespace-nowrap w-[140px]">
|
||||
linkedin.com/in/
|
||||
</div>
|
||||
<input
|
||||
id="linkedin-profile"
|
||||
type="text"
|
||||
placeholder="username"
|
||||
value={linkedinProfile}
|
||||
onChange={(e) => handleLinkedInChange(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (linkedinProfile.trim()) {
|
||||
const error = validateLinkedInHandle(linkedinProfile)
|
||||
setErrors((prev) => ({ ...prev, linkedin: error }))
|
||||
}
|
||||
}}
|
||||
className={`flex-1 px-4 py-2 bg-[#070E1B] text-white placeholder-onboarding focus:outline-none transition-colors ${
|
||||
errors.linkedin ? "bg-[#290F0A]" : ""
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
{errors.linkedin && (
|
||||
<div className="absolute left-full ml-3">
|
||||
<div
|
||||
|
|
@ -282,8 +286,8 @@ export function MemoriesStep({ onSubmit }: MemoriesStepProps) {
|
|||
}}
|
||||
onClick={() => {
|
||||
const formData = {
|
||||
twitter: twitterHandle,
|
||||
linkedin: linkedinProfile,
|
||||
twitter: toXProfileUrl(twitterHandle),
|
||||
linkedin: toLinkedInProfileUrl(linkedinProfile),
|
||||
description: description,
|
||||
otherLinks: otherLinks.filter((l) => l.trim()),
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { Button } from "@ui/components/button"
|
||||
import { cn } from "@lib/utils"
|
||||
import { dmSansClassName } from "@/utils/fonts"
|
||||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import Image from "next/image"
|
||||
|
||||
interface XBookmarksDetailViewProps {
|
||||
|
|
@ -27,9 +27,7 @@ const steps = [
|
|||
},
|
||||
]
|
||||
|
||||
export function XBookmarksDetailView({
|
||||
onBack,
|
||||
}: XBookmarksDetailViewProps) {
|
||||
export function XBookmarksDetailView({ onBack }: XBookmarksDetailViewProps) {
|
||||
const handleInstall = () => {
|
||||
window.open(
|
||||
"https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnangednailhoegogi",
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { dmSans125ClassName } from "@/utils/fonts"
|
||||
import { dmSans125ClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { useAuth } from "@lib/auth-context"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@ui/components/avatar"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { dmSans125ClassName } from "@/utils/fonts"
|
||||
import { dmSans125ClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { $fetch } from "@lib/api"
|
||||
import { fetchSubscriptionStatus } from "@lib/queries"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { dmSans125ClassName } from "@/utils/fonts"
|
||||
import { dmSans125ClassName } from "@/lib/fonts"
|
||||
import { analytics } from "@/lib/analytics"
|
||||
import { cn } from "@lib/utils"
|
||||
import { authClient } from "@lib/auth"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { dmSans125ClassName } from "@/utils/fonts"
|
||||
import { dmSans125ClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { ArrowUpRight } from "lucide-react"
|
||||
|
||||
|
|
|
|||
92
apps/web/components/new/text-editor/extensions.tsx
Normal file
92
apps/web/components/new/text-editor/extensions.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import StarterKit from "@tiptap/starter-kit"
|
||||
import Placeholder from "@tiptap/extension-placeholder"
|
||||
import Link from "@tiptap/extension-link"
|
||||
import Image from "@tiptap/extension-image"
|
||||
import TaskList from "@tiptap/extension-task-list"
|
||||
import TaskItem from "@tiptap/extension-task-item"
|
||||
import { cx } from "class-variance-authority"
|
||||
|
||||
const placeholder = Placeholder.configure({
|
||||
placeholder: 'Write, paste anything or type "/" for commands...',
|
||||
})
|
||||
|
||||
const taskList = TaskList.configure({
|
||||
HTMLAttributes: {
|
||||
class: cx("not-prose pl-2"),
|
||||
},
|
||||
})
|
||||
|
||||
const taskItem = TaskItem.configure({
|
||||
HTMLAttributes: {
|
||||
class: cx("flex items-start my-4"),
|
||||
},
|
||||
nested: true,
|
||||
})
|
||||
|
||||
const link = Link.configure({
|
||||
HTMLAttributes: {
|
||||
class: cx(
|
||||
"text-muted-foreground underline underline-offset-[3px] hover:text-primary transition-colors cursor-pointer",
|
||||
),
|
||||
},
|
||||
openOnClick: false,
|
||||
})
|
||||
|
||||
const image = Image.configure({
|
||||
HTMLAttributes: {
|
||||
class: cx("rounded-lg border border-muted"),
|
||||
},
|
||||
})
|
||||
|
||||
const starterKit = StarterKit.configure({
|
||||
bulletList: {
|
||||
HTMLAttributes: {
|
||||
class: cx("list-disc list-outside leading-3 -mt-2"),
|
||||
},
|
||||
},
|
||||
orderedList: {
|
||||
HTMLAttributes: {
|
||||
class: cx("list-decimal list-outside leading-3 -mt-2"),
|
||||
},
|
||||
},
|
||||
listItem: {
|
||||
HTMLAttributes: {
|
||||
class: cx("leading-normal -mb-2"),
|
||||
},
|
||||
},
|
||||
blockquote: {
|
||||
HTMLAttributes: {
|
||||
class: cx("border-l-4 border-primary"),
|
||||
},
|
||||
},
|
||||
codeBlock: {
|
||||
HTMLAttributes: {
|
||||
class: cx("rounded-sm bg-muted border p-5 font-mono font-medium"),
|
||||
},
|
||||
},
|
||||
code: {
|
||||
HTMLAttributes: {
|
||||
class: cx("rounded-md bg-muted px-1.5 py-1 font-mono font-medium"),
|
||||
spellcheck: "false",
|
||||
},
|
||||
},
|
||||
horizontalRule: {
|
||||
HTMLAttributes: {
|
||||
class: cx("mt-4 mb-6 border-t border-muted-foreground"),
|
||||
},
|
||||
},
|
||||
dropcursor: {
|
||||
color: "#DBEAFE",
|
||||
width: 4,
|
||||
},
|
||||
gapcursor: false,
|
||||
})
|
||||
|
||||
export const defaultExtensions = [
|
||||
starterKit,
|
||||
placeholder,
|
||||
link,
|
||||
image,
|
||||
taskList,
|
||||
taskItem,
|
||||
]
|
||||
174
apps/web/components/new/text-editor/index.tsx
Normal file
174
apps/web/components/new/text-editor/index.tsx
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
"use client"
|
||||
|
||||
import { useEditor, EditorContent } from "@tiptap/react"
|
||||
import { BubbleMenu } from "@tiptap/react/menus"
|
||||
import type { Editor } from "@tiptap/core"
|
||||
import { Markdown } from "@tiptap/markdown"
|
||||
import { useRef, useEffect, useCallback } from "react"
|
||||
import { defaultExtensions } from "./extensions"
|
||||
import { slashCommand } from "./suggestions"
|
||||
import { Bold, Italic, Code } from "lucide-react"
|
||||
import { useDebouncedCallback } from "use-debounce"
|
||||
import { cn } from "@lib/utils"
|
||||
|
||||
const extensions = [...defaultExtensions, slashCommand, Markdown]
|
||||
|
||||
export function TextEditor({
|
||||
content: initialContent,
|
||||
onContentChange,
|
||||
onSubmit,
|
||||
}: {
|
||||
content: string | undefined
|
||||
onContentChange: (content: string) => void
|
||||
onSubmit: () => void
|
||||
}) {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const editorRef = useRef<Editor | null>(null)
|
||||
const onSubmitRef = useRef(onSubmit)
|
||||
const hasUserEditedRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
onSubmitRef.current = onSubmit
|
||||
}, [onSubmit])
|
||||
|
||||
const debouncedUpdates = useDebouncedCallback((editor: Editor) => {
|
||||
if (!hasUserEditedRef.current) return
|
||||
const json = editor.getJSON()
|
||||
const markdown = editor.storage.markdown?.manager?.serialize(json) ?? ""
|
||||
onContentChange?.(markdown)
|
||||
}, 500)
|
||||
|
||||
const editor = useEditor({
|
||||
extensions,
|
||||
content: initialContent,
|
||||
contentType: "markdown",
|
||||
immediatelyRender: true,
|
||||
onCreate: ({ editor }) => {
|
||||
editorRef.current = editor
|
||||
},
|
||||
onUpdate: ({ editor }) => {
|
||||
editorRef.current = editor
|
||||
debouncedUpdates(editor)
|
||||
},
|
||||
editorProps: {
|
||||
handleKeyDown: (_view, event) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
onSubmitRef.current?.()
|
||||
return true
|
||||
}
|
||||
hasUserEditedRef.current = true
|
||||
return false
|
||||
},
|
||||
handleTextInput: () => {
|
||||
hasUserEditedRef.current = true
|
||||
return false
|
||||
},
|
||||
handlePaste: () => {
|
||||
hasUserEditedRef.current = true
|
||||
return false
|
||||
},
|
||||
handleDrop: () => {
|
||||
hasUserEditedRef.current = true
|
||||
return false
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (editor && initialContent) {
|
||||
hasUserEditedRef.current = false
|
||||
editor.commands.setContent(initialContent, { contentType: "markdown" })
|
||||
}
|
||||
}, [editor, initialContent])
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLElement
|
||||
if (target.closest(".ProseMirror")) {
|
||||
return
|
||||
}
|
||||
if (target.closest("button, a")) {
|
||||
return
|
||||
}
|
||||
|
||||
const proseMirror = containerRef.current?.querySelector(
|
||||
".ProseMirror",
|
||||
) as HTMLElement
|
||||
if (proseMirror && editorRef.current) {
|
||||
setTimeout(() => {
|
||||
proseMirror.focus()
|
||||
editorRef.current?.commands.focus("end")
|
||||
}, 0)
|
||||
}
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
editor?.destroy()
|
||||
}
|
||||
}, [editor])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* biome-ignore lint/a11y/useSemanticElements: div is needed as container for editor, cannot use button */}
|
||||
{/* biome-ignore lint/a11y/useKeyWithClickEvents: we need to use a div to get the focus on the editor */}
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
ref={containerRef}
|
||||
onClick={handleClick}
|
||||
className="w-full h-full outline-none prose prose-invert max-w-none [&_.ProseMirror]:outline-none [&_.ProseMirror]:focus:outline-none [&_.ProseMirror-focused]:outline-none text-editor-prose cursor-text"
|
||||
>
|
||||
<EditorContent editor={editor} />
|
||||
</div>
|
||||
{editor && (
|
||||
<BubbleMenu
|
||||
editor={editor}
|
||||
options={{ placement: "bottom-start", offset: 8 }}
|
||||
>
|
||||
<div className="flex items-center gap-1 rounded-[8px] bg-[#1b1f24] p-2 shadow-[0px_4px_20px_0px_rgba(0,0,0,0.25),inset_1px_1px_1px_0px_rgba(255,255,255,0.1)]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
editor.chain().focus().toggleBold().run()
|
||||
}
|
||||
className={cn(
|
||||
"flex items-center justify-center rounded-[4px] p-1.5 hover:bg-[#2e353d] cursor-pointer text-[#fafafa]",
|
||||
editor.isActive("bold") && "bg-[#2e353d]",
|
||||
)}
|
||||
>
|
||||
<Bold size={16} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
editor.chain().focus().toggleItalic().run()
|
||||
}
|
||||
className={cn(
|
||||
"flex items-center justify-center rounded-[4px] p-1.5 hover:bg-[#2e353d] cursor-pointer text-[#fafafa]",
|
||||
editor.isActive("italic") && "bg-[#2e353d]",
|
||||
)}
|
||||
>
|
||||
<Italic size={16} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
editor.chain().focus().toggleCode().run()
|
||||
}
|
||||
className={cn(
|
||||
"flex items-center justify-center rounded-[4px] p-1.5 hover:bg-[#2e353d] cursor-pointer text-[#fafafa]",
|
||||
editor.isActive("code") && "bg-[#2e353d]",
|
||||
)}
|
||||
>
|
||||
<Code size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</BubbleMenu>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
308
apps/web/components/new/text-editor/slash-command.tsx
Normal file
308
apps/web/components/new/text-editor/slash-command.tsx
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
"use client"
|
||||
|
||||
import { Extension, type Editor, type Range } from "@tiptap/core"
|
||||
import Suggestion, { type SuggestionOptions } from "@tiptap/suggestion"
|
||||
import { useEffect, useLayoutEffect, useState, useRef } from "react"
|
||||
import { createPortal } from "react-dom"
|
||||
import { createRoot, type Root } from "react-dom/client"
|
||||
import {
|
||||
useFloating,
|
||||
offset,
|
||||
flip,
|
||||
shift,
|
||||
autoUpdate,
|
||||
} from "@floating-ui/react"
|
||||
import { cn } from "@lib/utils"
|
||||
|
||||
export interface SuggestionItem {
|
||||
title: string
|
||||
description: string
|
||||
searchTerms?: string[]
|
||||
icon: React.ReactNode
|
||||
command: (props: { editor: Editor; range: Range }) => void
|
||||
}
|
||||
|
||||
interface CommandListProps {
|
||||
items: SuggestionItem[]
|
||||
command: (item: SuggestionItem) => void
|
||||
selectedIndex: number
|
||||
}
|
||||
|
||||
function CommandList({ items, command, selectedIndex }: CommandListProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const selectedElement = containerRef.current?.querySelector(
|
||||
`[data-index="${selectedIndex}"]`,
|
||||
)
|
||||
selectedElement?.scrollIntoView({ block: "nearest" })
|
||||
}, [selectedIndex])
|
||||
|
||||
if (items.length === 0) {
|
||||
return (
|
||||
<div className="z-50 h-auto max-h-[330px] overflow-y-auto rounded-[8px] bg-[#1b1f24] p-2 shadow-[0px_4px_20px_0px_rgba(0,0,0,0.25),inset_1px_1px_1px_0px_rgba(255,255,255,0.1)]">
|
||||
<div className="px-2 text-muted-foreground">No results</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="z-50 h-auto max-h-[330px] overflow-y-auto rounded-[8px] bg-[#1b1f24] p-2 shadow-[0px_4px_20px_0px_rgba(0,0,0,0.25),inset_1px_1px_1px_0px_rgba(255,255,255,0.1)]"
|
||||
>
|
||||
{items.map((item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
key={item.title}
|
||||
data-index={index}
|
||||
onClick={() => command(item)}
|
||||
className={cn(
|
||||
"flex w-full items-center gap-2 rounded-[4px] px-3 py-2 text-left hover:bg-[#2e353d]",
|
||||
index === selectedIndex && "bg-[#2e353d]",
|
||||
)}
|
||||
>
|
||||
<div className="flex size-[20px] shrink-0 items-center justify-center text-[#fafafa]">
|
||||
{item.icon}
|
||||
</div>
|
||||
<p className="font-medium text-[16px] leading-[1.35] tracking-[-0.16px] text-[#fafafa]">
|
||||
{item.title}
|
||||
</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface CommandMenuProps {
|
||||
items: SuggestionItem[]
|
||||
command: (item: SuggestionItem) => void
|
||||
clientRect: (() => DOMRect | null) | null
|
||||
selectedIndex: number
|
||||
}
|
||||
|
||||
function CommandMenu({
|
||||
items,
|
||||
command,
|
||||
clientRect,
|
||||
selectedIndex,
|
||||
}: CommandMenuProps) {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement: "bottom-start",
|
||||
middleware: [offset(8), flip(), shift()],
|
||||
whileElementsMounted: autoUpdate,
|
||||
})
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const rect = clientRect?.()
|
||||
if (rect) {
|
||||
refs.setReference({
|
||||
getBoundingClientRect: () => rect,
|
||||
})
|
||||
}
|
||||
}, [clientRect, refs])
|
||||
|
||||
if (!mounted) return null
|
||||
|
||||
return createPortal(
|
||||
<div ref={refs.setFloating} style={floatingStyles} className="z-50">
|
||||
<CommandList
|
||||
items={items}
|
||||
command={command}
|
||||
selectedIndex={selectedIndex}
|
||||
/>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
|
||||
export function createSlashCommand(items: SuggestionItem[]) {
|
||||
let component: {
|
||||
updateProps: (props: CommandMenuProps) => void
|
||||
destroy: () => void
|
||||
element: HTMLElement
|
||||
} | null = null
|
||||
let root: Root | null = null
|
||||
let selectedIndex = 0
|
||||
let currentItems: SuggestionItem[] = []
|
||||
|
||||
const renderMenu = (props: {
|
||||
items: SuggestionItem[]
|
||||
command: (item: SuggestionItem) => void
|
||||
clientRect: (() => DOMRect | null) | null
|
||||
}) => {
|
||||
root?.render(
|
||||
<CommandMenu
|
||||
items={props.items}
|
||||
command={props.command}
|
||||
clientRect={props.clientRect}
|
||||
selectedIndex={selectedIndex}
|
||||
/>,
|
||||
)
|
||||
}
|
||||
|
||||
const suggestion: Omit<SuggestionOptions<SuggestionItem>, "editor"> = {
|
||||
char: "/",
|
||||
items: ({ query }) => {
|
||||
return items.filter(
|
||||
(item) =>
|
||||
item.title.toLowerCase().includes(query.toLowerCase()) ||
|
||||
item.searchTerms?.some((term) =>
|
||||
term.toLowerCase().includes(query.toLowerCase()),
|
||||
),
|
||||
)
|
||||
},
|
||||
command: ({ editor, range, props }) => {
|
||||
props.command({ editor, range })
|
||||
},
|
||||
render: () => {
|
||||
let currentCommand: ((item: SuggestionItem) => void) | null = null
|
||||
let currentClientRect: (() => DOMRect | null) | null = null
|
||||
|
||||
return {
|
||||
onStart: (props) => {
|
||||
selectedIndex = 0
|
||||
currentItems = props.items as SuggestionItem[]
|
||||
currentCommand = props.command as (
|
||||
item: SuggestionItem,
|
||||
) => void
|
||||
currentClientRect = props.clientRect ?? null
|
||||
|
||||
const element = document.createElement("div")
|
||||
document.body.appendChild(element)
|
||||
|
||||
root = createRoot(element)
|
||||
if (currentCommand) {
|
||||
renderMenu({
|
||||
items: currentItems,
|
||||
command: currentCommand,
|
||||
clientRect: currentClientRect,
|
||||
})
|
||||
}
|
||||
|
||||
component = {
|
||||
element,
|
||||
updateProps: (newProps: CommandMenuProps) => {
|
||||
root?.render(
|
||||
<CommandMenu
|
||||
items={newProps.items}
|
||||
command={newProps.command}
|
||||
clientRect={newProps.clientRect}
|
||||
selectedIndex={newProps.selectedIndex}
|
||||
/>,
|
||||
)
|
||||
},
|
||||
destroy: () => {
|
||||
root?.unmount()
|
||||
element.remove()
|
||||
root = null
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
onUpdate: (props) => {
|
||||
currentItems = props.items as SuggestionItem[]
|
||||
currentCommand = props.command as (
|
||||
item: SuggestionItem,
|
||||
) => void
|
||||
currentClientRect = props.clientRect ?? null
|
||||
|
||||
if (selectedIndex >= currentItems.length) {
|
||||
selectedIndex = Math.max(0, currentItems.length - 1)
|
||||
}
|
||||
|
||||
if (currentCommand) {
|
||||
component?.updateProps({
|
||||
items: currentItems,
|
||||
command: currentCommand,
|
||||
clientRect: currentClientRect,
|
||||
selectedIndex,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
onKeyDown: (props) => {
|
||||
const { event } = props
|
||||
|
||||
if (event.key === "Escape") {
|
||||
component?.destroy()
|
||||
component = null
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.key === "ArrowUp") {
|
||||
selectedIndex =
|
||||
selectedIndex <= 0
|
||||
? currentItems.length - 1
|
||||
: selectedIndex - 1
|
||||
if (currentCommand) {
|
||||
component?.updateProps({
|
||||
items: currentItems,
|
||||
command: currentCommand,
|
||||
clientRect: currentClientRect,
|
||||
selectedIndex,
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.key === "ArrowDown") {
|
||||
selectedIndex =
|
||||
selectedIndex >= currentItems.length - 1
|
||||
? 0
|
||||
: selectedIndex + 1
|
||||
if (currentCommand) {
|
||||
component?.updateProps({
|
||||
items: currentItems,
|
||||
command: currentCommand,
|
||||
clientRect: currentClientRect,
|
||||
selectedIndex,
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (event.key === "Enter") {
|
||||
const item = currentItems[selectedIndex]
|
||||
if (item && currentCommand) {
|
||||
currentCommand(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
onExit: () => {
|
||||
component?.destroy()
|
||||
component = null
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return Extension.create({
|
||||
name: "slashCommand",
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
suggestion,
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
Suggestion({
|
||||
editor: this.editor,
|
||||
...this.options.suggestion,
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
}
|
||||
103
apps/web/components/new/text-editor/suggestions.tsx
Normal file
103
apps/web/components/new/text-editor/suggestions.tsx
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import {
|
||||
Heading1,
|
||||
Heading2,
|
||||
Heading3,
|
||||
List,
|
||||
ListOrdered,
|
||||
TextQuote,
|
||||
Text,
|
||||
} from "lucide-react"
|
||||
import { createSlashCommand, type SuggestionItem } from "./slash-command"
|
||||
|
||||
export const suggestionItems: SuggestionItem[] = [
|
||||
{
|
||||
title: "Heading 1",
|
||||
description: "Big section heading.",
|
||||
searchTerms: ["title", "big", "large"],
|
||||
icon: <Heading1 size={20} />,
|
||||
command: ({ editor, range }) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.setNode("heading", { level: 1 })
|
||||
.run()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Heading 2",
|
||||
description: "Medium section heading.",
|
||||
searchTerms: ["subtitle", "medium"],
|
||||
icon: <Heading2 size={20} />,
|
||||
command: ({ editor, range }) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.setNode("heading", { level: 2 })
|
||||
.run()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Heading 3",
|
||||
description: "Small section heading.",
|
||||
searchTerms: ["subtitle", "small"],
|
||||
icon: <Heading3 size={20} />,
|
||||
command: ({ editor, range }) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.setNode("heading", { level: 3 })
|
||||
.run()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Text",
|
||||
description: "Just start typing with plain text.",
|
||||
searchTerms: ["p", "paragraph"],
|
||||
icon: <Text size={18} />,
|
||||
command: ({ editor, range }) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.toggleNode("paragraph", "paragraph")
|
||||
.run()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Bullet List",
|
||||
description: "Create a simple bullet list.",
|
||||
searchTerms: ["unordered", "point"],
|
||||
icon: <List size={20} />,
|
||||
command: ({ editor, range }) => {
|
||||
editor.chain().focus().deleteRange(range).toggleBulletList().run()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Numbered List",
|
||||
description: "Create a list with numbering.",
|
||||
searchTerms: ["ordered"],
|
||||
icon: <ListOrdered size={20} />,
|
||||
command: ({ editor, range }) => {
|
||||
editor.chain().focus().deleteRange(range).toggleOrderedList().run()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Block Quote",
|
||||
description: "Capture a quote.",
|
||||
searchTerms: ["blockquote"],
|
||||
icon: <TextQuote size={20} />,
|
||||
command: ({ editor, range }) =>
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.toggleNode("paragraph", "paragraph")
|
||||
.toggleBlockquote()
|
||||
.run(),
|
||||
},
|
||||
]
|
||||
|
||||
export const slashCommand = createSlashCommand(suggestionItems)
|
||||
|
|
@ -88,3 +88,35 @@
|
|||
inset 0 2px 4px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Disable ProseMirror focus styles */
|
||||
.ProseMirror:focus,
|
||||
.ProseMirror-focused,
|
||||
.ProseMirror:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Override prose paragraph margins for text editor */
|
||||
.text-editor-prose.prose :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Style placeholder for text editor */
|
||||
.text-editor-prose .ProseMirror p.is-editor-empty:first-child::before {
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
color: #525966;
|
||||
pointer-events: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.text-editor-prose .ProseMirror .is-empty::before {
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
color: #525966;
|
||||
pointer-events: none;
|
||||
height: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ interface DocumentsQueryData {
|
|||
}
|
||||
|
||||
interface UseDocumentMutationsOptions {
|
||||
onClose: () => void
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions) {
|
||||
export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions = {}) {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const noteMutation = useMutation({
|
||||
|
|
@ -101,7 +101,7 @@ export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions) {
|
|||
queryClient.invalidateQueries({
|
||||
queryKey: ["documents-with-memories", variables.project],
|
||||
})
|
||||
onClose()
|
||||
onClose?.()
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -184,7 +184,7 @@ export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions) {
|
|||
queryClient.invalidateQueries({
|
||||
queryKey: ["documents-with-memories", variables.project],
|
||||
})
|
||||
onClose()
|
||||
onClose?.()
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -301,7 +301,43 @@ export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions) {
|
|||
queryClient.invalidateQueries({
|
||||
queryKey: ["documents-with-memories", variables.project],
|
||||
})
|
||||
onClose()
|
||||
onClose?.()
|
||||
},
|
||||
})
|
||||
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: async ({
|
||||
documentId,
|
||||
content,
|
||||
}: {
|
||||
documentId: string
|
||||
content: string
|
||||
}) => {
|
||||
const response = await $fetch(`@patch/documents/${documentId}`, {
|
||||
body: {
|
||||
content,
|
||||
metadata: {
|
||||
sm_source: "consumer",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.error?.message || "Failed to save document")
|
||||
}
|
||||
|
||||
return response.data
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success("Document saved successfully!")
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["documents-with-memories"],
|
||||
})
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error("Failed to save document", {
|
||||
description: error instanceof Error ? error.message : "Unknown error",
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -309,5 +345,6 @@ export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions) {
|
|||
noteMutation,
|
||||
linkMutation,
|
||||
fileMutation,
|
||||
updateMutation,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
190
apps/web/lib/url-helpers.ts
Normal file
190
apps/web/lib/url-helpers.ts
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/**
|
||||
* Validates if a string is a valid URL.
|
||||
*/
|
||||
export const isValidUrl = (url: string): boolean => {
|
||||
try {
|
||||
new URL(url)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a URL by adding https:// prefix if missing.
|
||||
*/
|
||||
export const normalizeUrl = (url: string): string => {
|
||||
if (!url.trim()) return ""
|
||||
if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||
return url
|
||||
}
|
||||
return `https://${url}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a URL is a Twitter/X URL.
|
||||
*/
|
||||
export const isTwitterUrl = (url: string): boolean => {
|
||||
const normalizedUrl = url.toLowerCase()
|
||||
return (
|
||||
normalizedUrl.includes("twitter.com") || normalizedUrl.includes("x.com")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a URL is a LinkedIn profile URL (not a company page).
|
||||
*/
|
||||
export const isLinkedInProfileUrl = (url: string): boolean => {
|
||||
const normalizedUrl = url.toLowerCase()
|
||||
return (
|
||||
normalizedUrl.includes("linkedin.com/in/") &&
|
||||
!normalizedUrl.includes("linkedin.com/company/")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects and validates URLs from LinkedIn profile and other links, excluding Twitter.
|
||||
*/
|
||||
export const collectValidUrls = (
|
||||
linkedinProfile: string,
|
||||
otherLinks: string[],
|
||||
): string[] => {
|
||||
const urls: string[] = []
|
||||
|
||||
if (linkedinProfile.trim()) {
|
||||
const normalizedLinkedIn = normalizeUrl(linkedinProfile.trim())
|
||||
if (
|
||||
isValidUrl(normalizedLinkedIn) &&
|
||||
isLinkedInProfileUrl(normalizedLinkedIn)
|
||||
) {
|
||||
urls.push(normalizedLinkedIn)
|
||||
}
|
||||
}
|
||||
|
||||
otherLinks
|
||||
.filter((link) => link.trim())
|
||||
.forEach((link) => {
|
||||
const normalizedLink = normalizeUrl(link.trim())
|
||||
if (isValidUrl(normalizedLink) && !isTwitterUrl(normalizedLink)) {
|
||||
urls.push(normalizedLink)
|
||||
}
|
||||
})
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts X/Twitter handle from various input formats (URLs, handles with @, etc.).
|
||||
*/
|
||||
export function parseXHandle(input: string): string {
|
||||
if (!input.trim()) return ""
|
||||
|
||||
let value = input.trim()
|
||||
|
||||
if (value.startsWith("@")) {
|
||||
value = value.slice(1)
|
||||
}
|
||||
|
||||
const lowerValue = value.toLowerCase()
|
||||
if (lowerValue.includes("x.com") || lowerValue.includes("twitter.com")) {
|
||||
try {
|
||||
let url: URL
|
||||
if (value.startsWith("http://") || value.startsWith("https://")) {
|
||||
url = new URL(value)
|
||||
} else {
|
||||
url = new URL(`https://${value}`)
|
||||
}
|
||||
|
||||
const pathSegments = url.pathname.split("/").filter(Boolean)
|
||||
if (pathSegments.length > 0) {
|
||||
const firstSegment = pathSegments[0]
|
||||
if (firstSegment && firstSegment !== "status" && firstSegment !== "i") {
|
||||
return firstSegment
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
const match = value.match(/(?:x\.com|twitter\.com)\/([^/\s?#]+)/i)
|
||||
const handle = match?.[1]
|
||||
if (handle && handle !== "status") {
|
||||
return handle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
value.includes("/") &&
|
||||
!lowerValue.includes("x.com") &&
|
||||
!lowerValue.includes("twitter.com")
|
||||
) {
|
||||
const parts = value.split("/").filter(Boolean)
|
||||
const firstPart = parts[0]
|
||||
if (firstPart) {
|
||||
return firstPart
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts LinkedIn handle from various input formats (URLs, handles with @, etc.).
|
||||
*/
|
||||
export function parseLinkedInHandle(input: string): string {
|
||||
if (!input.trim()) return ""
|
||||
|
||||
let value = input.trim()
|
||||
|
||||
if (value.startsWith("@")) {
|
||||
value = value.slice(1)
|
||||
}
|
||||
|
||||
const lowerValue = value.toLowerCase()
|
||||
if (lowerValue.includes("linkedin.com")) {
|
||||
try {
|
||||
let url: URL
|
||||
if (value.startsWith("http://") || value.startsWith("https://")) {
|
||||
url = new URL(value)
|
||||
} else {
|
||||
url = new URL(`https://${value}`)
|
||||
}
|
||||
|
||||
const pathMatch = url.pathname.match(/\/(in|pub)\/([^/\s?#]+)/i)
|
||||
const handle = pathMatch?.[2]
|
||||
if (handle) {
|
||||
return handle
|
||||
}
|
||||
} catch {
|
||||
const match = value.match(/linkedin\.com\/(?:in|pub)\/([^/\s?#]+)/i)
|
||||
const handle = match?.[1]
|
||||
if (handle) {
|
||||
return handle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value.includes("/in/") || value.includes("/pub/")) {
|
||||
const match = value.match(/\/(?:in|pub)\/([^/\s?#]+)/i)
|
||||
const handle = match?.[1]
|
||||
if (handle) {
|
||||
return handle
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts X/Twitter handle to full profile URL.
|
||||
*/
|
||||
export function toXProfileUrl(handle: string): string {
|
||||
if (!handle.trim()) return ""
|
||||
return `https://x.com/${handle.trim()}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts LinkedIn handle to full profile URL.
|
||||
*/
|
||||
export function toLinkedInProfileUrl(handle: string): string {
|
||||
if (!handle.trim()) return ""
|
||||
return `https://linkedin.com/in/${handle.trim()}`
|
||||
}
|
||||
|
|
@ -2,6 +2,19 @@ import { withSentryConfig } from "@sentry/nextjs"
|
|||
import type { NextConfig } from "next"
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
transpilePackages: [
|
||||
"@tiptap/core",
|
||||
"@tiptap/react",
|
||||
"@tiptap/pm",
|
||||
"@tiptap/starter-kit",
|
||||
"@tiptap/extension-placeholder",
|
||||
"@tiptap/extension-link",
|
||||
"@tiptap/extension-image",
|
||||
"@tiptap/extension-task-list",
|
||||
"@tiptap/extension-task-item",
|
||||
"@tiptap/suggestion",
|
||||
"@tiptap/markdown",
|
||||
],
|
||||
experimental: {
|
||||
viewTransition: true,
|
||||
turbopackFileSystemCacheForDev: true,
|
||||
|
|
|
|||
|
|
@ -15,13 +15,15 @@
|
|||
"postdeploy": "bun run sentry:sourcemaps"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/google": "^2.0.0-beta.13",
|
||||
"@ai-sdk/react": "2.0.0-beta.24",
|
||||
"@ai-sdk/google": "^3.0.9",
|
||||
"@ai-sdk/react": "^3.0.39",
|
||||
"@ai-sdk/xai": "^3.0.23",
|
||||
"@better-fetch/fetch": "^1.1.18",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@floating-ui/react": "^0.27.0",
|
||||
"@opennextjs/cloudflare": "^1.12.0",
|
||||
"@radix-ui/react-accordion": "^1.2.11",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
|
|
@ -48,8 +50,19 @@
|
|||
"@tanstack/react-query-devtools": "^5.84.2",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@tanstack/react-virtual": "^3.13.12",
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/extension-image": "^3.15.3",
|
||||
"@tiptap/extension-link": "^3.15.3",
|
||||
"@tiptap/extension-placeholder": "^3.15.3",
|
||||
"@tiptap/extension-task-item": "^3.15.3",
|
||||
"@tiptap/extension-task-list": "^3.15.3",
|
||||
"@tiptap/markdown": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3",
|
||||
"@tiptap/react": "^3.15.3",
|
||||
"@tiptap/starter-kit": "^3.15.3",
|
||||
"@tiptap/suggestion": "^3.15.3",
|
||||
"@types/dompurify": "^3.2.0",
|
||||
"ai": "5.0.0-beta.24",
|
||||
"ai": "^6.0.35",
|
||||
"autumn-js": "0.0.116",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
|
@ -85,6 +98,7 @@
|
|||
"streamdown": "^1.1.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tw-animate-css": "^1.3.4",
|
||||
"use-debounce": "^10.1.0",
|
||||
"vaul": "^1.1.2",
|
||||
"zustand": "^5.0.7"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
export const isValidUrl = (url: string): boolean => {
|
||||
try {
|
||||
new URL(url)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizeUrl = (url: string): string => {
|
||||
if (!url.trim()) return ""
|
||||
if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||
return url
|
||||
}
|
||||
return `https://${url}`
|
||||
}
|
||||
|
||||
export const isTwitterUrl = (url: string): boolean => {
|
||||
const normalizedUrl = url.toLowerCase()
|
||||
return (
|
||||
normalizedUrl.includes("twitter.com") || normalizedUrl.includes("x.com")
|
||||
)
|
||||
}
|
||||
|
||||
export const isLinkedInProfileUrl = (url: string): boolean => {
|
||||
const normalizedUrl = url.toLowerCase()
|
||||
return (
|
||||
normalizedUrl.includes("linkedin.com/in/") &&
|
||||
!normalizedUrl.includes("linkedin.com/company/")
|
||||
)
|
||||
}
|
||||
|
||||
export const collectValidUrls = (
|
||||
linkedinProfile: string,
|
||||
otherLinks: string[],
|
||||
): string[] => {
|
||||
const urls: string[] = []
|
||||
|
||||
if (linkedinProfile.trim()) {
|
||||
const normalizedLinkedIn = normalizeUrl(linkedinProfile.trim())
|
||||
if (
|
||||
isValidUrl(normalizedLinkedIn) &&
|
||||
isLinkedInProfileUrl(normalizedLinkedIn)
|
||||
) {
|
||||
urls.push(normalizedLinkedIn)
|
||||
}
|
||||
}
|
||||
|
||||
otherLinks
|
||||
.filter((link) => link.trim())
|
||||
.forEach((link) => {
|
||||
const normalizedLink = normalizeUrl(link.trim())
|
||||
if (isValidUrl(normalizedLink) && !isTwitterUrl(normalizedLink)) {
|
||||
urls.push(normalizedLink)
|
||||
}
|
||||
})
|
||||
|
||||
return urls
|
||||
}
|
||||
208
bun.lock
208
bun.lock
|
|
@ -138,13 +138,15 @@
|
|||
"name": "@repo/web",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/google": "^2.0.0-beta.13",
|
||||
"@ai-sdk/react": "2.0.0-beta.24",
|
||||
"@ai-sdk/google": "^3.0.9",
|
||||
"@ai-sdk/react": "^3.0.39",
|
||||
"@ai-sdk/xai": "^3.0.23",
|
||||
"@better-fetch/fetch": "^1.1.18",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@floating-ui/react": "^0.27.0",
|
||||
"@opennextjs/cloudflare": "^1.12.0",
|
||||
"@radix-ui/react-accordion": "^1.2.11",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
|
|
@ -171,8 +173,19 @@
|
|||
"@tanstack/react-query-devtools": "^5.84.2",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@tanstack/react-virtual": "^3.13.12",
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/extension-image": "^3.15.3",
|
||||
"@tiptap/extension-link": "^3.15.3",
|
||||
"@tiptap/extension-placeholder": "^3.15.3",
|
||||
"@tiptap/extension-task-item": "^3.15.3",
|
||||
"@tiptap/extension-task-list": "^3.15.3",
|
||||
"@tiptap/markdown": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3",
|
||||
"@tiptap/react": "^3.15.3",
|
||||
"@tiptap/starter-kit": "^3.15.3",
|
||||
"@tiptap/suggestion": "^3.15.3",
|
||||
"@types/dompurify": "^3.2.0",
|
||||
"ai": "5.0.0-beta.24",
|
||||
"ai": "^6.0.35",
|
||||
"autumn-js": "0.0.116",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
|
@ -208,6 +221,7 @@
|
|||
"streamdown": "^1.1.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tw-animate-css": "^1.3.4",
|
||||
"use-debounce": "^10.1.0",
|
||||
"vaul": "^1.1.2",
|
||||
"zustand": "^5.0.7",
|
||||
},
|
||||
|
|
@ -372,10 +386,12 @@
|
|||
|
||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="],
|
||||
|
||||
"@ai-sdk/react": ["@ai-sdk/react@2.0.0-beta.24", "", { "dependencies": { "@ai-sdk/provider-utils": "3.0.0-beta.5", "ai": "5.0.0-beta.24", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.25.76 || ^4" }, "optionalPeers": ["zod"] }, "sha512-Hfgb+z7MteHMaSdQ3G8hhTDridmUk2fkuRa17OaFlsfySYaFQd/exC0EBJlT+05ljcJPInyurdrxbgZLtsoPpg=="],
|
||||
"@ai-sdk/react": ["@ai-sdk/react@3.0.39", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.7", "ai": "6.0.37", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-Q/39hAazwxItCbqEDWC4pa3+HXLVvTzeu/zu7ghANqRNCxzmqBb/GYEIU/wiYNRS05hP6R1l9bA9S4U6sx0iTA=="],
|
||||
|
||||
"@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="],
|
||||
|
||||
"@ai-sdk/xai": ["@ai-sdk/xai@3.0.23", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.10", "@ai-sdk/provider": "3.0.3", "@ai-sdk/provider-utils": "4.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-sWWJK0lGNFWfGTzgFkgTRZUeXycaXY3QufMfPwev2uRYvRNF6QFGZn2Ut3PM70OVl8NP6DHMnYl0loM4VyC7ug=="],
|
||||
|
||||
"@aklinker1/rollup-plugin-visualizer": ["@aklinker1/rollup-plugin-visualizer@5.12.0", "", { "dependencies": { "open": "^8.4.0", "picomatch": "^2.3.1", "source-map": "^0.7.4", "yargs": "^17.5.1" }, "peerDependencies": { "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-X24LvEGw6UFmy0lpGJDmXsMyBD58XmX1bbwsaMLhNoM+UMQfQ3b2RtC+nz4b/NoRK5r6QJSKJHBNVeUdwqybaQ=="],
|
||||
|
||||
"@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-jsElTJ0sQ4wHRz+C45tfect76BwbTbgkgKByOzpCN9xG61N5V6u/glvg1CsNJhq2xJIFpKHSwG3D2wPPuEYOrQ=="],
|
||||
|
|
@ -782,6 +798,8 @@
|
|||
|
||||
"@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="],
|
||||
|
||||
"@floating-ui/react": ["@floating-ui/react@0.27.16", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.6", "@floating-ui/utils": "^0.2.10", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=17.0.0", "react-dom": ">=17.0.0" } }, "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g=="],
|
||||
|
||||
"@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="],
|
||||
|
||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
|
||||
|
|
@ -1306,6 +1324,8 @@
|
|||
|
||||
"@react-router/serve": ["@react-router/serve@7.12.0", "", { "dependencies": { "@mjackson/node-fetch-server": "^0.2.0", "@react-router/express": "7.12.0", "@react-router/node": "7.12.0", "compression": "^1.8.1", "express": "^4.19.2", "get-port": "5.1.1", "morgan": "^1.10.1", "source-map-support": "^0.5.21" }, "peerDependencies": { "react-router": "7.12.0" }, "bin": { "react-router-serve": "bin.js" } }, "sha512-j1ltgU7s3wAwOosZ5oxgHSsmVyK706gY/yIs8qVmC239wQ3zr3eqaXk3TVVLMeRy+eDgPNmgc6oNJv2o328VgA=="],
|
||||
|
||||
"@remirror/core-constants": ["@remirror/core-constants@3.0.0", "", {}, "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg=="],
|
||||
|
||||
"@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.9.0", "", {}, "sha512-SoLMv7dbH+njWzXnOY6fI08dFMI5+/dQ+vY3n8RnnbdG7MdJEgiP28Xj/xWlnRnED/aB6SFw56Zop+LbmaaKqA=="],
|
||||
|
||||
"@repo/docs": ["@repo/docs@workspace:apps/docs"],
|
||||
|
|
@ -1698,6 +1718,74 @@
|
|||
|
||||
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.18", "", {}, "sha512-Mx86Hqu1k39icq2Zusq+Ey2J6dDWTjDvEv43PJtRCoEYTLyfaPnxIQ6iy7YAOK0NV/qOEmZQ/uCufrppZxTgcg=="],
|
||||
|
||||
"@tiptap/core": ["@tiptap/core@3.15.3", "", { "peerDependencies": { "@tiptap/pm": "^3.15.3" } }, "sha512-bmXydIHfm2rEtGju39FiQNfzkFx9CDvJe+xem1dgEZ2P6Dj7nQX9LnA1ZscW7TuzbBRkL5p3dwuBIi3f62A66A=="],
|
||||
|
||||
"@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-13x5UsQXtttFpoS/n1q173OeurNxppsdWgP3JfsshzyxIghhC141uL3H6SGYQLPU31AizgDs2OEzt6cSUevaZg=="],
|
||||
|
||||
"@tiptap/extension-bold": ["@tiptap/extension-bold@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-I8JYbkkUTNUXbHd/wCse2bR0QhQtJD7+0/lgrKOmGfv5ioLxcki079Nzuqqay3PjgYoJLIJQvm3RAGxT+4X91w=="],
|
||||
|
||||
"@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.15.3", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-e88DG1bTy6hKxrt7iPVQhJnH5/EOrnKpIyp09dfRDgWrrW88fE0Qjys7a/eT8W+sXyXM3z10Ye7zpERWsrLZDg=="],
|
||||
|
||||
"@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.15.3", "", { "peerDependencies": { "@tiptap/extension-list": "^3.15.3" } }, "sha512-MGwEkNT7ltst6XaWf0ObNgpKQ4PvuuV3igkBrdYnQS+qaAx9IF4isygVPqUc9DvjYC306jpyKsNqNrENIXcosA=="],
|
||||
|
||||
"@tiptap/extension-code": ["@tiptap/extension-code@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-x6LFt3Og6MFINYpsMzrJnz7vaT9Yk1t4oXkbJsJRSavdIWBEBcoRudKZ4sSe/AnsYlRJs8FY2uR76mt9e+7xAQ=="],
|
||||
|
||||
"@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-q1UB9icNfdJppTqMIUWfoRKkx5SSdWIpwZoL2NeOI5Ah3E20/dQKVttIgLhsE521chyvxCYCRaHD5tMNGKfhyw=="],
|
||||
|
||||
"@tiptap/extension-document": ["@tiptap/extension-document@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-AC72nI2gnogBuETCKbZekn+h6t5FGGcZG2abPGKbz/x9rwpb6qV2hcbAQ30t6M7H6cTOh2/Ut8bEV2MtMB15sw=="],
|
||||
|
||||
"@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.15.3", "", { "peerDependencies": { "@tiptap/extensions": "^3.15.3" } }, "sha512-jGI5XZpdo8GSYQFj7HY15/oEwC2m2TqZz0/Fln5qIhY32XlZhWrsMuMI6WbUJrTH16es7xO6jmRlDsc6g+vJWg=="],
|
||||
|
||||
"@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.15.3", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-+3DVBleKKffadEJEdLYxmYAJOjHjLSqtiSFUE3RABT4V2ka1ODy2NIpyKX0o1SvQ5N1jViYT9Q+yUbNa6zCcDw=="],
|
||||
|
||||
"@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.15.3", "", { "peerDependencies": { "@tiptap/extensions": "^3.15.3" } }, "sha512-Kaw0sNzP0bQI/xEAMSfIpja6xhsu9WqqAK/puzOIS1RKWO47Wps/tzqdSJ9gfslPIb5uY5mKCfy8UR8Xgiia8w=="],
|
||||
|
||||
"@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-8HjxmeRbBiXW+7JKemAJtZtHlmXQ9iji398CPQ0yYde68WbIvUhHXjmbJE5pxFvvQTJ/zJv1aISeEOZN2bKBaw=="],
|
||||
|
||||
"@tiptap/extension-heading": ["@tiptap/extension-heading@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-G1GG6iN1YXPS+75arDpo+bYRzhr3dNDw99c7D7na3aDawa9Qp7sZ/bVrzFUUcVEce0cD6h83yY7AooBxEc67hA=="],
|
||||
|
||||
"@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-FYkN7L6JsfwwNEntmLklCVKvgL0B0N47OXMacRk6kYKQmVQ4Nvc7q/VJLpD9sk4wh4KT1aiCBfhKEBTu5pv1fg=="],
|
||||
|
||||
"@tiptap/extension-image": ["@tiptap/extension-image@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-Tjq9BHlC/0bGR9/uySA0tv6I1Ua1Q5t5P/mdbWyZi4JdUpKHRfgenzfXF5DYnklJ01QJ7uOPSp9sAGgPzBixtQ=="],
|
||||
|
||||
"@tiptap/extension-italic": ["@tiptap/extension-italic@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-6XeuPjcWy7OBxpkgOV7bD6PATO5jhIxc8SEK4m8xn8nelGTBIbHGqK37evRv+QkC7E0MUryLtzwnmmiaxcKL0Q=="],
|
||||
|
||||
"@tiptap/extension-link": ["@tiptap/extension-link@3.15.3", "", { "dependencies": { "linkifyjs": "^4.3.2" }, "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-PdDXyBF9Wco9U1x6e+b7tKBWG+kqBDXDmaYXHkFm/gYuQCQafVJ5mdrDdKgkHDWVnJzMWZXBcZjT9r57qtlLWg=="],
|
||||
|
||||
"@tiptap/extension-list": ["@tiptap/extension-list@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-n7y/MF9lAM5qlpuH5IR4/uq+kJPEJpe9NrEiH+NmkO/5KJ6cXzpJ6F4U17sMLf2SNCq+TWN9QK8QzoKxIn50VQ=="],
|
||||
|
||||
"@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.15.3", "", { "peerDependencies": { "@tiptap/extension-list": "^3.15.3" } }, "sha512-CCxL5ek1p0lO5e8aqhnPzIySldXRSigBFk2fP9OLgdl5qKFLs2MGc19jFlx5+/kjXnEsdQTFbGY1Sizzt0TVDw=="],
|
||||
|
||||
"@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.15.3", "", { "peerDependencies": { "@tiptap/extension-list": "^3.15.3" } }, "sha512-UxqnTEEAKrL+wFQeSyC9z0mgyUUVRS2WTcVFoLZCE6/Xus9F53S4bl7VKFadjmqI4GpDk5Oe2IOUc72o129jWg=="],
|
||||
|
||||
"@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.15.3", "", { "peerDependencies": { "@tiptap/extension-list": "^3.15.3" } }, "sha512-/8uhw528Iy0c9wF6tHCiIn0ToM0Ml6Ll2c/3iPRnKr4IjXwx2Lr994stUFihb+oqGZwV1J8CPcZJ4Ufpdqi4Dw=="],
|
||||
|
||||
"@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-lc0Qu/1AgzcEfS67NJMj5tSHHhH6NtA6uUpvppEKGsvJwgE2wKG1onE4isrVXmcGRdxSMiCtyTDemPNMu6/ozQ=="],
|
||||
|
||||
"@tiptap/extension-placeholder": ["@tiptap/extension-placeholder@3.15.3", "", { "peerDependencies": { "@tiptap/extensions": "^3.15.3" } }, "sha512-XcHHnojT186hKIoOgcPBesXk89+caNGVUdMtc171Vcr/5s0dpnr4q5LfE+YRC+S85CpCxCRRnh84Ou+XRtOqrw=="],
|
||||
|
||||
"@tiptap/extension-strike": ["@tiptap/extension-strike@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-Y1P3eGNY7RxQs2BcR6NfLo9VfEOplXXHAqkOM88oowWWOE7dMNeFFZM9H8HNxoQgXJ7H0aWW9B7ZTWM9hWli2Q=="],
|
||||
|
||||
"@tiptap/extension-task-item": ["@tiptap/extension-task-item@3.15.3", "", { "peerDependencies": { "@tiptap/extension-list": "^3.15.3" } }, "sha512-bkrmouc1rE5n9ONw2G7+zCGfBRoF2HJWq8REThPMzg/6+L5GJJ5YTN4UmncaP48U9jHX8xeihjgg9Ypenjl4lw=="],
|
||||
|
||||
"@tiptap/extension-task-list": ["@tiptap/extension-task-list@3.15.3", "", { "peerDependencies": { "@tiptap/extension-list": "^3.15.3" } }, "sha512-nh8iBk1LHVIoqxphLoqZlLAN9fF2i9ZeK+2TjGSS35lfh7sYzRoSjNW0E81Uy48YuCzM1NQYghYR5Qfc7vm4jA=="],
|
||||
|
||||
"@tiptap/extension-text": ["@tiptap/extension-text@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-MhkBz8ZvrqOKtKNp+ZWISKkLUlTrDR7tbKZc2OnNcUTttL9dz0HwT+cg91GGz19fuo7ttDcfsPV6eVmflvGToA=="],
|
||||
|
||||
"@tiptap/extension-underline": ["@tiptap/extension-underline@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3" } }, "sha512-r/IwcNN0W366jGu4Y0n2MiFq9jGa4aopOwtfWO4d+J0DyeS2m7Go3+KwoUqi0wQTiVU74yfi4DF6eRsMQ9/iHQ=="],
|
||||
|
||||
"@tiptap/extensions": ["@tiptap/extensions@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-ycx/BgxR4rc9tf3ZyTdI98Z19yKLFfqM3UN+v42ChuIwkzyr9zyp7kG8dB9xN2lNqrD+5y/HyJobz/VJ7T90gA=="],
|
||||
|
||||
"@tiptap/markdown": ["@tiptap/markdown@3.15.3", "", { "dependencies": { "marked": "^15.0.12" }, "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-JjjZ/X7H2+/Jeapk8GurbncJVyG9ai5YD/eJLBKDyWqsoQwsrHbDlYBx26q9J5VwWXzxe9G6fmHNlAfw0Pokow=="],
|
||||
|
||||
"@tiptap/pm": ["@tiptap/pm@3.15.3", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.24.1", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.5.0", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.38.1" } }, "sha512-Zm1BaU1TwFi3CQiisxjgnzzIus+q40bBKWLqXf6WEaus8Z6+vo1MT2pU52dBCMIRaW9XNDq3E5cmGtMc1AlveA=="],
|
||||
|
||||
"@tiptap/react": ["@tiptap/react@3.15.3", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "fast-equals": "^5.3.3", "use-sync-external-store": "^1.4.0" }, "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.15.3", "@tiptap/extension-floating-menu": "^3.15.3" }, "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-XvouB+Hrqw8yFmZLPEh+HWlMeRSjZfHSfWfWuw5d8LSwnxnPeu3Bg/rjHrRrdwb+7FumtzOnNWMorpb/PSOttQ=="],
|
||||
|
||||
"@tiptap/starter-kit": ["@tiptap/starter-kit@3.15.3", "", { "dependencies": { "@tiptap/core": "^3.15.3", "@tiptap/extension-blockquote": "^3.15.3", "@tiptap/extension-bold": "^3.15.3", "@tiptap/extension-bullet-list": "^3.15.3", "@tiptap/extension-code": "^3.15.3", "@tiptap/extension-code-block": "^3.15.3", "@tiptap/extension-document": "^3.15.3", "@tiptap/extension-dropcursor": "^3.15.3", "@tiptap/extension-gapcursor": "^3.15.3", "@tiptap/extension-hard-break": "^3.15.3", "@tiptap/extension-heading": "^3.15.3", "@tiptap/extension-horizontal-rule": "^3.15.3", "@tiptap/extension-italic": "^3.15.3", "@tiptap/extension-link": "^3.15.3", "@tiptap/extension-list": "^3.15.3", "@tiptap/extension-list-item": "^3.15.3", "@tiptap/extension-list-keymap": "^3.15.3", "@tiptap/extension-ordered-list": "^3.15.3", "@tiptap/extension-paragraph": "^3.15.3", "@tiptap/extension-strike": "^3.15.3", "@tiptap/extension-text": "^3.15.3", "@tiptap/extension-underline": "^3.15.3", "@tiptap/extensions": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-ia+eQr9Mt1ln2UO+kK4kFTJOrZK4GhvZXFjpCCYuHtco3rhr2fZAIxEEY4cl/vo5VO5WWyPqxhkFeLcoWmNjSw=="],
|
||||
|
||||
"@tiptap/suggestion": ["@tiptap/suggestion@3.15.3", "", { "peerDependencies": { "@tiptap/core": "^3.15.3", "@tiptap/pm": "^3.15.3" } }, "sha512-+CbaHhPfKUe+fNpUIQaOPhh6xI+xL5jbK1zw++U+CZIRrVAAmHRhO+D0O2HdiE1RK7596y8bRqMiB2CRHF7emA=="],
|
||||
|
||||
"@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
|
||||
|
||||
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
||||
|
|
@ -1836,10 +1924,16 @@
|
|||
|
||||
"@types/katex": ["@types/katex@0.16.8", "", {}, "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg=="],
|
||||
|
||||
"@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
|
||||
|
||||
"@types/lodash": ["@types/lodash@4.17.23", "", {}, "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA=="],
|
||||
|
||||
"@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
|
||||
|
||||
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||
|
||||
"@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
|
||||
|
||||
"@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="],
|
||||
|
||||
"@types/minimatch": ["@types/minimatch@3.0.5", "", {}, "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="],
|
||||
|
|
@ -1874,6 +1968,8 @@
|
|||
|
||||
"@types/urijs": ["@types/urijs@1.19.26", "", {}, "sha512-wkXrVzX5yoqLnndOwFsieJA7oKM8cNkOKJtf/3vVGSUFkWDKZvFHpIl9Pvqb/T9UsawBBFMTTD8xu7sK5MWuvg=="],
|
||||
|
||||
"@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="],
|
||||
|
||||
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.53.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/type-utils": "8.53.0", "@typescript-eslint/utils": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.53.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg=="],
|
||||
|
|
@ -2336,6 +2432,8 @@
|
|||
|
||||
"cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="],
|
||||
|
||||
"crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="],
|
||||
|
||||
"cron-schedule": ["cron-schedule@6.0.0", "", {}, "sha512-BoZaseYGXOo5j5HUwTaegIog3JJbuH4BbrY9A1ArLjXpy+RWb3mV28F/9Gv1dDA7E2L8kngWva4NWisnLTyfgQ=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
|
@ -3266,6 +3364,10 @@
|
|||
|
||||
"linkedom": ["linkedom@0.18.12", "", { "dependencies": { "css-select": "^5.1.0", "cssom": "^0.5.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "uhyphen": "^0.2.0" }, "peerDependencies": { "canvas": ">= 2" }, "optionalPeers": ["canvas"] }, "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q=="],
|
||||
|
||||
"linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="],
|
||||
|
||||
"linkifyjs": ["linkifyjs@4.3.2", "", {}, "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA=="],
|
||||
|
||||
"listr2": ["listr2@8.3.3", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ=="],
|
||||
|
||||
"loader-runner": ["loader-runner@4.3.1", "", {}, "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q=="],
|
||||
|
|
@ -3316,9 +3418,11 @@
|
|||
|
||||
"markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="],
|
||||
|
||||
"markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="],
|
||||
|
||||
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
|
||||
|
||||
"marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
|
||||
"marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
|
||||
|
||||
"marky": ["marky@1.3.0", "", {}, "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ=="],
|
||||
|
||||
|
|
@ -3362,6 +3466,8 @@
|
|||
|
||||
"mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
|
||||
|
||||
"mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="],
|
||||
|
||||
"media-query-parser": ["media-query-parser@2.0.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" } }, "sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w=="],
|
||||
|
||||
"media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
|
||||
|
|
@ -3614,6 +3720,8 @@
|
|||
|
||||
"ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="],
|
||||
|
||||
"orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="],
|
||||
|
||||
"os-shim": ["os-shim@0.1.3", "", {}, "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A=="],
|
||||
|
||||
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
|
||||
|
|
@ -3792,6 +3900,42 @@
|
|||
|
||||
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
|
||||
|
||||
"prosemirror-changeset": ["prosemirror-changeset@2.3.1", "", { "dependencies": { "prosemirror-transform": "^1.0.0" } }, "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ=="],
|
||||
|
||||
"prosemirror-collab": ["prosemirror-collab@1.3.1", "", { "dependencies": { "prosemirror-state": "^1.0.0" } }, "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ=="],
|
||||
|
||||
"prosemirror-commands": ["prosemirror-commands@1.7.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.10.2" } }, "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w=="],
|
||||
|
||||
"prosemirror-dropcursor": ["prosemirror-dropcursor@1.8.2", "", { "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0", "prosemirror-view": "^1.1.0" } }, "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw=="],
|
||||
|
||||
"prosemirror-gapcursor": ["prosemirror-gapcursor@1.4.0", "", { "dependencies": { "prosemirror-keymap": "^1.0.0", "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-view": "^1.0.0" } }, "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ=="],
|
||||
|
||||
"prosemirror-history": ["prosemirror-history@1.5.0", "", { "dependencies": { "prosemirror-state": "^1.2.2", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.31.0", "rope-sequence": "^1.3.0" } }, "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg=="],
|
||||
|
||||
"prosemirror-inputrules": ["prosemirror-inputrules@1.5.1", "", { "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw=="],
|
||||
|
||||
"prosemirror-keymap": ["prosemirror-keymap@1.2.3", "", { "dependencies": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw=="],
|
||||
|
||||
"prosemirror-markdown": ["prosemirror-markdown@1.13.2", "", { "dependencies": { "@types/markdown-it": "^14.0.0", "markdown-it": "^14.0.0", "prosemirror-model": "^1.25.0" } }, "sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g=="],
|
||||
|
||||
"prosemirror-menu": ["prosemirror-menu@1.2.5", "", { "dependencies": { "crelt": "^1.0.0", "prosemirror-commands": "^1.0.0", "prosemirror-history": "^1.0.0", "prosemirror-state": "^1.0.0" } }, "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ=="],
|
||||
|
||||
"prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="],
|
||||
|
||||
"prosemirror-schema-basic": ["prosemirror-schema-basic@1.2.4", "", { "dependencies": { "prosemirror-model": "^1.25.0" } }, "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ=="],
|
||||
|
||||
"prosemirror-schema-list": ["prosemirror-schema-list@1.5.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.7.3" } }, "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q=="],
|
||||
|
||||
"prosemirror-state": ["prosemirror-state@1.4.4", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.27.0" } }, "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw=="],
|
||||
|
||||
"prosemirror-tables": ["prosemirror-tables@1.8.5", "", { "dependencies": { "prosemirror-keymap": "^1.2.3", "prosemirror-model": "^1.25.4", "prosemirror-state": "^1.4.4", "prosemirror-transform": "^1.10.5", "prosemirror-view": "^1.41.4" } }, "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw=="],
|
||||
|
||||
"prosemirror-trailing-node": ["prosemirror-trailing-node@3.0.0", "", { "dependencies": { "@remirror/core-constants": "3.0.0", "escape-string-regexp": "^4.0.0" }, "peerDependencies": { "prosemirror-model": "^1.22.1", "prosemirror-state": "^1.4.2", "prosemirror-view": "^1.33.8" } }, "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ=="],
|
||||
|
||||
"prosemirror-transform": ["prosemirror-transform@1.10.5", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw=="],
|
||||
|
||||
"prosemirror-view": ["prosemirror-view@1.41.5", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-UDQbIPnDrjE8tqUBbPmCOZgtd75htE6W3r0JCmY9bL6W1iemDM37MZEKC49d+tdQ0v/CKx4gjxLoLsfkD2NiZA=="],
|
||||
|
||||
"proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="],
|
||||
|
||||
"protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
|
||||
|
|
@ -3810,6 +3954,8 @@
|
|||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="],
|
||||
|
||||
"pupa": ["pupa@3.3.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA=="],
|
||||
|
||||
"puppeteer": ["puppeteer@22.14.0", "", { "dependencies": { "@puppeteer/browsers": "2.3.0", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1312386", "puppeteer-core": "22.14.0" }, "bin": { "puppeteer": "lib/esm/puppeteer/node/cli.js" } }, "sha512-MGTR6/pM8zmWbTdazb6FKnwIihzsSEXBPH49mFFU96DNZpQOevCAZMnjBZGlZRGRzRK6aADCavR6SQtrbv5dQw=="],
|
||||
|
|
@ -3998,6 +4144,8 @@
|
|||
|
||||
"rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="],
|
||||
|
||||
"rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="],
|
||||
|
||||
"rou3": ["rou3@0.6.3", "", {}, "sha512-1HSG1ENTj7Kkm5muMnXuzzfdDOf7CFnbSYFA+H3Fp/rB9lOCxCPgy1jlZxTKyFoC5jJay8Mmc+VbPLYRjzYLrA=="],
|
||||
|
||||
"roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
|
||||
|
|
@ -4218,6 +4366,8 @@
|
|||
|
||||
"swr": ["swr@2.3.8", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w=="],
|
||||
|
||||
"tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
|
||||
|
||||
"tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
|
||||
|
|
@ -4346,6 +4496,8 @@
|
|||
|
||||
"typescript-eslint": ["typescript-eslint@8.53.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.53.0", "@typescript-eslint/parser": "8.53.0", "@typescript-eslint/typescript-estree": "8.53.0", "@typescript-eslint/utils": "8.53.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-xHURCQNxZ1dsWn0sdOaOfCSQG0HKeqSj9OexIxrz6ypU6wHYOdX2I3D2b8s8wFSsSOYJb+6q283cLiLlkEsBYw=="],
|
||||
|
||||
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
|
||||
|
||||
"ufo": ["ufo@1.6.2", "", {}, "sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q=="],
|
||||
|
||||
"uhyphen": ["uhyphen@0.2.0", "", {}, "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA=="],
|
||||
|
|
@ -4420,6 +4572,8 @@
|
|||
|
||||
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
||||
|
||||
"use-debounce": ["use-debounce@10.1.0", "", { "peerDependencies": { "react": "*" } }, "sha512-lu87Za35V3n/MyMoEpD5zJv0k7hCn0p+V/fK2kWD+3k2u3kOCwO593UArbczg1fhfs2rqPEnHpULJ3KmGdDzvg=="],
|
||||
|
||||
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
|
||||
|
||||
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
||||
|
|
@ -4466,6 +4620,8 @@
|
|||
|
||||
"vscode-uri": ["vscode-uri@3.0.8", "", {}, "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="],
|
||||
|
||||
"w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
|
||||
|
||||
"warning": ["warning@4.0.3", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w=="],
|
||||
|
||||
"watchpack": ["watchpack@2.4.4", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA=="],
|
||||
|
|
@ -4598,12 +4754,18 @@
|
|||
|
||||
"@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"@ai-sdk/react/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-beta.5", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-4Dv/wiGZrvO6fI7P0yMLa4XZru0XW8LPibTObbkHBdweLUVGIze7aCfxxQeY44Uqcbl/h6/yBTkx2XmPtwf/Ow=="],
|
||||
"@ai-sdk/react/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.7", "", { "dependencies": { "@ai-sdk/provider": "3.0.3", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w=="],
|
||||
|
||||
"@ai-sdk/react/ai": ["ai@5.0.0-beta.24", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-beta.10", "@ai-sdk/provider": "2.0.0-beta.1", "@ai-sdk/provider-utils": "3.0.0-beta.5", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" }, "bin": { "ai": "dist/bin/ai.min.js" } }, "sha512-glQIA+PGEP+UEPB+thdqNZi9Ot4Yjiqsl071S1KPaRTGHmBIg/c8OYb2mXCRM+3cNCFGVnCTudZoYUVNwBpFxg=="],
|
||||
"@ai-sdk/react/ai": ["ai@6.0.37", "", { "dependencies": { "@ai-sdk/gateway": "3.0.15", "@ai-sdk/provider": "3.0.3", "@ai-sdk/provider-utils": "4.0.7", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-GUMBYx0TXKxXRcWy6DY3ryHJZ7cATxc99WPT7WCD8hGCYkdhFVItKPTtINeTlK+FlUomDqzjPGtiDcVftcw//w=="],
|
||||
|
||||
"@ai-sdk/ui-utils/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="],
|
||||
|
||||
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.10", "", { "dependencies": { "@ai-sdk/provider": "3.0.3", "@ai-sdk/provider-utils": "4.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-dM+CXjW3+QAZVl2NKhch9T5gCa8p7CMJR16KtaRS9HzY8H/ASD8hiAssnDGz00XyOKYpDkd9Mj2mwK25u5LxnQ=="],
|
||||
|
||||
"@ai-sdk/xai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg=="],
|
||||
|
||||
"@ai-sdk/xai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.6", "", { "dependencies": { "@ai-sdk/provider": "3.0.3", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-o/SP1GQOrpXAzHjMosPHI0Pu+YkwxIMndSjSLrEXtcVixdrjqrGaA9I7xJcWf+XpRFJ9byPHrKYnprwS+36gMg=="],
|
||||
|
||||
"@aklinker1/rollup-plugin-visualizer/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
||||
|
||||
"@aklinker1/rollup-plugin-visualizer/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
|
@ -5040,9 +5202,9 @@
|
|||
|
||||
"@repo/docs/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"@repo/web/@ai-sdk/google": ["@ai-sdk/google@2.0.52", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2XUnGi3f7TV4ujoAhA+Fg3idUoG/+Y2xjCRg70a1/m0DH1KSQqYaCboJ1C19y6ZHGdf5KNT20eJdswP6TvrY2g=="],
|
||||
"@repo/web/@ai-sdk/google": ["@ai-sdk/google@3.0.9", "", { "dependencies": { "@ai-sdk/provider": "3.0.3", "@ai-sdk/provider-utils": "4.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-whRdK0gCZL92UbEHdmQ6edm3cp5ZZSqwE79AIvTEaJR+BeaMUJchTxz/I5w9l3EHGOiDxrKYssklqO3z45KGXg=="],
|
||||
|
||||
"@repo/web/ai": ["ai@5.0.0-beta.24", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-beta.10", "@ai-sdk/provider": "2.0.0-beta.1", "@ai-sdk/provider-utils": "3.0.0-beta.5", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" }, "bin": { "ai": "dist/bin/ai.min.js" } }, "sha512-glQIA+PGEP+UEPB+thdqNZi9Ot4Yjiqsl071S1KPaRTGHmBIg/c8OYb2mXCRM+3cNCFGVnCTudZoYUVNwBpFxg=="],
|
||||
"@repo/web/ai": ["ai@6.0.35", "", { "dependencies": { "@ai-sdk/gateway": "3.0.14", "@ai-sdk/provider": "3.0.3", "@ai-sdk/provider-utils": "4.0.6", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MxgtU6CjnegH1rhRfomM0gptKxP6r+9sxbLvYq36C1l85+o0LacqbXLdNVYzqab+lHN4q7ZP3QS8wZq4YkZahA=="],
|
||||
|
||||
"@repo/web/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
|
|
@ -5354,6 +5516,8 @@
|
|||
|
||||
"memory-graph-playground/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
|
||||
|
||||
"mermaid/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
|
@ -5474,6 +5638,8 @@
|
|||
|
||||
"streamdown/lucide-react": ["lucide-react@0.542.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw=="],
|
||||
|
||||
"streamdown/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
|
||||
|
||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
|
@ -5564,11 +5730,15 @@
|
|||
|
||||
"wxt/vite-node": ["vite-node@5.2.0", "", { "dependencies": { "cac": "^6.7.14", "es-module-lexer": "^1.7.0", "obug": "^2.0.0", "pathe": "^2.0.3", "vite": "^7.2.2" }, "bin": { "vite-node": "dist/cli.mjs" } }, "sha512-7UT39YxUukIA97zWPXUGb0SGSiLexEGlavMwU3HDE6+d/HJhKLjLqu4eX2qv6SQiocdhKLRcusroDwXHQ6CnRQ=="],
|
||||
|
||||
"@ai-sdk/react/@ai-sdk/provider-utils/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0-beta.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Z8SPncMtS3RsoXITmT7NVwrAq6M44dmw0DoUOYJqNNtCu8iMWuxB8Nxsoqpa0uEEy9R1V1ZThJAXTYgjTUxl3w=="],
|
||||
"@ai-sdk/react/@ai-sdk/provider-utils/@ai-sdk/provider": ["@ai-sdk/provider@3.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg=="],
|
||||
|
||||
"@ai-sdk/react/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.0-beta.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.1", "@ai-sdk/provider-utils": "3.0.0-beta.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-v+LXXm8INLYAdxHnNMVAJ/B7k+Nejn5dCQMg/F8SRetB5dEQ4sbfimE+b6rawILJznnsy2fugUO1oFFXlUS5Yg=="],
|
||||
"@ai-sdk/react/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/react/ai/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0-beta.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Z8SPncMtS3RsoXITmT7NVwrAq6M44dmw0DoUOYJqNNtCu8iMWuxB8Nxsoqpa0uEEy9R1V1ZThJAXTYgjTUxl3w=="],
|
||||
"@ai-sdk/react/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.3", "@ai-sdk/provider-utils": "4.0.7", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OsWcXMfkF9U38YhU7926rYt4IAtJuZlnM1e8STN8hHb4qbiTaORBSXcFaJuOEZ1kYOf3DqqP7CxbmM543ePzIg=="],
|
||||
|
||||
"@ai-sdk/react/ai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg=="],
|
||||
|
||||
"@ai-sdk/xai/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@aklinker1/rollup-plugin-visualizer/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
|
||||
|
||||
|
|
@ -6076,13 +6246,15 @@
|
|||
|
||||
"@raycast/api/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
|
||||
|
||||
"@repo/web/@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
|
||||
"@repo/web/@ai-sdk/google/@ai-sdk/provider": ["@ai-sdk/provider@3.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg=="],
|
||||
|
||||
"@repo/web/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.0-beta.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.1", "@ai-sdk/provider-utils": "3.0.0-beta.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-v+LXXm8INLYAdxHnNMVAJ/B7k+Nejn5dCQMg/F8SRetB5dEQ4sbfimE+b6rawILJznnsy2fugUO1oFFXlUS5Yg=="],
|
||||
"@repo/web/@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.7", "", { "dependencies": { "@ai-sdk/provider": "3.0.3", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w=="],
|
||||
|
||||
"@repo/web/ai/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0-beta.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Z8SPncMtS3RsoXITmT7NVwrAq6M44dmw0DoUOYJqNNtCu8iMWuxB8Nxsoqpa0uEEy9R1V1ZThJAXTYgjTUxl3w=="],
|
||||
"@repo/web/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.14", "", { "dependencies": { "@ai-sdk/provider": "3.0.3", "@ai-sdk/provider-utils": "4.0.6", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-udVpkDaQ00jMcBvtGGvmkEBU31XidsHB4E8HIF9l7/H7lyjOS/EtXzN2adoupDg5j1/VjjSI3Ny5P1zHUvLyMA=="],
|
||||
|
||||
"@repo/web/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-beta.5", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-4Dv/wiGZrvO6fI7P0yMLa4XZru0XW8LPibTObbkHBdweLUVGIze7aCfxxQeY44Uqcbl/h6/yBTkx2XmPtwf/Ow=="],
|
||||
"@repo/web/ai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg=="],
|
||||
|
||||
"@repo/web/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.6", "", { "dependencies": { "@ai-sdk/provider": "3.0.3", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-o/SP1GQOrpXAzHjMosPHI0Pu+YkwxIMndSjSLrEXtcVixdrjqrGaA9I7xJcWf+XpRFJ9byPHrKYnprwS+36gMg=="],
|
||||
|
||||
"@stoplight/better-ajv-errors/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
|
|
@ -6608,6 +6780,10 @@
|
|||
|
||||
"@puppeteer/browsers/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"@repo/web/@ai-sdk/google/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@repo/web/ai/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"agents/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"agents/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const buttonVariants = cva(
|
|||
"rounded-xl !px-6 !py-3 bg-black border border-[#161F2C] hover:bg-[#161F2C] !h-[40px] cursor-pointer text-white",
|
||||
linkPreview:
|
||||
"rounded-xl !px-3 !py-1 bg-black border border-[#161F2C] hover:bg-[#161F2C] cursor-pointer text-white border border-[#161F2C]",
|
||||
insideOut: "shadow-inside-out rounded-full bg-[#0D121A]",
|
||||
insideOut: "shadow-inside-out rounded-full bg-[#0D121A] cursor-pointer",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue