toolbox: adds devtoolbox to easily manage debug components
This commit is contained in:
parent
9cbd0a62ca
commit
df20a47253
@ -9,6 +9,8 @@ export const HomeClient: FC<{ body: Promise<string> }> = ({ body }) => {
|
|||||||
return (
|
return (
|
||||||
<TTCMD
|
<TTCMD
|
||||||
body={text}
|
body={text}
|
||||||
|
parserId="home"
|
||||||
|
title="home"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
@apply pb-6 border-b border-b-primary-500 dark:border-b-dark-500 min-w-full;
|
@apply pb-6 mb-6 border-b border-b-primary-500 dark:border-b-dark-500 min-w-full;
|
||||||
}
|
}
|
||||||
.heading h1 {
|
.heading h1 {
|
||||||
@apply text-5xl font-bold;
|
@apply text-5xl font-bold;
|
||||||
@ -50,7 +50,7 @@
|
|||||||
@apply dark:text-primary-500 text-primary-100 py-4 px-6 font-bold text-lg;
|
@apply dark:text-primary-500 text-primary-100 py-4 px-6 font-bold text-lg;
|
||||||
}
|
}
|
||||||
.p {
|
.p {
|
||||||
@apply py-1;
|
@apply pb-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poppable {
|
.poppable {
|
||||||
|
@ -52,6 +52,8 @@ export const HelpClient: FC<{ body: Promise<string>; title: string }> = ({
|
|||||||
<TTCMD
|
<TTCMD
|
||||||
body={cleanBody}
|
body={cleanBody}
|
||||||
escapeTOC={escapeTOC}
|
escapeTOC={escapeTOC}
|
||||||
|
parserId={title}
|
||||||
|
title={title}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{toc && (
|
{toc && (
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
QuestionMarkCircleIcon,
|
QuestionMarkCircleIcon,
|
||||||
} from "@heroicons/react/24/solid";
|
} from "@heroicons/react/24/solid";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { DevToolboxContextProvider } from "@/components/devtools/context";
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
@ -50,6 +51,8 @@ export default function RootLayout({
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
console.log(process.env.NODE_ENV);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className={inter.className + " flex min-h-[100vh]"}>
|
<body className={inter.className + " flex min-h-[100vh]"}>
|
||||||
@ -71,9 +74,13 @@ export default function RootLayout({
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<main className="p-8 w-full overflow-visible">
|
<DevToolboxContextProvider
|
||||||
{children}
|
isDev={process.env.NODE_ENV !== "production"}
|
||||||
</main>
|
>
|
||||||
|
<main className="p-8 w-full overflow-visible">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
</DevToolboxContextProvider>
|
||||||
<div id="root-portal"></div>
|
<div id="root-portal"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
52
components/devtools/Toolbox.tsx
Normal file
52
components/devtools/Toolbox.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { Portal } from "@/lib/portal/components";
|
||||||
|
import { FC, PropsWithChildren, use, useEffect, useState } from "react";
|
||||||
|
import { DevToolboxContext } from "./context";
|
||||||
|
import { WrenchScrewdriverIcon } from "@heroicons/react/24/solid";
|
||||||
|
import { XMarkIcon } from "@heroicons/react/16/solid";
|
||||||
|
|
||||||
|
export const DevToolbox: FC = () => {
|
||||||
|
const { tools, shouldShowDevTools } = use(DevToolboxContext);
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
return shouldShowDevTools
|
||||||
|
? (
|
||||||
|
<Portal>
|
||||||
|
<div className="dev-portal flex flex-col gap-2 fixed bottom-2 right-2 border rounded-lg bg-black/50">
|
||||||
|
{open
|
||||||
|
? (
|
||||||
|
<div className="relative p-2">
|
||||||
|
<button
|
||||||
|
className="p-1 absolute top-2 right-2"
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
>
|
||||||
|
<XMarkIcon className="w-3 h-3">
|
||||||
|
</XMarkIcon>
|
||||||
|
</button>
|
||||||
|
<p className="mb-4 mr-8">Dev Toolbox</p>
|
||||||
|
{Object.values(tools)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<div>
|
||||||
|
<button className="p-4" onClick={() => setOpen(!open)}>
|
||||||
|
<WrenchScrewdriverIcon className="w-4 h-4">
|
||||||
|
</WrenchScrewdriverIcon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Portal>
|
||||||
|
)
|
||||||
|
: <></>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DevTool: FC<PropsWithChildren<{ id: string }>> = (
|
||||||
|
{ children, id },
|
||||||
|
) => {
|
||||||
|
const { addTool, removeTool } = use(DevToolboxContext);
|
||||||
|
useEffect(() => {
|
||||||
|
addTool(id, children);
|
||||||
|
(() => removeTool(id));
|
||||||
|
}, [addTool, children, id, removeTool]);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
};
|
66
components/devtools/context.tsx
Normal file
66
components/devtools/context.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
Dispatch,
|
||||||
|
FC,
|
||||||
|
PropsWithChildren,
|
||||||
|
ReactNode,
|
||||||
|
SetStateAction,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { DevToolbox } from "./Toolbox";
|
||||||
|
|
||||||
|
interface ContextProps {
|
||||||
|
tools: Record<string, ReactNode>;
|
||||||
|
addTool: (key: string, r: ReactNode) => void;
|
||||||
|
removeTool: (key: string) => void;
|
||||||
|
shouldShowDevTools: boolean;
|
||||||
|
setShouldShowDevTools: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DevToolboxContext = createContext<ContextProps>({
|
||||||
|
tools: {},
|
||||||
|
addTool: () => {},
|
||||||
|
removeTool: () => {},
|
||||||
|
shouldShowDevTools: false,
|
||||||
|
setShouldShowDevTools: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DevToolboxContextProvider: FC<
|
||||||
|
PropsWithChildren<{ isDev: boolean }>
|
||||||
|
> = (
|
||||||
|
{ children, isDev },
|
||||||
|
) => {
|
||||||
|
console.log(isDev);
|
||||||
|
const [tools, setTools] = useState<Record<string, ReactNode>>({});
|
||||||
|
const [shouldShowDevTools, setShouldShowDevTools] = useState(isDev);
|
||||||
|
|
||||||
|
const addTool = useCallback((key: string, r: ReactNode) => {
|
||||||
|
setTools((t) => ({ ...t, [key]: r }));
|
||||||
|
}, []);
|
||||||
|
const removeTool = useCallback((key: string) => {
|
||||||
|
setTools((t) => ({ ...t, [key]: undefined }));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (localStorage.getItem("dev")) setShouldShowDevTools(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DevToolboxContext.Provider
|
||||||
|
value={{
|
||||||
|
tools,
|
||||||
|
addTool,
|
||||||
|
removeTool,
|
||||||
|
shouldShowDevTools,
|
||||||
|
setShouldShowDevTools,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<DevToolbox></DevToolbox>
|
||||||
|
</DevToolboxContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -72,7 +72,7 @@ export const MDSkeletonLoader: FC = () => {
|
|||||||
i,
|
i,
|
||||||
) => (
|
) => (
|
||||||
<li
|
<li
|
||||||
key={t}
|
key={t + i}
|
||||||
className={"my-2 leading-8 text-black/20 " + indentation[
|
className={"my-2 leading-8 text-black/20 " + indentation[
|
||||||
i === 0 ? 0 : Math.floor(Math.random() * indentation.length)
|
i === 0 ? 0 : Math.floor(Math.random() * indentation.length)
|
||||||
]}
|
]}
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { createElements } from "@/lib/tcmd";
|
import { createElements } from "@/lib/tcmd";
|
||||||
import React, { FC, Suspense, useEffect, useMemo, useState } from "react";
|
import React, { FC, Suspense, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import { MDSkeletonLoader } from "../loader";
|
import { MDSkeletonLoader } from "../loader";
|
||||||
|
import { DevTool } from "../devtools/Toolbox";
|
||||||
|
|
||||||
export const TTCMD: FC<
|
interface Props {
|
||||||
{ body: string; escapeTOC?: (tokens: Token[]) => boolean }
|
body: string;
|
||||||
> = ({ body, escapeTOC = () => false }) => {
|
escapeTOC?: (tokens: Token[]) => boolean;
|
||||||
|
parserId: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TTCMD: FC<Props> = (
|
||||||
|
{ body, parserId, escapeTOC = () => false, title },
|
||||||
|
) => {
|
||||||
const elements = useMemo(() => createElements(body), [body]);
|
const elements = useMemo(() => createElements(body), [body]);
|
||||||
|
|
||||||
const [toc, start, end] = useMemo(() => {
|
const [toc, start, end] = useMemo(() => {
|
||||||
@ -31,15 +37,19 @@ export const TTCMD: FC<
|
|||||||
setHasEscapedTOC(escapeTOC(toc));
|
setHasEscapedTOC(escapeTOC(toc));
|
||||||
}, [escapeTOC, toc]);
|
}, [escapeTOC, toc]);
|
||||||
|
|
||||||
|
console.log("mdId", parserId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<MDSkeletonLoader />}>
|
<Suspense fallback={<MDSkeletonLoader />}>
|
||||||
<button
|
<DevTool id={parserId}>
|
||||||
className="btn-primary"
|
<button
|
||||||
onClick={() =>
|
className="btn-primary"
|
||||||
navigator.clipboard.writeText(JSON.stringify(elements, null, 2))}
|
onClick={() =>
|
||||||
>
|
navigator.clipboard.writeText(JSON.stringify(elements, null, 2))}
|
||||||
copy ast
|
>
|
||||||
</button>
|
Copy AST for {title}
|
||||||
|
</button>
|
||||||
|
</DevTool>
|
||||||
{hasEscapedTOC !== undefined &&
|
{hasEscapedTOC !== undefined &&
|
||||||
(
|
(
|
||||||
<TTCMDRenderer
|
<TTCMDRenderer
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { ReactNode, useCallback, useRef } from 'react'
|
import { ReactNode, useCallback, useRef } from "react";
|
||||||
|
|
||||||
export const useRefCallback = <T = ReactNode>() : [T | null, (arg: T) => void] => {
|
export const useRefCallback = <T = ReactNode>(): [
|
||||||
|
T | null,
|
||||||
|
(arg: T) => void,
|
||||||
|
] => {
|
||||||
const ref = useRef<T | null>(null);
|
const ref = useRef<T | null>(null);
|
||||||
const setRef = useCallback((val: T) => {
|
const setRef = useCallback((val: T) => {
|
||||||
console.log(val);
|
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
// does something?
|
// does something?
|
||||||
}
|
}
|
||||||
@ -12,8 +14,8 @@ export const useRefCallback = <T = ReactNode>() : [T | null, (arg: T) => void] =
|
|||||||
// also does something?
|
// also does something?
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.current = val
|
ref.current = val;
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
return [ref.current, setRef]
|
return [ref.current, setRef];
|
||||||
}
|
};
|
||||||
|
@ -108,11 +108,11 @@ export const buildOnlyDefaultElements = () => {
|
|||||||
style={{
|
style={{
|
||||||
"--grid-cols": metadata.columns,
|
"--grid-cols": metadata.columns,
|
||||||
} as React.CSSProperties}
|
} as React.CSSProperties}
|
||||||
className="grid grid-cols-dynamic gap-x-8 gap-y-6 mb-6"
|
className="grid grid-cols-dynamic gap-x-8 mb-6"
|
||||||
>
|
>
|
||||||
{children?.map((c) => {
|
{children?.map((c) => {
|
||||||
const Comp = c.metadata.span ? Fragment : "div";
|
const Comp = c.metadata.span ? Fragment : "div";
|
||||||
return <Comp key={c.uuid}>{c.render(c)}</Comp>;
|
return <Comp className="p" key={c.uuid}>{c.render(c)}</Comp>;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
18
md/home.md
18
md/home.md
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Tabletop Commander (TTC) is a rules-and-tools app for tabletop games - board, card, war, role-playing, you name it! It is the spiritual successor of Chapter Master by Emmaline Autumn, a Warhammer 40,000 9th Edition rules reference and game helper.
|
Tabletop Commander (TTC) is a rules-and-tools app for tabletop games - board, card, war, role-playing, you name it! It is the spiritual successor of Chapter Master by Emmaline Autumn, a Warhammer 40,000 9th Edition rules reference and game helper.
|
||||||
|
|
||||||
Emma decided to move on from Chapter Master as her interest in 40k was supplanted by the greater wargaming hobby after the release of 10th edition made clear that Chapter Master was too inflexible and tedious to work on.
|
Emma decided to move on from Chapter Master as her interest in Warhammer 40k waned, replaced by a broader enthusiasm for the wargaming hobby. The release of the 10th edition highlighted Chapter Master's inflexibility and tediousness, prompting her to seek new avenues.
|
||||||
|
|
||||||
See, Emma had a vision that anyone could contribute to making rules corrections so that anyone could have all of the rules as they currently exist. This ballooned into the idea that you could have all the rules as they existed at *any point in time.* As soon as she realized that every code change either needed to keep that backward compatibility in mind or would cause the data to no longer be usable, she decided to drop Chapter Master entirely.
|
See, Emma had a vision that anyone could contribute to making rules corrections so that anyone could have all of the rules as they currently exist. This ballooned into the idea that you could have all the rules as they existed at *any point in time.* As soon as she realized that every code change either needed to keep that backward compatibility in mind or would cause the data to no longer be usable, she decided to drop Chapter Master entirely.
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ It didn't sit right with her. A big project no longer being worked on and a dead
|
|||||||
|
|
||||||
### Game Systems
|
### Game Systems
|
||||||
|
|
||||||
The basis of TTC is called a Game System Package. This package
|
The basis of TTC is called a Game System. This package
|
||||||
includes everything needed for a game system, including schemas,
|
includes everything needed for a game system, including schemas,
|
||||||
publications, and tools. Players can follow a Game System to get
|
publications, and tools. Players can follow a Game System to get
|
||||||
consistently updated content publications, or fork it to
|
consistently updated content publications, or fork it to
|
||||||
@ -81,12 +81,20 @@ Publications use an enhanced markdown syntax (ttcMD) that
|
|||||||
implements ttcQuery, and adds a bit of custom syntax for things
|
implements ttcQuery, and adds a bit of custom syntax for things
|
||||||
like pop-overs and styling hints for rendering.
|
like pop-overs and styling hints for rendering.
|
||||||
|
|
||||||
The styling aspect is similar to a very trimmed down CSS, but
|
Though it uses markdown as its base syntax, ttcMD could more accurately be called a templating language as it contains a lot of custom elements to handle different layouts
|
||||||
can accomplish quite a lot. For example, this page is actually
|
|
||||||
built using ttcMD!
|
|
||||||
|
|
||||||
[~~cta Learn More](/help/Publications.md)
|
[~~cta Learn More](/help/Publications.md)
|
||||||
|
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
[[2
|
||||||
|
|
||||||
|
Want to keep up with TTC? Join us over at the CyborgGrizzly Games Discord server! Come discuss tabletop gaming, stay updated on the latest developments with Tabletop Commander, and collaborate with fellow gamers to create and update game systems. It's the perfect spot to connect with like-minded enthusiasts and be part of shaping the future of tabletop gaming. Don't miss out – hop on the Discord server today!
|
||||||
|
|
||||||
|
Disclaimer: *I'm so sorry, I had ChatGPT write that last paragraph. I tried to save it, but it's just so... corporate*
|
||||||
|
|
||||||
|
[~~cta Join the Discord](https://discord.gg/bePt7MQHQA)
|
||||||
|
|
||||||
|
]]
|
||||||
/[]
|
/[]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user