start liveblocks integration
This commit is contained in:
parent
6e28d283cd
commit
0f1654e3dd
@ -1,4 +1,5 @@
|
|||||||
import Navbar from "@/components/editor/navbar"
|
import Navbar from "@/components/editor/navbar"
|
||||||
|
import { Room } from "@/components/editor/room"
|
||||||
import { Sandbox, User, UsersToSandboxes } from "@/lib/types"
|
import { Sandbox, User, UsersToSandboxes } from "@/lib/types"
|
||||||
import { currentUser } from "@clerk/nextjs"
|
import { currentUser } from "@clerk/nextjs"
|
||||||
import dynamic from "next/dynamic"
|
import dynamic from "next/dynamic"
|
||||||
@ -48,10 +49,12 @@ export default async function CodePage({ params }: { params: { id: string } }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden overscroll-none w-screen flex flex-col h-screen bg-background">
|
<div className="overflow-hidden overscroll-none w-screen flex flex-col h-screen bg-background">
|
||||||
|
{/* <Room> */}
|
||||||
<Navbar userData={userData} sandboxData={sandboxData} shared={shared} />
|
<Navbar userData={userData} sandboxData={sandboxData} shared={shared} />
|
||||||
<div className="w-screen flex grow">
|
<div className="w-screen flex grow">
|
||||||
<CodeEditor userData={userData} sandboxId={sandboxId} />
|
<CodeEditor userData={userData} sandboxId={sandboxId} />
|
||||||
</div>
|
</div>
|
||||||
|
{/* </Room> */}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { User } from "@/lib/types"
|
||||||
import { currentUser } from "@clerk/nextjs"
|
import { currentUser } from "@clerk/nextjs"
|
||||||
import { redirect } from "next/navigation"
|
import { redirect } from "next/navigation"
|
||||||
|
|
||||||
@ -13,9 +14,9 @@ export default async function AppAuthLayout({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dbUser = await fetch(`http://localhost:8787/api/user?id=${user.id}`)
|
const dbUser = await fetch(`http://localhost:8787/api/user?id=${user.id}`)
|
||||||
const dbUserJSON = await dbUser.json()
|
const dbUserJSON = (await dbUser.json()) as User
|
||||||
|
|
||||||
if (!dbUserJSON?.id) {
|
if (!dbUserJSON.id) {
|
||||||
const res = await fetch("http://localhost:8787/api/user", {
|
const res = await fetch("http://localhost:8787/api/user", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
43
frontend/app/api/lb-auth/route.ts
Normal file
43
frontend/app/api/lb-auth/route.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { User } from "@/lib/types"
|
||||||
|
import { currentUser } from "@clerk/nextjs"
|
||||||
|
import { Liveblocks } from "@liveblocks/node"
|
||||||
|
import { NextRequest } from "next/server"
|
||||||
|
|
||||||
|
const API_KEY = process.env.LIVEBLOCKS_SECRET_KEY!
|
||||||
|
|
||||||
|
const liveblocks = new Liveblocks({
|
||||||
|
secret: API_KEY!,
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
const clerkUser = await currentUser()
|
||||||
|
|
||||||
|
if (!clerkUser) {
|
||||||
|
return new Response("Unauthorized", { status: 401 })
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch(`http://localhost:8787/api/user?id=${clerkUser.id}`)
|
||||||
|
const user = (await res.json()) as User
|
||||||
|
|
||||||
|
// Create a session for the current user
|
||||||
|
// userInfo is made available in Liveblocks presence hooks, e.g. useOthers
|
||||||
|
const session = liveblocks.prepareSession(user.id, {
|
||||||
|
userInfo: {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Give the user access to the room
|
||||||
|
user.sandbox.forEach((sandbox) => {
|
||||||
|
session.allow(`${sandbox.id}`, session.FULL_ACCESS)
|
||||||
|
})
|
||||||
|
user.usersToSandboxes.forEach((userToSandbox) => {
|
||||||
|
session.allow(`${userToSandbox.sandboxId}`, session.FULL_ACCESS)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Authorize the user and return the result
|
||||||
|
const { body, status } = await session.authorize()
|
||||||
|
return new Response(body, { status })
|
||||||
|
}
|
@ -1,9 +1,17 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import Editor, { BeforeMount, OnMount } from "@monaco-editor/react"
|
|
||||||
import monaco from "monaco-editor"
|
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
// import theme from "./theme.json"
|
import monaco from "monaco-editor"
|
||||||
|
import Editor, { BeforeMount, OnMount } from "@monaco-editor/react"
|
||||||
|
import { io } from "socket.io-client"
|
||||||
|
import { toast } from "sonner"
|
||||||
|
import { useClerk } from "@clerk/nextjs"
|
||||||
|
|
||||||
|
import * as Y from "yjs"
|
||||||
|
import LiveblocksProvider from "@liveblocks/yjs"
|
||||||
|
import { MonacoBinding } from "y-monaco"
|
||||||
|
import { Awareness } from "y-protocols/awareness"
|
||||||
|
import { useRoom } from "@/liveblocks.config"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ResizableHandle,
|
ResizableHandle,
|
||||||
@ -22,17 +30,12 @@ import {
|
|||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import Tab from "../ui/tab"
|
import Tab from "../ui/tab"
|
||||||
import Sidebar from "./sidebar"
|
import Sidebar from "./sidebar"
|
||||||
import { useClerk } from "@clerk/nextjs"
|
|
||||||
import { TFile, TFileData, TFolder, TTab } from "./sidebar/types"
|
|
||||||
|
|
||||||
import { io } from "socket.io-client"
|
|
||||||
import { processFileType, validateName } from "@/lib/utils"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import EditorTerminal from "./terminal"
|
import EditorTerminal from "./terminal"
|
||||||
import { Button } from "../ui/button"
|
import { Button } from "../ui/button"
|
||||||
import { User } from "@/lib/types"
|
|
||||||
import { Input } from "../ui/input"
|
|
||||||
import GenerateInput from "./generate"
|
import GenerateInput from "./generate"
|
||||||
|
import { TFile, TFileData, TFolder, TTab } from "./sidebar/types"
|
||||||
|
import { User } from "@/lib/types"
|
||||||
|
import { processFileType, validateName } from "@/lib/utils"
|
||||||
|
|
||||||
export default function CodeEditor({
|
export default function CodeEditor({
|
||||||
userData,
|
userData,
|
||||||
@ -62,6 +65,7 @@ export default function CodeEditor({
|
|||||||
const [terminals, setTerminals] = useState<string[]>([])
|
const [terminals, setTerminals] = useState<string[]>([])
|
||||||
|
|
||||||
const clerk = useClerk()
|
const clerk = useClerk()
|
||||||
|
const room = useRoom()
|
||||||
|
|
||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null)
|
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null)
|
||||||
const editorContainerRef = useRef<HTMLDivElement>(null)
|
const editorContainerRef = useRef<HTMLDivElement>(null)
|
||||||
@ -230,7 +234,7 @@ export default function CodeEditor({
|
|||||||
if (e.key === "s" && (e.metaKey || e.ctrlKey)) {
|
if (e.key === "s" && (e.metaKey || e.ctrlKey)) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const activeTab = tabs.find((t) => t.id === activeId)
|
// const activeTab = tabs.find((t) => t.id === activeId)
|
||||||
// console.log("saving:", activeTab?.name, editorRef.current?.getValue())
|
// console.log("saving:", activeTab?.name, editorRef.current?.getValue())
|
||||||
|
|
||||||
setTabs((prev) =>
|
setTabs((prev) =>
|
||||||
@ -258,6 +262,27 @@ export default function CodeEditor({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editorRef.current) return
|
||||||
|
|
||||||
|
const yDoc = new Y.Doc()
|
||||||
|
const yText = yDoc.getText("monaco")
|
||||||
|
const yProvider: any = new LiveblocksProvider(room, yDoc)
|
||||||
|
|
||||||
|
const binding = new MonacoBinding(
|
||||||
|
yText,
|
||||||
|
editorRef.current.getModel() as monaco.editor.ITextModel,
|
||||||
|
new Set([editorRef.current]),
|
||||||
|
yProvider.awareness as Awareness
|
||||||
|
)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
yDoc.destroy()
|
||||||
|
yProvider.destroy()
|
||||||
|
binding.destroy()
|
||||||
|
}
|
||||||
|
}, [editorRef, room])
|
||||||
|
|
||||||
// connection/disconnection effect + resizeobserver
|
// connection/disconnection effect + resizeobserver
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket.connect()
|
socket.connect()
|
||||||
|
22
frontend/components/editor/room.tsx
Normal file
22
frontend/components/editor/room.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { RoomProvider } from "@/liveblocks.config"
|
||||||
|
import { useSearchParams } from "next/navigation"
|
||||||
|
import { ClientSideSuspense } from "@liveblocks/react"
|
||||||
|
|
||||||
|
export function Room({ children }: { children: React.ReactNode }) {
|
||||||
|
// const roomId = useExampleRoomId("liveblocks:examples:nextjs-yjs-monaco");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RoomProvider
|
||||||
|
id={"roomId"}
|
||||||
|
initialPresence={{
|
||||||
|
cursor: null,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ClientSideSuspense fallback={<div>Loading!!!!</div>}>
|
||||||
|
{() => children}
|
||||||
|
</ClientSideSuspense>
|
||||||
|
</RoomProvider>
|
||||||
|
)
|
||||||
|
}
|
161
frontend/liveblocks.config.ts
Normal file
161
frontend/liveblocks.config.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import { createClient } from "@liveblocks/client"
|
||||||
|
import { createRoomContext, createLiveblocksContext } from "@liveblocks/react"
|
||||||
|
import YLiveblocksProvider from "@liveblocks/yjs"
|
||||||
|
|
||||||
|
const client = createClient({
|
||||||
|
// publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY!,
|
||||||
|
authEndpoint: "/api/lb-auth",
|
||||||
|
// throttle: 100,
|
||||||
|
async resolveUsers({ userIds }) {
|
||||||
|
// Used only for Comments and Notifications. Return a list of user information
|
||||||
|
// retrieved from `userIds`. This info is used in comments, mentions etc.
|
||||||
|
|
||||||
|
// const usersData = await __fetchUsersFromDB__(userIds);
|
||||||
|
//
|
||||||
|
// return usersData.map((userData) => ({
|
||||||
|
// name: userData.name,
|
||||||
|
// avatar: userData.avatar.src,
|
||||||
|
// }));
|
||||||
|
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
async resolveMentionSuggestions({ text }) {
|
||||||
|
// Used only for Comments. Return a list of userIds that match `text`.
|
||||||
|
// These userIds are used to create a mention list when typing in the
|
||||||
|
// composer.
|
||||||
|
//
|
||||||
|
// For example when you type "@jo", `text` will be `"jo"`, and
|
||||||
|
// you should to return an array with John and Joanna's userIds:
|
||||||
|
// ["john@example.com", "joanna@example.com"]
|
||||||
|
|
||||||
|
// const users = await getUsers({ search: text });
|
||||||
|
// return users.map((user) => user.id);
|
||||||
|
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
async resolveRoomsInfo({ roomIds }) {
|
||||||
|
// Used only for Comments and Notifications. Return a list of room information
|
||||||
|
// retrieved from `roomIds`.
|
||||||
|
|
||||||
|
// const roomsData = await __fetchRoomsFromDB__(roomIds);
|
||||||
|
//
|
||||||
|
// return roomsData.map((roomData) => ({
|
||||||
|
// name: roomData.name,
|
||||||
|
// url: roomData.url,
|
||||||
|
// }));
|
||||||
|
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Presence represents the properties that exist on every user in the Room
|
||||||
|
// and that will automatically be kept in sync. Accessible through the
|
||||||
|
// `user.presence` property. Must be JSON-serializable.
|
||||||
|
type Presence = {
|
||||||
|
// cursor: { x: number, y: number } | null,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, Storage represents the shared document that persists in the
|
||||||
|
// Room, even after all users leave. Fields under Storage typically are
|
||||||
|
// LiveList, LiveMap, LiveObject instances, for which updates are
|
||||||
|
// automatically persisted and synced to all connected clients.
|
||||||
|
type Storage = {
|
||||||
|
// author: LiveObject<{ firstName: string, lastName: string }>,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, UserMeta represents static/readonly metadata on each user, as
|
||||||
|
// provided by your own custom auth back end (if used). Useful for data that
|
||||||
|
// will not change during a session, like a user's name or avatar.
|
||||||
|
type UserMeta = {
|
||||||
|
// id?: string, // Accessible through `user.id`
|
||||||
|
// info?: Json, // Accessible through `user.info`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, the type of custom events broadcast and listened to in this
|
||||||
|
// room. Use a union for multiple events. Must be JSON-serializable.
|
||||||
|
type RoomEvent = {
|
||||||
|
// type: "NOTIFICATION",
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, when using Comments, ThreadMetadata represents metadata on
|
||||||
|
// each thread. Can only contain booleans, strings, and numbers.
|
||||||
|
export type ThreadMetadata = {
|
||||||
|
// resolved: boolean;
|
||||||
|
// quote: string;
|
||||||
|
// time: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Room-level hooks, use inside `RoomProvider`
|
||||||
|
export const {
|
||||||
|
suspense: {
|
||||||
|
RoomProvider,
|
||||||
|
useRoom,
|
||||||
|
useMyPresence,
|
||||||
|
useUpdateMyPresence,
|
||||||
|
useSelf,
|
||||||
|
useOthers,
|
||||||
|
useOthersMapped,
|
||||||
|
useOthersListener,
|
||||||
|
useOthersConnectionIds,
|
||||||
|
useOther,
|
||||||
|
useBroadcastEvent,
|
||||||
|
useEventListener,
|
||||||
|
useErrorListener,
|
||||||
|
useStorage,
|
||||||
|
useObject,
|
||||||
|
useMap,
|
||||||
|
useList,
|
||||||
|
useBatch,
|
||||||
|
useHistory,
|
||||||
|
useUndo,
|
||||||
|
useRedo,
|
||||||
|
useCanUndo,
|
||||||
|
useCanRedo,
|
||||||
|
useMutation,
|
||||||
|
useStatus,
|
||||||
|
useLostConnectionListener,
|
||||||
|
useThreads,
|
||||||
|
useCreateThread,
|
||||||
|
useEditThreadMetadata,
|
||||||
|
useCreateComment,
|
||||||
|
useEditComment,
|
||||||
|
useDeleteComment,
|
||||||
|
useAddReaction,
|
||||||
|
useRemoveReaction,
|
||||||
|
useThreadSubscription,
|
||||||
|
useMarkThreadAsRead,
|
||||||
|
useRoomNotificationSettings,
|
||||||
|
useUpdateRoomNotificationSettings,
|
||||||
|
|
||||||
|
// These hooks can be exported from either context
|
||||||
|
// useUser,
|
||||||
|
// useRoomInfo
|
||||||
|
},
|
||||||
|
} = createRoomContext<Presence, Storage, UserMeta, RoomEvent, ThreadMetadata>(
|
||||||
|
client
|
||||||
|
)
|
||||||
|
|
||||||
|
// Project-level hooks, use inside `LiveblocksProvider`
|
||||||
|
export const {
|
||||||
|
suspense: {
|
||||||
|
LiveblocksProvider,
|
||||||
|
useMarkInboxNotificationAsRead,
|
||||||
|
useMarkAllInboxNotificationsAsRead,
|
||||||
|
useInboxNotifications,
|
||||||
|
useUnreadInboxNotificationsCount,
|
||||||
|
|
||||||
|
// These hooks can be exported from either context
|
||||||
|
useUser,
|
||||||
|
useRoomInfo,
|
||||||
|
},
|
||||||
|
} = createLiveblocksContext<UserMeta, ThreadMetadata>(client)
|
||||||
|
|
||||||
|
export type TypedLiveblocksProvider = YLiveblocksProvider<
|
||||||
|
Presence,
|
||||||
|
Storage,
|
||||||
|
UserMeta,
|
||||||
|
RoomEvent
|
||||||
|
>
|
191
frontend/package-lock.json
generated
191
frontend/package-lock.json
generated
@ -11,6 +11,10 @@
|
|||||||
"@clerk/nextjs": "^4.29.12",
|
"@clerk/nextjs": "^4.29.12",
|
||||||
"@clerk/themes": "^1.7.12",
|
"@clerk/themes": "^1.7.12",
|
||||||
"@hookform/resolvers": "^3.3.4",
|
"@hookform/resolvers": "^3.3.4",
|
||||||
|
"@liveblocks/client": "^1.12.0",
|
||||||
|
"@liveblocks/node": "^1.12.0",
|
||||||
|
"@liveblocks/react": "^1.12.0",
|
||||||
|
"@liveblocks/yjs": "^1.12.0",
|
||||||
"@monaco-editor/react": "^4.6.0",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-context-menu": "^2.1.5",
|
"@radix-ui/react-context-menu": "^2.1.5",
|
||||||
@ -38,6 +42,9 @@
|
|||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vscode-icons-js": "^11.6.1",
|
"vscode-icons-js": "^11.6.1",
|
||||||
|
"y-monaco": "^0.1.5",
|
||||||
|
"y-protocols": "^1.0.6",
|
||||||
|
"yjs": "^13.6.15",
|
||||||
"zod": "^3.23.3"
|
"zod": "^3.23.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -340,6 +347,57 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@liveblocks/client": {
|
||||||
|
"version": "1.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@liveblocks/client/-/client-1.12.0.tgz",
|
||||||
|
"integrity": "sha512-TL4sPbWBlrGF7UXLNx2RYuZi/Z51jXALMFAdaWkYE0Qz7mMwxTVSyjPVR7ZXVuqdSo0CTQ1rpPyZeqcoUDtEoQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@liveblocks/core": "1.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@liveblocks/core": {
|
||||||
|
"version": "1.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@liveblocks/core/-/core-1.12.0.tgz",
|
||||||
|
"integrity": "sha512-cPuVZwSh+EBnJL8DA999h4QDNSlOMFyxHPPHm9qVBf9Cl+NY7/Bg3cyKFiHOki6g7y8dQj8No2tpnAHWdqqalA=="
|
||||||
|
},
|
||||||
|
"node_modules/@liveblocks/node": {
|
||||||
|
"version": "1.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@liveblocks/node/-/node-1.12.0.tgz",
|
||||||
|
"integrity": "sha512-xq9EusU2ntZ2vkIQT6TrqPWv4N+txMkzn2FgNllJxt+VdJhhvbt1UkAIokywIZSElFC6gt1VUNMRFM+a7nSJUw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@liveblocks/core": "1.12.0",
|
||||||
|
"@stablelib/base64": "^1.0.1",
|
||||||
|
"fast-sha256": "^1.3.0",
|
||||||
|
"node-fetch": "^2.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@liveblocks/react": {
|
||||||
|
"version": "1.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@liveblocks/react/-/react-1.12.0.tgz",
|
||||||
|
"integrity": "sha512-bxI420FRqUqEAHIyC6jTT40JiMDaum6+7Ag6osme2N0YsshOKZx4/z3uCD3LPdOQAzhaHiv9ZmVYwRFp3z9RfA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@liveblocks/client": "1.12.0",
|
||||||
|
"@liveblocks/core": "1.12.0",
|
||||||
|
"nanoid": "^3",
|
||||||
|
"use-sync-external-store": "^1.2.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.14.0 || ^17 || ^18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@liveblocks/yjs": {
|
||||||
|
"version": "1.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@liveblocks/yjs/-/yjs-1.12.0.tgz",
|
||||||
|
"integrity": "sha512-jCGUlfh8hW2Cr5jRrx/c/dBaY3u6psIIVPETDO91AvKgeVld50I7tcYGjFARWwaOYMhSD3c57iAsOjEkGIvpHA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@liveblocks/client": "1.12.0",
|
||||||
|
"@liveblocks/core": "1.12.0",
|
||||||
|
"js-base64": "^3.7.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"yjs": "^13.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@monaco-editor/loader": {
|
"node_modules/@monaco-editor/loader": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz",
|
||||||
@ -1329,6 +1387,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@stablelib/base64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="
|
||||||
|
},
|
||||||
"node_modules/@swc/helpers": {
|
"node_modules/@swc/helpers": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||||
@ -2008,6 +2071,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fast-plist/-/fast-plist-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-plist/-/fast-plist-0.1.3.tgz",
|
||||||
"integrity": "sha512-d9cEfo/WcOezgPLAC/8t8wGb6YOD6JTCPMw2QcG2nAdFmyY+9rTUizCTaGjIZAloWENTEUMAPpkUAIJJJ0i96A=="
|
"integrity": "sha512-d9cEfo/WcOezgPLAC/8t8wGb6YOD6JTCPMw2QcG2nAdFmyY+9rTUizCTaGjIZAloWENTEUMAPpkUAIJJJ0i96A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-sha256": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||||
@ -2228,6 +2296,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/isomorphic.js": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz",
|
||||||
|
"integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jackspeak": {
|
"node_modules/jackspeak": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
|
||||||
@ -2253,6 +2330,11 @@
|
|||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-base64": {
|
||||||
|
"version": "3.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz",
|
||||||
|
"integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="
|
||||||
|
},
|
||||||
"node_modules/js-cookie": {
|
"node_modules/js-cookie": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
|
||||||
@ -2266,6 +2348,26 @@
|
|||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lib0": {
|
||||||
|
"version": "0.2.93",
|
||||||
|
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.93.tgz",
|
||||||
|
"integrity": "sha512-M5IKsiFJYulS+8Eal8f+zAqf5ckm1vffW0fFDxfgxJ+uiVopvDdd3PxJmz0GsVi3YNO7QCFSq0nAsiDmNhLj9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"isomorphic.js": "^0.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js",
|
||||||
|
"0gentesthtml": "bin/gentesthtml.js",
|
||||||
|
"0serve": "bin/0serve.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||||
@ -2522,6 +2624,25 @@
|
|||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch-native": {
|
"node_modules/node-fetch-native": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz",
|
||||||
@ -3376,6 +3497,11 @@
|
|||||||
"to-no-case": "^1.0.0"
|
"to-no-case": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||||
|
},
|
||||||
"node_modules/ts-interface-checker": {
|
"node_modules/ts-interface-checker": {
|
||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||||
@ -3519,6 +3645,20 @@
|
|||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@ -3645,6 +3785,41 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/y-monaco": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/y-monaco/-/y-monaco-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-zyTCMXtewIG+jP9JikJ5NN3/jLCqpkDY3titPIGBrmVeZsJr6qVeFVP3hPfmWOPHGlGZ39KCIzXrC7mFZRNFIw==",
|
||||||
|
"dependencies": {
|
||||||
|
"lib0": "^0.2.43"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0",
|
||||||
|
"npm": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": ">=0.20.0",
|
||||||
|
"yjs": "^13.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/y-protocols": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"lib0": "^0.2.85"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"yjs": "^13.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
|
||||||
@ -3656,6 +3831,22 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/yjs": {
|
||||||
|
"version": "13.6.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.15.tgz",
|
||||||
|
"integrity": "sha512-moFv4uNYhp8BFxIk3AkpoAnnjts7gwdpiG8RtyFiKbMtxKCS0zVZ5wPaaGpwC3V2N/K8TK8MwtSI3+WO9CHWjQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"lib0": "^0.2.86"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.23.3",
|
"version": "3.23.3",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.3.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.3.tgz",
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
"@clerk/nextjs": "^4.29.12",
|
"@clerk/nextjs": "^4.29.12",
|
||||||
"@clerk/themes": "^1.7.12",
|
"@clerk/themes": "^1.7.12",
|
||||||
"@hookform/resolvers": "^3.3.4",
|
"@hookform/resolvers": "^3.3.4",
|
||||||
|
"@liveblocks/client": "^1.12.0",
|
||||||
|
"@liveblocks/node": "^1.12.0",
|
||||||
|
"@liveblocks/react": "^1.12.0",
|
||||||
|
"@liveblocks/yjs": "^1.12.0",
|
||||||
"@monaco-editor/react": "^4.6.0",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-context-menu": "^2.1.5",
|
"@radix-ui/react-context-menu": "^2.1.5",
|
||||||
@ -39,6 +43,9 @@
|
|||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vscode-icons-js": "^11.6.1",
|
"vscode-icons-js": "^11.6.1",
|
||||||
|
"y-monaco": "^0.1.5",
|
||||||
|
"y-protocols": "^1.0.6",
|
||||||
|
"yjs": "^13.6.15",
|
||||||
"zod": "^3.23.3"
|
"zod": "^3.23.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user