Compare commits
6 Commits
fd5e5bcc8b
...
dev
Author | SHA1 | Date | |
---|---|---|---|
c8f20fbda8 | |||
a2fde9cc79 | |||
84cbea8ce1 | |||
b529445851 | |||
f87a759048 | |||
5b16cc60f7 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,3 +43,5 @@ temp.json
|
||||
temp.md
|
||||
|
||||
.dragonshoard/
|
||||
|
||||
certificates
|
@@ -1,23 +0,0 @@
|
||||
"use server";
|
||||
|
||||
import { auth } from "@/auth";
|
||||
import { prisma } from "@/prisma/prismaClient";
|
||||
import { isEmailVerified } from "@/util/isEmailVerified";
|
||||
|
||||
export const createGameSystem = async (name: string) => {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) return null;
|
||||
|
||||
if (!isEmailVerified(session.user.id)) return null;
|
||||
|
||||
const { id } = await prisma.gameSystem.create({
|
||||
data: {
|
||||
name,
|
||||
authorId: session.user.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
return id;
|
||||
};
|
@@ -1,7 +0,0 @@
|
||||
"use server";
|
||||
import { prisma } from "@/prisma/prismaClient";
|
||||
|
||||
// DEV TOOL ONLY
|
||||
export async function deleteAllGameSystems() {
|
||||
await prisma.gameSystem.deleteMany();
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
"use server";
|
||||
|
||||
import { auth } from "@/auth";
|
||||
import { prisma } from "@/prisma/prismaClient";
|
||||
import { isEmailVerified } from "@/util/isEmailVerified";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export const createSchema = async (form: FormData) => {
|
||||
const name = form.get("name")?.toString();
|
||||
const gsId = form.get("gsId")?.toString();
|
||||
|
||||
const session = await auth();
|
||||
|
||||
if (!name || !gsId || !session?.user?.id || !isEmailVerified(session.user.id))
|
||||
return;
|
||||
|
||||
const { id } = await prisma.schema.create({
|
||||
data: {
|
||||
name,
|
||||
schema: "{}",
|
||||
types: "{}",
|
||||
version: 0,
|
||||
gameSystemId: gsId,
|
||||
authorId: session.user.id,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
redirect(`/game-systems/${gsId}/schema/${id}`);
|
||||
};
|
@@ -1,20 +0,0 @@
|
||||
"use server";
|
||||
|
||||
import { prisma } from "@/prisma/prismaClient";
|
||||
|
||||
export const findSchema = async (id: string) => {
|
||||
const schema = await prisma.schema.findFirst({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
gameSystem: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return schema;
|
||||
};
|
130
actions/Schemas/index.ts
Normal file
130
actions/Schemas/index.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
"use server";
|
||||
import { auth } from "@/auth";
|
||||
import { isEmailVerified } from "@/util/isEmailVerified";
|
||||
import { redirect } from "next/navigation";
|
||||
import { prisma } from "@/prisma/prismaClient";
|
||||
import { Schema } from "@/types";
|
||||
|
||||
export const saveSchemaDb = async (s: Schema, version: number) => {
|
||||
const sesh = await auth();
|
||||
if (!sesh?.user?.id) return;
|
||||
|
||||
const { id, SchemaRevision } = await prisma.schema.upsert({
|
||||
// data: {
|
||||
// ...s,
|
||||
// },
|
||||
create: {
|
||||
name: s.name,
|
||||
SchemaRevision: {
|
||||
create: {
|
||||
fields: s.fields,
|
||||
types: s.types,
|
||||
},
|
||||
},
|
||||
authorId: sesh.user.id,
|
||||
gameSystemId: s.gameSystemId,
|
||||
id: undefined,
|
||||
},
|
||||
update: {
|
||||
name: s.name,
|
||||
},
|
||||
where: {
|
||||
id: s.id,
|
||||
},
|
||||
include: {
|
||||
// id: true,
|
||||
SchemaRevision: {
|
||||
where: {
|
||||
version: s.version,
|
||||
},
|
||||
select: {
|
||||
version: true,
|
||||
isFinal: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// const schema2 = await prisma.schema.findUnique({where:{id}})
|
||||
if (
|
||||
!SchemaRevision.at(0) ||
|
||||
SchemaRevision[0].version < version ||
|
||||
SchemaRevision[0].isFinal
|
||||
) {
|
||||
await prisma.schemaRevision.create({
|
||||
data: {
|
||||
schemaId: id,
|
||||
types: s.types,
|
||||
fields: s.fields,
|
||||
version,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
redirect(`/game-systems/${s.gameSystemId}/schema/${id}`);
|
||||
};
|
||||
|
||||
export const findSchema = async (
|
||||
id: string,
|
||||
version: number,
|
||||
): Promise<Schema | null> => {
|
||||
const schema = await prisma.schema.findFirst({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
// include: {
|
||||
// gameSystem: {
|
||||
// select: {
|
||||
// id: true,
|
||||
// name: true,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
include: {
|
||||
SchemaRevision: {
|
||||
where: {
|
||||
version,
|
||||
},
|
||||
select: {
|
||||
version: true,
|
||||
fields: true,
|
||||
types: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// select: {
|
||||
// id: true,
|
||||
// name: true,
|
||||
// },
|
||||
});
|
||||
|
||||
if (!schema?.SchemaRevision[0]) return null;
|
||||
|
||||
return {
|
||||
fields: schema.SchemaRevision[0].fields,
|
||||
types: schema.SchemaRevision[0].types,
|
||||
id: schema.id,
|
||||
gameSystemId: schema.gameSystemId,
|
||||
name: schema.name,
|
||||
} as Schema;
|
||||
};
|
||||
|
||||
export const createSchema = async (form: FormData) => {
|
||||
const name = form.get("name")?.toString();
|
||||
const gsId = form.get("gsId")?.toString();
|
||||
|
||||
const session = await auth();
|
||||
|
||||
if (!name || !gsId || !session?.user?.id || !isEmailVerified(session.user.id))
|
||||
return;
|
||||
|
||||
const { id } = await prisma.schema.create({
|
||||
data: {
|
||||
name,
|
||||
gameSystemId: gsId,
|
||||
authorId: session.user.id,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
redirect(`/game-systems/${gsId}/schema/${id}`);
|
||||
};
|
@@ -1,5 +1,5 @@
|
||||
"use server";
|
||||
import { signIn, signOut } from "@/auth";
|
||||
import { auth, signIn, signOut } from "@/auth";
|
||||
|
||||
export const signInWithDiscord = async () => {
|
||||
await signIn("discord");
|
||||
@@ -10,3 +10,5 @@ export const signInWithCreds = async (formData: FormData) => {
|
||||
};
|
||||
|
||||
export const signOutOfApp = () => signOut();
|
||||
|
||||
export const getSession = async () => await auth();
|
||||
|
@@ -1,18 +1,21 @@
|
||||
// import { setCurrentGameSystem } from "@/actions/GameSystems/client";
|
||||
import { setCurrentGameSystem } from "@/actions/GameSystems/client";
|
||||
import { auth } from "@/auth";
|
||||
import { prisma } from "@/prisma/prismaClient";
|
||||
import Link from "next/link";
|
||||
|
||||
export default async function GameSystem(
|
||||
{ params: { id } }: { params: { id: string } },
|
||||
) {
|
||||
export default async function GameSystem({
|
||||
params: { id },
|
||||
}: {
|
||||
params: { id: string };
|
||||
}) {
|
||||
if (!id) throw "HOW DID YOU GET HERE?";
|
||||
|
||||
const gameSystem = await prisma.gameSystem.findFirst({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
include: {
|
||||
schemas: {
|
||||
select: {
|
||||
name: true,
|
||||
@@ -26,8 +29,16 @@ export default async function GameSystem(
|
||||
},
|
||||
},
|
||||
},
|
||||
// select: {
|
||||
// id: true,
|
||||
// name: true,
|
||||
// },
|
||||
});
|
||||
|
||||
const session = await auth();
|
||||
|
||||
session?.user?.id && (await setCurrentGameSystem(session.user.id, id));
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="heading">
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { deleteAllGameSystems } from "@/actions/GameSystems/deleteAll";
|
||||
import { deleteAllGameSystems } from "@/actions/GameSystems/devactions";
|
||||
import { DevTool } from "@/components/devtools/DevTool";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { FC, PropsWithChildren } from "react";
|
||||
|
@@ -1,5 +1,5 @@
|
||||
"use client";
|
||||
import { createGameSystem } from "@/actions/GameSystems/create";
|
||||
import { createGameSystem } from "@/actions/GameSystems";
|
||||
import { useToast } from "@/components/toast";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
|
@@ -2,8 +2,12 @@ import { prisma } from "@/prisma/prismaClient";
|
||||
import CreateGameSystem from "./create";
|
||||
import { GameSystemsClient } from "./client";
|
||||
import Link from "next/link";
|
||||
import { setCurrentGameSystem } from "@/actions/GameSystems/client";
|
||||
import { auth } from "@/auth";
|
||||
|
||||
export default async function GameSystems() {
|
||||
const session = await auth();
|
||||
session?.user?.id && (await setCurrentGameSystem(session.user.id));
|
||||
const existingGameSystems = await prisma.gameSystem.findMany({
|
||||
orderBy: {
|
||||
created: "asc",
|
||||
|
@@ -1,13 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Roboto } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import {
|
||||
BookOpenIcon,
|
||||
CircleStackIcon,
|
||||
Cog8ToothIcon,
|
||||
PuzzlePieceIcon,
|
||||
QuestionMarkCircleIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import Link from "next/link";
|
||||
import { DevToolboxContextProvider } from "@/components/devtools/context";
|
||||
import { RecoilRootClient } from "@/components/recoilRoot";
|
||||
@@ -15,6 +8,10 @@ import { JotaiProvider } from "@/components/jotaiProvider";
|
||||
import { Toaster } from "@/components/toast";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
import { User } from "@/components/user/index";
|
||||
import { getCurrentGameSystem } from "@/actions/GameSystems";
|
||||
import { Nav } from "@/components/nav";
|
||||
import { SSE } from "@/components/sse";
|
||||
import { auth } from "@/auth";
|
||||
|
||||
const roboto = Roboto({ subsets: ["latin"], weight: "400" });
|
||||
|
||||
@@ -23,38 +20,12 @@ export const metadata: Metadata = {
|
||||
description: "Rules and tools for tabletop games!",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const navItems = [
|
||||
{
|
||||
to: "/game-systems",
|
||||
icon: PuzzlePieceIcon,
|
||||
text: "Game Systems",
|
||||
},
|
||||
{
|
||||
to: "/schemas",
|
||||
icon: CircleStackIcon,
|
||||
text: "Schemas",
|
||||
},
|
||||
{
|
||||
to: "/publications",
|
||||
icon: BookOpenIcon,
|
||||
text: "Publications",
|
||||
},
|
||||
{
|
||||
to: "/settings",
|
||||
icon: Cog8ToothIcon,
|
||||
text: "Settings",
|
||||
},
|
||||
{
|
||||
to: "/help",
|
||||
icon: QuestionMarkCircleIcon,
|
||||
text: "How do?",
|
||||
},
|
||||
];
|
||||
const currentGame = await getCurrentGameSystem();
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
@@ -65,19 +36,7 @@ export default function RootLayout({
|
||||
<h1 className="text-lg font-bold pb-6 border-b dark:border-dark-500 border-primary-600">
|
||||
<Link href="/">Tabletop Commander</Link>
|
||||
</h1>
|
||||
<ul className="my-6 flex flex-col gap-6">
|
||||
{navItems.map((n) => (
|
||||
<li key={"nav-item" + n.text}>
|
||||
<Link
|
||||
href={n.to}
|
||||
className="flex items-center gap-2 group hover:text-purple-300 transition-colors"
|
||||
>
|
||||
<n.icon className="w-6 h-6 group-hover:fill-purple-300 transition-colors" />
|
||||
{n.text}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Nav game={currentGame ?? undefined} />
|
||||
<div className="mt-auto">
|
||||
<User />
|
||||
</div>
|
||||
@@ -96,6 +55,7 @@ export default function RootLayout({
|
||||
<div id="root-portal"></div>
|
||||
</body>
|
||||
</SessionProvider>
|
||||
<SSE />
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
@@ -20,6 +20,8 @@ export const { handlers, signIn, signOut, auth } = NextAuth(async () => {
|
||||
Discord({
|
||||
clientId,
|
||||
clientSecret,
|
||||
// redirectProxyUrl:
|
||||
// "https://bottomsurgery.local:3000/api/auth/callback/discord",
|
||||
}),
|
||||
Credentials({
|
||||
credentials: {
|
||||
@@ -30,7 +32,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth(async () => {
|
||||
let user = null;
|
||||
|
||||
const pwHash = await saltAndHashPassword(
|
||||
credentials.password as string
|
||||
credentials.password as string,
|
||||
);
|
||||
user = await prisma.user.findFirst({
|
||||
where: {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { FC, PropsWithChildren } from "react";
|
||||
import { Poppable } from "@/lib/poppables/components/poppable";
|
||||
import { Icon } from "@/components/Icon";
|
||||
import { QuestionMarkCircleIcon } from "@heroicons/react/24/solid";
|
||||
|
||||
export const HelpPopper: FC<PropsWithChildren> = ({ children }) => {
|
||||
return (
|
||||
@@ -9,7 +9,7 @@ export const HelpPopper: FC<PropsWithChildren> = ({ children }) => {
|
||||
preferredAlign="centered"
|
||||
preferredEdge="bottom"
|
||||
>
|
||||
<Icon icon="Help" className="svg-white w-4 h-4" />
|
||||
<QuestionMarkCircleIcon className="w-4 h-4 fill-white" />
|
||||
</Poppable>
|
||||
);
|
||||
};
|
||||
|
@@ -2,13 +2,14 @@ import { FC, useCallback, useEffect, useState } from "react";
|
||||
import { useObjectStateWrapper } from "../../hooks/useObjectState";
|
||||
import { ValueField } from "./value-field";
|
||||
import { HelpPopper } from "../Poppables/help";
|
||||
import { Icon } from "../Icon";
|
||||
import { RESERVED_FIELDS } from "../../constants/ReservedFields";
|
||||
import {
|
||||
fieldTypeOptions,
|
||||
FieldTypes,
|
||||
fieldTypesWithValues,
|
||||
} from "./fieldtypes";
|
||||
import { TrashIcon } from "@heroicons/react/24/solid";
|
||||
import { FieldType } from "@/types";
|
||||
|
||||
interface IProps {
|
||||
update: (arg: FieldType) => void;
|
||||
@@ -17,9 +18,12 @@ interface IProps {
|
||||
deleteField: (arg: string) => void;
|
||||
}
|
||||
|
||||
export const FieldEditor: FC<IProps> = (
|
||||
{ update, field, fieldName, deleteField },
|
||||
) => {
|
||||
export const FieldEditor: FC<IProps> = ({
|
||||
update,
|
||||
field,
|
||||
fieldName,
|
||||
deleteField,
|
||||
}) => {
|
||||
const { bindProperty, bindPropertyCheck } = useObjectStateWrapper(
|
||||
field,
|
||||
(e) => update(typeof e === "function" ? e(field) : e),
|
||||
@@ -83,9 +87,8 @@ export const FieldEditor: FC<IProps> = (
|
||||
)}
|
||||
<span className="flex items-center gap-2">
|
||||
<label>
|
||||
<input type="checkbox" {...bindPropertyCheck("isConstant")} />
|
||||
{" "}
|
||||
Is constant
|
||||
<input type="checkbox" {...bindPropertyCheck("isConstant")} /> Is
|
||||
constant
|
||||
</label>
|
||||
<HelpPopper>
|
||||
<p className="text-sm">
|
||||
@@ -108,7 +111,11 @@ export const FieldEditor: FC<IProps> = (
|
||||
</label>
|
||||
<label className="w-min">
|
||||
Limit:
|
||||
<input className="w-12 min-w-min" type="number" {...bindProperty("limit")} />
|
||||
<input
|
||||
className="w-12 min-w-min"
|
||||
type="number"
|
||||
{...bindProperty("limit")}
|
||||
/>
|
||||
</label>
|
||||
<HelpPopper>
|
||||
<p className="text-sm">
|
||||
@@ -122,11 +129,7 @@ export const FieldEditor: FC<IProps> = (
|
||||
className="no-default self-end ml-auto"
|
||||
onClick={() => deleteField(fieldName)}
|
||||
>
|
||||
<Icon
|
||||
className="svg-red-700 hover:svg-red-500 trash-can w-6 h-6"
|
||||
icon="Trash"
|
||||
>
|
||||
</Icon>
|
||||
<TrashIcon className="w-6 h-6 fill-white" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
@@ -1,14 +1,15 @@
|
||||
import { useRecoilValue } from "recoil";
|
||||
import { SchemaEditAtom } from "../../recoil/atoms/schema";
|
||||
import { TEMPLATE_TYPES } from "../../constants/TemplateTypes";
|
||||
import { FC, PropsWithChildren } from "react";
|
||||
import { useAtom } from "jotai";
|
||||
import { InputBinder } from "@/types";
|
||||
|
||||
interface IProps {
|
||||
bind: InputBinder;
|
||||
}
|
||||
|
||||
export const FieldTypeInput: FC<PropsWithChildren<IProps>> = ({ bind }) => {
|
||||
const schema = useRecoilValue(SchemaEditAtom);
|
||||
const [schema] = useAtom(SchemaEditAtom);
|
||||
|
||||
return (
|
||||
<label className="w-min">
|
||||
|
@@ -1,30 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import { FC, useCallback, useState } from "react";
|
||||
import { FC, useCallback, useEffect, useState } from "react";
|
||||
import AnimatedPageContainer from "@/components/AnimatedPageContainer";
|
||||
import { TypeEditor } from "./type-editor";
|
||||
import { useObjectStateWrapper } from "@/hooks/useObjectState";
|
||||
import { useInput } from "../../hooks/useInput";
|
||||
import { useRecoilState, useResetRecoilState } from "recoil";
|
||||
import { SchemaEditAtom } from "@/recoil/atoms/schema";
|
||||
import { SchemaViewer } from "./schema-viewer";
|
||||
import { TemplateEditor } from "./template-editor";
|
||||
import { Icon } from "@/components/Icon";
|
||||
import { useParams } from "next/navigation";
|
||||
import { FieldTypes } from "./fieldtypes";
|
||||
import { prisma } from "@/prisma/prismaClient";
|
||||
import { findSchema, saveSchemaDb } from "@/actions/Schemas/index";
|
||||
import { useToast } from "../toast";
|
||||
import { useAtom } from "jotai";
|
||||
import { Schema, TypeType } from "@/types";
|
||||
import { TrashIcon } from "@heroicons/react/24/solid";
|
||||
import { PencilSquareIcon } from "@heroicons/react/24/solid";
|
||||
|
||||
export const SchemaBuilder: FC = () => {
|
||||
const [schema, setSchema] = useRecoilState(SchemaEditAtom);
|
||||
const resetSchema = useResetRecoilState(SchemaEditAtom);
|
||||
const [schema, setSchema] = useAtom<Schema>(SchemaEditAtom);
|
||||
// const resetSchema = useResetRecoilState(SchemaEditAtom);
|
||||
const { createToast } = useToast();
|
||||
const { update: updateSchema, bindProperty: bindSchemaProperty } =
|
||||
useObjectStateWrapper<Schema>(schema, setSchema);
|
||||
|
||||
const { schemaId, gameSystemId } = useParams<{
|
||||
const { schemaId, id: gameSystemId } = useParams<{
|
||||
schemaId: string;
|
||||
gameSystemId?: string;
|
||||
id: string;
|
||||
}>();
|
||||
|
||||
useEffect(() => {
|
||||
if (schemaId !== "create" && schemaId !== schema.id)
|
||||
findSchema(schemaId, 0).then((sc) => {
|
||||
if (!sc) return;
|
||||
setSchema(sc);
|
||||
});
|
||||
}, [schema.id, schemaId, setSchema]);
|
||||
|
||||
useEffect(() => {
|
||||
if (gameSystemId && !schema.gameSystemId)
|
||||
setSchema((sc) => ({ ...sc, gameSystemId }));
|
||||
}, [gameSystemId, schema.gameSystemId, setSchema]);
|
||||
|
||||
const {
|
||||
value: typeName,
|
||||
bind: bindTypeName,
|
||||
@@ -33,7 +51,7 @@ export const SchemaBuilder: FC = () => {
|
||||
|
||||
const [pageNumber, setPageNumber] = useState(0);
|
||||
|
||||
const [lastSaved, setLastSaved] = useState(schema);
|
||||
const [lastSaved, _setLastSaved] = useState(schema);
|
||||
|
||||
const [selectedType, setSelectedType] = useState("");
|
||||
|
||||
@@ -49,24 +67,13 @@ export const SchemaBuilder: FC = () => {
|
||||
setPageNumber(0);
|
||||
setSelectedType("");
|
||||
},
|
||||
[resetTypeName, updateSchema]
|
||||
[resetTypeName, updateSchema],
|
||||
);
|
||||
|
||||
const saveSchema = useCallback(async () => {
|
||||
// "use server";
|
||||
// setLastSaved(schema);
|
||||
// await prisma.schema.upsert({
|
||||
// where: { id: schema.id },
|
||||
// update: { ...schema },
|
||||
// create: {
|
||||
// name: schema.name,
|
||||
// schema: schema.schema,
|
||||
// types: schema.types,
|
||||
// version: 0,
|
||||
// gameSystemId,
|
||||
// },
|
||||
// });
|
||||
}, [schema, gameSystemId]);
|
||||
createToast({ msg: "Saving Schema", fading: true });
|
||||
await saveSchemaDb(schema, schema.version);
|
||||
}, [createToast, schema]);
|
||||
|
||||
const selectTypeForEdit = useCallback((typeKey: string) => {
|
||||
setSelectedType(typeKey);
|
||||
@@ -80,27 +87,24 @@ export const SchemaBuilder: FC = () => {
|
||||
} = useInput("", { disallowSpaces: true });
|
||||
const addSchemaField = useCallback(() => {
|
||||
updateSchema((s) => ({
|
||||
schema: {
|
||||
...s.schema,
|
||||
[schemaFieldName]: {
|
||||
display: "",
|
||||
type: FieldTypes.any,
|
||||
},
|
||||
fields: {
|
||||
...s.fields,
|
||||
[schemaFieldName]: FieldTypes.any,
|
||||
},
|
||||
}));
|
||||
resetSchemaFieldName();
|
||||
}, [resetSchemaFieldName, schemaFieldName, updateSchema]);
|
||||
|
||||
const updateSchemaField = useCallback(
|
||||
(key: string, template: Template) => {
|
||||
(key: string, fieldType: FieldTypes) => {
|
||||
updateSchema((s) => ({
|
||||
schema: {
|
||||
...s.schema,
|
||||
[key]: template,
|
||||
fields: {
|
||||
...s.fields,
|
||||
[key]: fieldType,
|
||||
},
|
||||
}));
|
||||
},
|
||||
[updateSchema]
|
||||
[updateSchema],
|
||||
);
|
||||
|
||||
const deleteType = useCallback(
|
||||
@@ -111,7 +115,7 @@ export const SchemaBuilder: FC = () => {
|
||||
return { types };
|
||||
});
|
||||
},
|
||||
[updateSchema]
|
||||
[updateSchema],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -133,15 +137,15 @@ export const SchemaBuilder: FC = () => {
|
||||
</button>
|
||||
</div>
|
||||
<ul className="rounded-lg overflow-hidden">
|
||||
{Object.entries(schema.schema).map(
|
||||
{Object.entries(schema.fields).map(
|
||||
([schemaFieldKey, schemaField]) => (
|
||||
<TemplateEditor
|
||||
key={schemaFieldKey}
|
||||
templateKey={schemaFieldKey}
|
||||
template={schemaField}
|
||||
fieldType={schemaField as FieldTypes}
|
||||
update={updateSchemaField}
|
||||
/>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -182,20 +186,14 @@ export const SchemaBuilder: FC = () => {
|
||||
className="no-default"
|
||||
onClick={() => selectTypeForEdit(t)}
|
||||
>
|
||||
<Icon
|
||||
icon="Anvil"
|
||||
className="anvil svg-olive-drab hover:svg-olive-drab-100 w-6 h-6"
|
||||
/>
|
||||
<PencilSquareIcon className="w-6 h-6 fill-white" />
|
||||
</button>
|
||||
<button
|
||||
title="Delete"
|
||||
className="no-default"
|
||||
onClick={() => deleteType(t)}
|
||||
>
|
||||
<Icon
|
||||
icon="Trash"
|
||||
className="trash-can svg-red-700 hover:svg-red-500 w-6 h-6"
|
||||
/>
|
||||
<TrashIcon className="w-6 h-6 fill-white" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
|
@@ -34,10 +34,10 @@ export const SchemaViewer: FC<IProps> = ({ schema, onTypeClick }) => {
|
||||
<hr />
|
||||
<p className="font-bold italic">Templates</p>
|
||||
<ul>
|
||||
{Object.entries(schema.schema).map(([templateKey, template]) => (
|
||||
{Object.entries(schema.fields).map(([templateKey, template]) => (
|
||||
<li key={templateKey}>
|
||||
<p className="font-bold">{templateKey}</p>
|
||||
<p className="font-thin text-xs">{template.type}</p>
|
||||
<p className="text-mixed-600 ml-2">Type: {template}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
@@ -1,39 +1,38 @@
|
||||
import { FC, useCallback } from "react";
|
||||
import { useObjectStateWrapper } from "@/hooks/useObjectState";
|
||||
import { FC, useCallback, useEffect } from "react";
|
||||
import { TEMPLATE_TYPES } from "@/constants/TemplateTypes";
|
||||
import { SchemaEditAtom } from "@/recoil/atoms/schema";
|
||||
import { useRecoilState } from "recoil";
|
||||
import { Icon } from "@/components/Icon";
|
||||
import { FieldTypes } from "./fieldtypes";
|
||||
import { useAtom } from "jotai";
|
||||
import { useInput } from "@/hooks/useInput";
|
||||
import { Schema } from "@/types";
|
||||
import { TrashIcon } from "@heroicons/react/24/solid";
|
||||
|
||||
interface IProps {
|
||||
templateKey: string;
|
||||
update: (arg0: string, arg1: Template) => void;
|
||||
template: Template;
|
||||
update: (arg0: string, arg1: FieldTypes) => void;
|
||||
fieldType: FieldTypes;
|
||||
}
|
||||
|
||||
export const TemplateEditor: FC<IProps> = (
|
||||
{ templateKey, update, template },
|
||||
) => {
|
||||
const [schema, setSchema] = useRecoilState(SchemaEditAtom);
|
||||
const updateTemplate = useCallback(
|
||||
(t: Template | ((arg: Template) => Template)) => {
|
||||
update(templateKey, typeof t === "function" ? t(template) : t);
|
||||
},
|
||||
[templateKey, update, template],
|
||||
);
|
||||
export const TemplateEditor: FC<IProps> = ({
|
||||
templateKey,
|
||||
update,
|
||||
fieldType,
|
||||
}) => {
|
||||
const [schema, setSchema] = useAtom(SchemaEditAtom);
|
||||
const { bind: bindFieldType, value } = useInput(fieldType);
|
||||
|
||||
const { bindProperty } = useObjectStateWrapper(
|
||||
template,
|
||||
updateTemplate,
|
||||
);
|
||||
useEffect(() => {
|
||||
update(templateKey, value);
|
||||
}, []);
|
||||
|
||||
const deleteTemplate = useCallback(() => {
|
||||
const deleteField = useCallback(() => {
|
||||
setSchema((s: Schema) => {
|
||||
const templates = { ...s.schema };
|
||||
delete templates[templateKey];
|
||||
const fields = { ...s.fields };
|
||||
delete fields[templateKey];
|
||||
return {
|
||||
...s,
|
||||
schema: templates,
|
||||
schema: fields,
|
||||
};
|
||||
});
|
||||
}, [setSchema, templateKey]);
|
||||
@@ -46,28 +45,25 @@ export const TemplateEditor: FC<IProps> = (
|
||||
Type:
|
||||
<input
|
||||
type="text"
|
||||
{...bindProperty("type", { disallowSpaces: true })}
|
||||
{...bindFieldType}
|
||||
list="type-editor-type-list"
|
||||
/>
|
||||
<datalist id="type-editor-type-list">
|
||||
{Object.keys(TEMPLATE_TYPES).map((k) => (
|
||||
<option key={"templatetype" + k} value={k}>{k}</option>
|
||||
<option key={"templatetype" + k} value={k}>
|
||||
{k}
|
||||
</option>
|
||||
))}
|
||||
{Object.keys(schema.types).map((k) => (
|
||||
<option key={"schematype" + k} value={k}>{k}</option>
|
||||
<option key={"schematype" + k} value={k}>
|
||||
{k}
|
||||
</option>
|
||||
))}
|
||||
</datalist>
|
||||
</label>
|
||||
<textarea {...bindProperty("display")} cols={30} rows={10}></textarea>
|
||||
</div>
|
||||
<button
|
||||
className="no-default"
|
||||
onClick={deleteTemplate}
|
||||
>
|
||||
<Icon
|
||||
icon="Trash"
|
||||
className="svg-red-700 hover:svg-red-500 trash-can w-6 h-6"
|
||||
/>
|
||||
<button className="no-default" onClick={deleteField}>
|
||||
<TrashIcon className="w-6 h-6 fill-white" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
|
@@ -9,6 +9,7 @@ import { useObjectState } from "../../hooks/useObjectState";
|
||||
import { useInput } from "../../hooks/useInput";
|
||||
import { FieldEditor } from "./field-editor";
|
||||
import { FieldTypes } from "./fieldtypes";
|
||||
import { FieldType, TypeType } from "@/types";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
@@ -18,9 +19,11 @@ interface IProps {
|
||||
|
||||
const constantProperties = ["metadata"];
|
||||
|
||||
export const TypeEditor: FC<PropsWithChildren<IProps>> = (
|
||||
{ saveType, name, type: passedType },
|
||||
) => {
|
||||
export const TypeEditor: FC<PropsWithChildren<IProps>> = ({
|
||||
saveType,
|
||||
name,
|
||||
type: passedType,
|
||||
}) => {
|
||||
const {
|
||||
update: updateType,
|
||||
reset: resetType,
|
||||
@@ -39,19 +42,22 @@ export const TypeEditor: FC<PropsWithChildren<IProps>> = (
|
||||
resetType();
|
||||
};
|
||||
|
||||
const addField = useCallback((e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
updateType({
|
||||
[propertyName]: {
|
||||
type: FieldTypes.number,
|
||||
value: "",
|
||||
isConstant: false,
|
||||
limit: 1,
|
||||
minimum: 1,
|
||||
},
|
||||
});
|
||||
resetPropertyName();
|
||||
}, [propertyName, updateType, resetPropertyName]);
|
||||
const addField = useCallback(
|
||||
(e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
updateType({
|
||||
[propertyName]: {
|
||||
type: FieldTypes.number,
|
||||
value: "",
|
||||
isConstant: false,
|
||||
limit: 1,
|
||||
minimum: 1,
|
||||
},
|
||||
});
|
||||
resetPropertyName();
|
||||
},
|
||||
[propertyName, updateType, resetPropertyName],
|
||||
);
|
||||
|
||||
const updateField = useCallback(
|
||||
(k: keyof typeof type) => (field: FieldType) => {
|
||||
@@ -64,13 +70,16 @@ export const TypeEditor: FC<PropsWithChildren<IProps>> = (
|
||||
passedType && setType(passedType);
|
||||
}, [passedType, setType]);
|
||||
|
||||
const deleteField = useCallback((name: string) => {
|
||||
setType((t) => {
|
||||
const fields = { ...t };
|
||||
delete fields[name];
|
||||
return fields;
|
||||
});
|
||||
}, [setType]);
|
||||
const deleteField = useCallback(
|
||||
(name: string) => {
|
||||
setType((t) => {
|
||||
const fields = { ...t };
|
||||
delete fields[name];
|
||||
return fields;
|
||||
});
|
||||
},
|
||||
[setType],
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -82,17 +91,18 @@ export const TypeEditor: FC<PropsWithChildren<IProps>> = (
|
||||
<button disabled={!propertyName}>Add Field</button>
|
||||
</form>
|
||||
<ul className="rounded-lg overflow-hidden">
|
||||
{Object.entries(type).reverse().filter(([k]) =>
|
||||
!constantProperties.includes(k)
|
||||
).map(([key, value]) => (
|
||||
<FieldEditor
|
||||
key={"field-editor" + key}
|
||||
field={value}
|
||||
update={updateField(key)}
|
||||
fieldName={key}
|
||||
deleteField={deleteField}
|
||||
/>
|
||||
))}
|
||||
{Object.entries(type)
|
||||
.reverse()
|
||||
.filter(([k]) => !constantProperties.includes(k))
|
||||
.map(([key, value]) => (
|
||||
<FieldEditor
|
||||
key={"field-editor" + key}
|
||||
field={value}
|
||||
update={updateField(key)}
|
||||
fieldName={key}
|
||||
deleteField={deleteField}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
<div>
|
||||
<button onClick={save} disabled={!Object.keys(type).length}>
|
||||
|
@@ -17,6 +17,13 @@ export default function SignIn() {
|
||||
type="password"
|
||||
name="password"
|
||||
/>
|
||||
<button
|
||||
role="button"
|
||||
type="submit"
|
||||
className="w-full p-2 rounded-lg bg-primary-500"
|
||||
>
|
||||
Sign In
|
||||
</button>
|
||||
</form>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="dark:border-dark-500 border-primary-600 flex-grow border-b"></div>
|
||||
@@ -24,7 +31,10 @@ export default function SignIn() {
|
||||
<div className="dark:border-dark-500 border-primary-600 flex-grow border-b"></div>
|
||||
</div>
|
||||
<form action={signInWithDiscord}>
|
||||
<button className="w-full p-2 bg-[#816ab1] rounded-lg" type="submit">
|
||||
<button
|
||||
className="w-full p-2 bg-[#816ab1] rounded-lg flex items-center justify-center"
|
||||
type="submit"
|
||||
>
|
||||
<Icon icon="Discord" className="mr-4 inline-block" />
|
||||
Sign in with Discord
|
||||
</button>
|
||||
|
@@ -6,17 +6,21 @@ interface IProps {
|
||||
title?: ReactNode;
|
||||
}
|
||||
|
||||
export const Accordion: FC<PropsWithChildren<IProps>> = (
|
||||
{ children, expandOnHover, expanded, title },
|
||||
) => {
|
||||
export const Accordion: FC<PropsWithChildren<IProps>> = ({
|
||||
children,
|
||||
expandOnHover,
|
||||
expanded,
|
||||
title,
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-expanded={open || expanded}
|
||||
data-expandonhover={expandOnHover}
|
||||
className={(expandOnHover ? "group/hover" : "group/controlled") +
|
||||
" group"}
|
||||
className={
|
||||
(expandOnHover ? "group/hover" : "group/controlled") + " group"
|
||||
}
|
||||
onClick={() => !title && !expandOnHover && setOpen(!open)}
|
||||
>
|
||||
{!!title && (
|
||||
@@ -24,9 +28,7 @@ export const Accordion: FC<PropsWithChildren<IProps>> = (
|
||||
className="flex justify-between cursor-pointer"
|
||||
onClick={() => !expandOnHover && setOpen(!open)}
|
||||
>
|
||||
<div className="accordion-title">
|
||||
{title}
|
||||
</div>
|
||||
<div className="accordion-title">{title}</div>
|
||||
<div
|
||||
className={`
|
||||
group-hover/hover:-rotate-180
|
||||
@@ -41,10 +43,8 @@ export const Accordion: FC<PropsWithChildren<IProps>> = (
|
||||
scale-y-50
|
||||
`}
|
||||
>
|
||||
<span className="block w-2 h-2 rotate-45 border-r-2 border-b-2 place-self-center">
|
||||
</span>
|
||||
<span className="block w-2 h-2 rotate-45 border-r-2 border-b-2 place-self-center">
|
||||
</span>
|
||||
<span className="block w-2 h-2 rotate-45 border-r-2 border-b-2 place-self-center"></span>
|
||||
<span className="block w-2 h-2 rotate-45 border-r-2 border-b-2 place-self-center"></span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -64,15 +64,15 @@ export const AccordionContent: FC<PropsWithChildren> = ({ children }) => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const Child = () => (
|
||||
<div className="absolute bottom-0 w-full" ref={updateRef}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative overflow-hidden">
|
||||
{<Child />}
|
||||
<div
|
||||
key={"accordion-content"}
|
||||
className="absolute bottom-0 w-full"
|
||||
ref={updateRef}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<span
|
||||
style={{ ["--v-height" as never]: height + "px" }}
|
||||
className="w-0 block h-0 group-hover/hover:h-variable group-data-[expanded]/controlled:h-variable transition-all duration-700"
|
||||
|
@@ -1,6 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { FC, PropsWithChildren, ReactNode, useCallback, useState } from "react";
|
||||
import {
|
||||
FC,
|
||||
PropsWithChildren,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { PoppableContent } from "./poppable-content";
|
||||
import { useDebounce } from "../../../hooks/useDebounce";
|
||||
|
||||
@@ -12,38 +19,45 @@ interface IProps {
|
||||
spacing?: number;
|
||||
}
|
||||
|
||||
export const Poppable: FC<PropsWithChildren<IProps>> = (
|
||||
{ className, content, children, preferredEdge, preferredAlign, spacing },
|
||||
) => {
|
||||
export const Poppable: FC<PropsWithChildren<IProps>> = ({
|
||||
className,
|
||||
content,
|
||||
children,
|
||||
preferredEdge,
|
||||
preferredAlign,
|
||||
spacing,
|
||||
}) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const closing = useDebounce(!isHovered, 1000);
|
||||
const closed = useDebounce(closing, 300);
|
||||
|
||||
const [ref, setRef] = useState<HTMLElement>();
|
||||
// const [ref, setRef] = useState<HTMLElement>();
|
||||
|
||||
const updateRef = useCallback((node: HTMLElement) => {
|
||||
if (!node) return;
|
||||
setRef(node);
|
||||
}, []);
|
||||
// const updateRef = useCallback((node: HTMLElement) => {
|
||||
// if (!node) return;
|
||||
// setRef(node);
|
||||
// }, []);
|
||||
|
||||
const ref = useRef(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
ref={updateRef}
|
||||
ref={ref}
|
||||
className={className}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
{!!ref && (
|
||||
{!!ref.current && (
|
||||
<PoppableContent
|
||||
preferredAlign={preferredAlign}
|
||||
preferredEdge={preferredEdge}
|
||||
spacing={spacing}
|
||||
isClosing={closing}
|
||||
isClosed={closed}
|
||||
relativeElement={ref}
|
||||
relativeElement={ref.current}
|
||||
setHover={setIsHovered}
|
||||
>
|
||||
{content}
|
||||
|
@@ -15,9 +15,13 @@ export class DHSecretClient {
|
||||
* @param dhBaseUri uri for hosted Dragon's Hoard instance
|
||||
* @param cacheDir path to cache dir
|
||||
*/
|
||||
constructor(private dhBaseUri: string, private cacheDir: string) {
|
||||
constructor(
|
||||
private dhBaseUri: string,
|
||||
private cacheDir: string,
|
||||
) {
|
||||
this.cacheLocation = this.cacheDir.trim().replace(/\/^/, "") + "/.dh_cache";
|
||||
mkdirSync(this.cacheDir, { recursive: true });
|
||||
|
||||
this.readDiskCache();
|
||||
this.token = this.fetchToken();
|
||||
}
|
||||
@@ -42,12 +46,13 @@ export class DHSecretClient {
|
||||
}
|
||||
|
||||
private readDiskCache() {
|
||||
const cache = readFileSync(this.cacheLocation, "utf-8");
|
||||
|
||||
if (!cache) {
|
||||
try {
|
||||
const cache = readFileSync(this.cacheLocation, "utf-8");
|
||||
this.cache = JSON.parse(cache || "{}");
|
||||
} catch {
|
||||
this.cache = {};
|
||||
this.writeDiskCache();
|
||||
} else this.cache = JSON.parse(cache || "{}");
|
||||
this.writeDiskCache().then(this.readDiskCache);
|
||||
}
|
||||
}
|
||||
private async writeDiskCache() {
|
||||
await writeFile(this.cacheLocation, JSON.stringify(this.cache), "utf-8");
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { PublicationAtom } from "@/recoil/atoms/publication";
|
||||
import { useState, useEffect, useCallback, useRef, ReactNode } from "react";
|
||||
import { useRecoilValue } from "recoil";
|
||||
import { TTCQueryResolver } from "../ttcQuery/TTCResolvers";
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
export function Resolver({ resolver }: { resolver: string }) {
|
||||
const parser = useRecoilValue(PublicationAtom);
|
||||
const [parser] = useAtom(PublicationAtom);
|
||||
const [res] = useState(new TTCQueryResolver(parser));
|
||||
const [content, setContent] = useState<ReactNode>("");
|
||||
useEffect(() => {
|
||||
@@ -15,7 +15,7 @@ export function Resolver({ resolver }: { resolver: string }) {
|
||||
<resolved.display />
|
||||
) : (
|
||||
resolved?.display
|
||||
)
|
||||
),
|
||||
);
|
||||
}, [resolver, res]);
|
||||
return <span>{content}</span>;
|
||||
@@ -31,7 +31,7 @@ export function OnDemandResolver({
|
||||
template: string;
|
||||
title?: string;
|
||||
}) {
|
||||
const parser = useRecoilValue(PublicationAtom);
|
||||
const [parser] = useAtom(PublicationAtom);
|
||||
const res = useRef(new TTCQueryResolver(parser));
|
||||
const [content, setContent] = useState<ReactNode>("");
|
||||
const generateContent = useCallback(() => {
|
||||
|
@@ -5,11 +5,13 @@ import { Poppable } from "../poppables/components/poppable";
|
||||
import { Accordion, AccordionContent } from "../accordion";
|
||||
import { OnDemandResolver, Resolver } from "./Resolver";
|
||||
|
||||
// import "crypto";
|
||||
|
||||
export const TokenRenderers = new Map<string, TokenRenderer<any>>();
|
||||
|
||||
export function buildIdentifierMap(): [
|
||||
TokenIdentifierMap,
|
||||
IdentifierRegistration
|
||||
IdentifierRegistration,
|
||||
] {
|
||||
const TokenIdentifiers = new Map<string, TokenIdentifier<any>>();
|
||||
|
||||
@@ -17,7 +19,7 @@ export function buildIdentifierMap(): [
|
||||
type: string,
|
||||
match: RegExp,
|
||||
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<M>,
|
||||
renderFunction: TokenRenderer<M>
|
||||
renderFunction: TokenRenderer<M>,
|
||||
): void;
|
||||
function registerIdentifier<M>(
|
||||
type: string,
|
||||
@@ -25,7 +27,7 @@ export function buildIdentifierMap(): [
|
||||
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<M>,
|
||||
renderFunction: TokenRenderer<M>,
|
||||
openTagRx: RegExp,
|
||||
closeTagRx: RegExp
|
||||
closeTagRx: RegExp,
|
||||
): void;
|
||||
function registerIdentifier<M = Record<string, string>>(
|
||||
type: string,
|
||||
@@ -33,7 +35,7 @@ export function buildIdentifierMap(): [
|
||||
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<M>,
|
||||
renderFunction: TokenRenderer<M>,
|
||||
openTagRx?: RegExp,
|
||||
closeTagRx?: RegExp
|
||||
closeTagRx?: RegExp,
|
||||
) {
|
||||
TokenIdentifiers.set(type, {
|
||||
rx: match,
|
||||
@@ -54,7 +56,7 @@ export function buildIdentifierMap(): [
|
||||
start,
|
||||
end,
|
||||
new RegExp(openTagRx, "g"),
|
||||
new RegExp(closeTagRx, "g")
|
||||
new RegExp(closeTagRx, "g"),
|
||||
);
|
||||
}
|
||||
: undefined,
|
||||
@@ -125,7 +127,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
);
|
||||
},
|
||||
/(?<![\/\?])(?:\[\])+/g,
|
||||
/\/\[\]/g
|
||||
/\/\[\]/g,
|
||||
);
|
||||
|
||||
// card
|
||||
@@ -170,7 +172,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
);
|
||||
},
|
||||
/\[\[/g,
|
||||
/\]\]/g
|
||||
/\]\]/g,
|
||||
);
|
||||
|
||||
// fenced code block
|
||||
@@ -192,7 +194,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
{token.content}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// list
|
||||
@@ -231,7 +233,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ordered-list
|
||||
@@ -271,7 +273,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
</ol>
|
||||
</>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ordered list-item
|
||||
@@ -300,7 +302,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
))}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// heading
|
||||
@@ -336,7 +338,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
{token.content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// image
|
||||
@@ -373,7 +375,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
}
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
return <img src={metadata.src} alt={token.content} />;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// anchor
|
||||
@@ -417,7 +419,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
{token.content}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// inline-code
|
||||
@@ -440,7 +442,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
{token.content}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// bold
|
||||
@@ -458,7 +460,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
},
|
||||
(token) => {
|
||||
return <span className="font-bold">{token.content}</span>;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// italic
|
||||
@@ -477,7 +479,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
},
|
||||
(token) => {
|
||||
return <span className="italic">{token.content}</span>;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// popover
|
||||
@@ -513,7 +515,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
</span>
|
||||
</Poppable>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// accordion
|
||||
@@ -543,7 +545,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
</Accordion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// paragraph
|
||||
@@ -569,7 +571,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// horizontal rule
|
||||
@@ -587,7 +589,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
},
|
||||
() => {
|
||||
return <div className="w-full border-b border-mixed-500 my-3"></div>;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// comment
|
||||
@@ -605,7 +607,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
},
|
||||
() => {
|
||||
return <></>;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// frontmatter
|
||||
@@ -624,7 +626,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
},
|
||||
(token) => {
|
||||
return <>{token.raw}</>;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// table
|
||||
@@ -655,8 +657,8 @@ export const buildOnlyDefaultElements = () => {
|
||||
r
|
||||
.split("|")
|
||||
.map((c) => c.trim())
|
||||
.filter((c) => !!c)
|
||||
)
|
||||
.filter((c) => !!c),
|
||||
),
|
||||
);
|
||||
|
||||
let headerRows: string[][] = [],
|
||||
@@ -679,7 +681,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
}
|
||||
|
||||
const maxColumns = Math.max(
|
||||
...[...headerRows, ...bodyRows, ...footerRows].map((r) => r.length)
|
||||
...[...headerRows, ...bodyRows, ...footerRows].map((r) => r.length),
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -770,7 +772,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
)}
|
||||
</table>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// resolver
|
||||
@@ -797,7 +799,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
if (t.content.startsWith("Error"))
|
||||
return <span className="red-500">{t.content}</span>;
|
||||
return <Resolver resolver={t.content} />;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// on-demand resolver
|
||||
@@ -839,7 +841,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
title={t.metadata.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return TokenIdentifiers;
|
||||
@@ -848,7 +850,7 @@ export const buildOnlyDefaultElements = () => {
|
||||
function findMatchingClosedParenthesis(
|
||||
str: string,
|
||||
openRegex: RegExp,
|
||||
closedRegex: RegExp
|
||||
closedRegex: RegExp,
|
||||
): number | null {
|
||||
let openings = 0;
|
||||
let closings = 0;
|
||||
@@ -904,7 +906,7 @@ function search(
|
||||
start: number,
|
||||
end: number,
|
||||
openRx: RegExp,
|
||||
closeRx: RegExp
|
||||
closeRx: RegExp,
|
||||
): SearchResult {
|
||||
const oldEnd = end;
|
||||
|
||||
@@ -912,7 +914,7 @@ function search(
|
||||
s,
|
||||
// s.substring(0, end - start),
|
||||
openRx,
|
||||
closeRx
|
||||
closeRx,
|
||||
);
|
||||
|
||||
if (newEnd === null)
|
||||
|
@@ -26,7 +26,6 @@ const tokenize = (body: string) => {
|
||||
const rx = new RegExp(token.rx);
|
||||
let match;
|
||||
while ((match = rx.exec(body)) !== null) {
|
||||
if (type === "p") debugger;
|
||||
const start = match.index;
|
||||
const end = rx.lastIndex;
|
||||
|
||||
@@ -180,9 +179,9 @@ const contentToChildren = (token: Token) => {
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
})
|
||||
}),
|
||||
),
|
||||
token.children || []
|
||||
token.children || [],
|
||||
).filter((c) => c.children?.length || (c.rendersContentOnly && c.content));
|
||||
};
|
||||
|
||||
|
@@ -80,7 +80,6 @@ export class TTCQueryResolver {
|
||||
}
|
||||
|
||||
const [res] = this.parser.search(q, stackItem.value as QueryableObject);
|
||||
debugger;
|
||||
if (Dice.isDice(res)) {
|
||||
const value = new Dice(res);
|
||||
return {
|
||||
|
@@ -1,13 +1,17 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: "standalone",
|
||||
webpack(config) {
|
||||
webpack(config, { isServer }) {
|
||||
config.module.rules.push({
|
||||
test: /\.svg$/i,
|
||||
issuer: /\.[jt]sx?$/,
|
||||
use: ["@svgr/webpack"],
|
||||
});
|
||||
|
||||
if (isServer) {
|
||||
import("./polyfills/customevent.js");
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
123
prisma/migrations/20240904083737_new_defaults/migration.sql
Normal file
123
prisma/migrations/20240904083737_new_defaults/migration.sql
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `authorId` on the `Publication` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `data` on the `Publication` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `originalId` on the `Schema` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `schema` on the `Schema` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `types` on the `Schema` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `version` on the `Schema` table. All the data in the column will be lost.
|
||||
- You are about to drop the `TagsOnPublications` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `TagsOnTags` table. If the table is not empty, all the data it contains will be lost.
|
||||
- Added the required column `schemaVersion` to the `Publication` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `publicationRevisionId` to the `Tag` table without a default value. This is not possible if the table is not empty.
|
||||
- Made the column `publicationId` on table `Tag` required. This step will fail if there are existing NULL values in that column.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Publication" DROP CONSTRAINT "Publication_authorId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Tag" DROP CONSTRAINT "Tag_publicationId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "TagsOnPublications" DROP CONSTRAINT "TagsOnPublications_publicationId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "TagsOnPublications" DROP CONSTRAINT "TagsOnPublications_tagId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "TagsOnTags" DROP CONSTRAINT "TagsOnTags_childTagId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "TagsOnTags" DROP CONSTRAINT "TagsOnTags_parentTagId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "GameSystem" ADD COLUMN "isPublic" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Publication" DROP COLUMN "authorId",
|
||||
DROP COLUMN "data",
|
||||
ADD COLUMN "schemaVersion" INTEGER NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Schema" DROP COLUMN "originalId",
|
||||
DROP COLUMN "schema",
|
||||
DROP COLUMN "types",
|
||||
DROP COLUMN "version";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Tag" ADD COLUMN "publicationRevisionId" TEXT NOT NULL,
|
||||
ALTER COLUMN "publicationId" SET NOT NULL;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "TagsOnPublications";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "TagsOnTags";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SchemaRevision" (
|
||||
"id" TEXT NOT NULL,
|
||||
"schemaId" TEXT NOT NULL,
|
||||
"fields" JSONB NOT NULL DEFAULT '{}',
|
||||
"types" JSONB NOT NULL DEFAULT '{}',
|
||||
"version" INTEGER NOT NULL DEFAULT 0,
|
||||
"isFinal" BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
CONSTRAINT "SchemaRevision_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PublicationRevision" (
|
||||
"id" TEXT NOT NULL,
|
||||
"previousId" TEXT,
|
||||
"publicationId" TEXT NOT NULL,
|
||||
"authorId" TEXT NOT NULL,
|
||||
"version" TEXT NOT NULL,
|
||||
"isFinal" BOOLEAN NOT NULL DEFAULT false,
|
||||
"data" JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
CONSTRAINT "PublicationRevision_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GameSystemFollows" (
|
||||
"gameSystemId" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "GameSystemFollows_pkey" PRIMARY KEY ("userId","gameSystemId")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "SchemaRevision_schemaId_version_key" ON "SchemaRevision"("schemaId", "version");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "PublicationRevision_publicationId_version_key" ON "PublicationRevision"("publicationId", "version");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "SchemaRevision" ADD CONSTRAINT "SchemaRevision_schemaId_fkey" FOREIGN KEY ("schemaId") REFERENCES "Schema"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Publication" ADD CONSTRAINT "Publication_schemaVersion_schemaId_fkey" FOREIGN KEY ("schemaVersion", "schemaId") REFERENCES "SchemaRevision"("version", "schemaId") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PublicationRevision" ADD CONSTRAINT "PublicationRevision_publicationId_fkey" FOREIGN KEY ("publicationId") REFERENCES "Publication"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PublicationRevision" ADD CONSTRAINT "PublicationRevision_previousId_fkey" FOREIGN KEY ("previousId") REFERENCES "PublicationRevision"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PublicationRevision" ADD CONSTRAINT "PublicationRevision_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Tag" ADD CONSTRAINT "Tag_publicationId_fkey" FOREIGN KEY ("publicationId") REFERENCES "Publication"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Tag" ADD CONSTRAINT "Tag_publicationRevisionId_fkey" FOREIGN KEY ("publicationRevisionId") REFERENCES "PublicationRevision"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GameSystemFollows" ADD CONSTRAINT "GameSystemFollows_gameSystemId_fkey" FOREIGN KEY ("gameSystemId") REFERENCES "GameSystem"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GameSystemFollows" ADD CONSTRAINT "GameSystemFollows_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@@ -1,9 +1,3 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
||||
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
@@ -14,13 +8,16 @@ datasource db {
|
||||
}
|
||||
|
||||
model GameSystem {
|
||||
id String @id @default(cuid())
|
||||
schemas Schema[]
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId String
|
||||
id String @id @default(cuid())
|
||||
schemas Schema[]
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId String
|
||||
followers GameSystemFollows[]
|
||||
|
||||
name String @unique
|
||||
created DateTime @default(now())
|
||||
name String @unique
|
||||
created DateTime @default(now())
|
||||
isPublic Boolean @default(false)
|
||||
thingUsers User[] @relation("currentGame")
|
||||
}
|
||||
|
||||
model Schema {
|
||||
@@ -31,59 +28,87 @@ model Schema {
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId String
|
||||
|
||||
originalId String?
|
||||
name String
|
||||
schema Json
|
||||
types Json
|
||||
version Int
|
||||
name String
|
||||
|
||||
SchemaRevision SchemaRevision[]
|
||||
}
|
||||
|
||||
model SchemaRevision {
|
||||
id String @id @default(cuid())
|
||||
schemaId String
|
||||
parentSchema Schema @relation(fields: [schemaId], references: [id])
|
||||
Publication Publication[]
|
||||
|
||||
fields Json @default("{}")
|
||||
types Json @default("{}")
|
||||
version Int @default(0)
|
||||
isFinal Boolean @default(false)
|
||||
|
||||
@@unique([schemaId, version])
|
||||
}
|
||||
|
||||
model Publication {
|
||||
id String @id @default(cuid())
|
||||
schema Schema @relation(fields: [schemaId], references: [id])
|
||||
schemaId String
|
||||
tags Tag[]
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId String
|
||||
id String @id @default(cuid())
|
||||
schema Schema @relation(fields: [schemaId], references: [id])
|
||||
schemaId String
|
||||
schemaVersion Int
|
||||
schemaRevision SchemaRevision @relation(fields: [schemaVersion, schemaId], references: [version, schemaId])
|
||||
PublicationRevision PublicationRevision[]
|
||||
tags Tag[]
|
||||
|
||||
name String
|
||||
data Json
|
||||
TagsOnPublications TagsOnPublications[]
|
||||
name String
|
||||
|
||||
// TagsOnPublications TagsOnPublications[]
|
||||
}
|
||||
|
||||
model TagsOnPublications {
|
||||
publication Publication @relation(fields: [publicationId], references: [id])
|
||||
publicationId String
|
||||
tagId String
|
||||
tag Tag @relation(fields: [tagId], references: [id])
|
||||
model PublicationRevision {
|
||||
id String @id @default(cuid())
|
||||
Tag Tag[]
|
||||
previousId String?
|
||||
publicationId String
|
||||
publication Publication @relation(fields: [publicationId], references: [id])
|
||||
previousRevision PublicationRevision? @relation(name: "downlineRevisions", fields: [previousId], references: [id])
|
||||
downlineRevisions PublicationRevision[] @relation("downlineRevisions")
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId String
|
||||
|
||||
@@id([publicationId, tagId])
|
||||
version String
|
||||
isFinal Boolean @default(false)
|
||||
data Json @default("{}")
|
||||
|
||||
@@unique([publicationId, version])
|
||||
}
|
||||
|
||||
// model TagsOnPublications {
|
||||
// publication Publication @relation(fields: [publicationId], references: [id])
|
||||
// publicationId String
|
||||
// tagId String
|
||||
// tag Tag @relation(fields: [tagId], references: [id])
|
||||
|
||||
// @@id([publicationId, tagId])
|
||||
// }
|
||||
|
||||
model Tag {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
Publication Publication? @relation(fields: [publicationId], references: [id])
|
||||
publicationId String?
|
||||
TagsOnPublications TagsOnPublications[]
|
||||
childTagsOnTags TagsOnTags[] @relation("childTag")
|
||||
parentTagsOnTags TagsOnTags[] @relation("parentTag")
|
||||
}
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
Publication Publication @relation(fields: [publicationId], references: [id])
|
||||
publicationId String
|
||||
PublicationRevision PublicationRevision @relation(fields: [publicationRevisionId], references: [id])
|
||||
publicationRevisionId String
|
||||
|
||||
model TagsOnTags {
|
||||
parentTagId String
|
||||
parentTag Tag @relation(fields: [parentTagId], references: [id], "parentTag")
|
||||
childTagId String
|
||||
childTag Tag @relation(fields: [childTagId], references: [id], "childTag")
|
||||
|
||||
@@id([parentTagId, childTagId])
|
||||
// TagsOnPublications TagsOnPublications[]
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
schemas Schema[]
|
||||
gameSystems GameSystem[]
|
||||
publications Publication[]
|
||||
id String @id @default(cuid())
|
||||
schemas Schema[]
|
||||
gameSystems GameSystem[]
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
PublicationRevision PublicationRevision[]
|
||||
followedSystems GameSystemFollows[]
|
||||
currentGameSystem GameSystem? @relation("currentGame", fields: [currentGameSystemId], references: [id])
|
||||
currentGameSystemId String?
|
||||
|
||||
name String?
|
||||
username String? @unique
|
||||
@@ -91,13 +116,20 @@ model User {
|
||||
emailVerified DateTime?
|
||||
passwordHash String?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model GameSystemFollows {
|
||||
gameSystemId String
|
||||
gameSystem GameSystem @relation(fields: [gameSystemId], references: [id])
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
|
||||
@@id([userId, gameSystemId])
|
||||
}
|
||||
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String @unique
|
||||
|
@@ -1,9 +1,14 @@
|
||||
import { TTCQueryParser } from "@/lib/ttcQuery/TTCQueryParser";
|
||||
import { atom } from "recoil";
|
||||
import { atom } from "jotai";
|
||||
// import { atom } from "recoil";
|
||||
|
||||
export const PublicationAtom = atom({
|
||||
key: "publication",
|
||||
default: new TTCQueryParser({
|
||||
path: { to: { dice: "2d6" } },
|
||||
}),
|
||||
});
|
||||
// export const PublicationAtom = atom({
|
||||
// key: "publication",
|
||||
// default: new TTCQueryParser({
|
||||
// path: { to: { dice: "2d6" } },
|
||||
// }),
|
||||
// });
|
||||
|
||||
export const PublicationAtom = atom(
|
||||
new TTCQueryParser({ path: { to: { dice: "2d6" } } }),
|
||||
);
|
||||
|
@@ -1,6 +1,17 @@
|
||||
import { atom } from "recoil";
|
||||
// import { atom } from "recoil";
|
||||
|
||||
import { Schema } from "@/types";
|
||||
import { atom } from "jotai";
|
||||
|
||||
// export const SchemaEditAtom = atom<Schema>({
|
||||
// key: "schema-edit",
|
||||
// default: { name: "", types: {}, schema: {}, id: "" },
|
||||
// });
|
||||
|
||||
export const SchemaEditAtom = atom<Schema>({
|
||||
key: "schema-edit",
|
||||
default: { name: "", types: {}, schema: {}, id: "" },
|
||||
name: "",
|
||||
id: "",
|
||||
types: {},
|
||||
fields: {},
|
||||
version: 0,
|
||||
});
|
||||
|
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -22,23 +18,11 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
"@/*": ["./*"]
|
||||
},
|
||||
"target": "es2022"
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"files": [
|
||||
"global.d.ts",
|
||||
"./components/mdeditor/TextEditor.tsx"
|
||||
]
|
||||
}
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"],
|
||||
"files": ["global.d.ts", "types.d.ts", "./components/mdeditor/TextEditor.tsx"]
|
||||
}
|
||||
|
18
types.d.ts
vendored
18
types.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import { FieldTypes } from "./components/schema/fieldtypes";
|
||||
|
||||
// MD Parser
|
||||
type IdentifiedToken<M> = {
|
||||
metadata: M;
|
||||
@@ -31,7 +33,7 @@ type FrontMatter = Record<string, string>;
|
||||
type SearchFunction = (
|
||||
s: string,
|
||||
start: number,
|
||||
end: number
|
||||
end: number,
|
||||
) => {
|
||||
start: number;
|
||||
end: number;
|
||||
@@ -53,7 +55,7 @@ type IdentifierRegistration = <N = Record<string, string>>(
|
||||
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<N>,
|
||||
renderFunction: TokenRenderer<N>,
|
||||
openTagRx?: RegExp,
|
||||
closeTagRx?: RegExp
|
||||
closeTagRx?: RegExp,
|
||||
) => void;
|
||||
|
||||
// Schema
|
||||
@@ -76,11 +78,17 @@ type Template = {
|
||||
display: string;
|
||||
};
|
||||
|
||||
type SchemaFields = Record<string, FieldTypes>;
|
||||
|
||||
type SchemaTypes = Record<string, TypeType>;
|
||||
|
||||
type Schema = {
|
||||
id: string;
|
||||
name: string;
|
||||
schema: Record<string, Template>;
|
||||
types: Record<string, TypeType>;
|
||||
fields: SchemaFields;
|
||||
types: SchemaTypes;
|
||||
version: number;
|
||||
gameSystemId?: string | null;
|
||||
};
|
||||
|
||||
// Input Binder
|
||||
@@ -88,7 +96,7 @@ type InputBinder = {
|
||||
name: string;
|
||||
value: string | number;
|
||||
onChange: (
|
||||
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
||||
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
|
||||
) => void;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user