From faad896f7e211ee189b5315dba1af41864c3ba94 Mon Sep 17 00:00:00 2001 From: Emma Date: Sat, 24 Feb 2024 19:31:48 -0700 Subject: [PATCH] Initial home page, work on tcmd parser --- app/globals.css | 51 ++++---- app/layout.tsx | 31 ++++- app/page.tsx | 244 ++++++++++++++++++++++---------------- bun.lockb | Bin 144817 -> 145214 bytes components/tcmd/index.tsx | 123 +++++++++++++++++++ lib/zip.ts | 17 +++ package.json | 5 +- tailwind.config.ts | 26 ++++ 8 files changed, 367 insertions(+), 130 deletions(-) create mode 100644 components/tcmd/index.tsx create mode 100644 lib/zip.ts diff --git a/app/globals.css b/app/globals.css index 875c01e..a5a2a84 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2,32 +2,37 @@ @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; +@layer base { + * { + @apply text-white + } + body { + @apply bg-mixed-100 + } + input { + @apply py-2 px-4 rounded-full bg-mixed-200 placeholder:text-dark-500 + } + h1,h2,h3,h4,h5,h6 { + @apply font-bold + } + p { + @apply py-1 } } -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} +@layer components { + .strapline { + @apply text-primary-500 uppercase font-bold mb-2 text-lg + } -@layer utilities { - .text-balance { - text-wrap: balance; + .card { + @apply bg-mixed-200 rounded-3xl p-6 shadow-2xl + } + + .btn-primary { + @apply bg-primary-500 py-4 px-6 text-mixed-100 rounded-full font-bold text-lg + } + .btn-secondary { + @apply text-primary-500 py-4 px-6 font-bold text-lg } } diff --git a/app/layout.tsx b/app/layout.tsx index 3314e47..cb9e8bd 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,12 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; +import { + BookOpenIcon, + CircleStackIcon, + Cog8ToothIcon, + PuzzlePieceIcon, +} from "@heroicons/react/24/solid"; const inter = Inter({ subsets: ["latin"] }); @@ -16,7 +22,30 @@ export default function RootLayout({ }>) { return ( - {children} + + +
+ {children} +
+ ); } diff --git a/app/page.tsx b/app/page.tsx index dc191aa..1987037 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,113 +1,149 @@ +import { TCMD } from "@/components/tcmd"; +import { + BookOpenIcon, + CircleStackIcon, + PuzzlePieceIcon, +} from "@heroicons/react/24/solid"; import Image from "next/image"; export default function Home() { return ( -
-
-

- Get started by editing  - app/page.tsx -

-
- - By{" "} - Vercel Logo - + <> +
+

Tabletop Commander

+

How does it work?

+
+
+
+

+ Tabletop Commander (TC) 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. +

+

+ 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 time +

+
+
+
+
+
+ +

Game Systems

+
+

+ The basis of TC is called a Game System Package. This package + includes everything needed for a game system, including schemas, + publications, and tools. Players can follow a Game System to get + consistently updated content publications, or fork it to + maintain it themselves. +

+

But who owns a Game System?

+

+ The neat part is that no one does! You can contribute to any + Game System with updates to publications and schemas through a + community review system. Those with the high enough scores + contribute more towards a total approval score which is used to + determine whether the Game System. The more your contributions + are approved, the higher your score becomes, the more weight + your approval and contributions carry. +

+

+ If your score is high enough, and a contribution request has + enough approvals, you can even be the one to merge it in! +

+
+ +
-
-
- Next.js Logo +
+ +

Schemas

+
+

+ Those who have studied English or databases, you would know that + a schema is a structural pattern. TC aims to provide a simple, + user-edited and maintained schema system for any game. +

+

+ If that flew over your head, don't worry. Others can share + the schemas they've made with everyone, which come as part + of a Game System package that you can fork or follow to get both + content and schemas ready to use. +

+

For the techies:

+

+ The schema system makes use of a powerful custom query language + (tcQuery) I designed. By writing queries directly into the + schema, we can reduce the amount of re-written content, while + maintaining the presence of data anywhere we need it. +

+
+ +
+
+ +
+
+ +

Publications

+
+

+ Publications are the actual content of the rules. They + don't just contain the content, but also the style in which + the content is shown. +

+

+ Content can include text, images, and even video (through + YouTube links or external embeds). Content can link to other + parts of the publication through context based pop-overs. +

+

For the techies (again):

+

+ Publications use an enhanced markdown syntax (tcMD) that + implements tcQuery, and adds a bit of custom syntax for things + like pop-overs and styling hints for rendering. +

+

+ The styling aspect is similar to a very trimmed down CSS, but + can accomplish quite a lot. For example, this page is actually + built using tcMD!* +

+
+ +
+
+ + * not quite yet, this page is currently built with React, but soon it + will be done. If this makes it to production, tell Emma she forgot to + turn the home page into magic + + +
+ -
- -
- -

- Docs{" "} - - -> - -

-

- Find in-depth information about Next.js features and API. -

-
- - -

- Learn{" "} - - -> - -

-

- Learn about Next.js in an interactive course with quizzes! -

-
- - -

- Templates{" "} - - -> - -

-

- Explore starter templates for Next.js. -

-
- - -

- Deploy{" "} - - -> - -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-
-
-
+ + ); } diff --git a/bun.lockb b/bun.lockb index 243a0344f4119b42e10185d5f837837c8e1eaae0..ac17f92e2dff068fb27bbc43b368532a8f60701a 100755 GIT binary patch delta 30606 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 diff --git a/components/tcmd/index.tsx b/components/tcmd/index.tsx new file mode 100644 index 0000000..2680dfe --- /dev/null +++ b/components/tcmd/index.tsx @@ -0,0 +1,123 @@ +import { zipArrays } from "@/lib/zip"; +import { FC, PropsWithChildren, useMemo } from "react"; + +export const TCMD: FC<{ body: string }> = ({ body }) => { + const elements = useMemo(() => createElements(body), [body]); + return ( + <> +
{JSON.stringify(elements,null,2)}
+ + ); +}; + +const createElements = (body: string) => { + const tokens = tokenize(body); + + return tokens; +}; + +type InlineToken = { + type: "text" | "bold"; + content: string; +}; + +type Line = string | InlineToken[]; + +type MultilineToken = { + type: "code"; + lines: Token[]; +}; + +type Token = { + type: "h1" | "h2" | "h3" | "p"; + line: Line; +}; + +const tokenize = (md: string) => { + const tokens: (Token | MultilineToken)[] = []; + md = md.replace(/(?<=[a-z])\n(?=[a-z])/g, " "); + const lines = md.split("\n"); + const multilineFlags = { + heading: 0, + }; + + const tokenMatches = [ + { + rx: /^\s*#\s/, + create: (line: Line) => tokens.push({ type: "h1", line }), + }, + { + rx: /^\s*##\s/, + create: (line: Line) => tokens.push({ type: "h2", line }), + }, + { + rx: /^\s*###\s/, + create: (line: Line) => tokens.push({ type: "h3", line }), + }, + ]; + + for (let line of lines) { + let foundLine = false; + token: + for (const token of tokenMatches) { + if (!token.rx.test(line)) continue token; + foundLine = true; + line = line.replace(token.rx, "").trim(); + + const lineContent = tokenizeInline(line); + token.create(lineContent); + } + + if (foundLine) continue; + + tokens.push({ + type: "p", + line: tokenizeInline(line), + }); + } + + console.log(tokens); + return tokens.filter((t) => (t as Token).line || (t as MultilineToken).lines); +}; + +const tokenizeInline = (line: string) => { + line = line.trim(); + const originalLine = line; + const insertMarker = "{^}"; + const tokens: InlineToken[] = []; + + const tokenMatches = [ + { + rx: /\*\*(.*?)\*\*/g, + create: (content: string) => + tokens.push({ + content, + type: "bold", + }), + }, + ]; + for (const token of tokenMatches) { + let match; + let last = 0; + while ((match = token.rx.exec(line)) !== null) { + const tokenStart = match.index; + const tokenEnd = match.index + match[0].length; + console.log(tokenEnd, token.rx.lastIndex); + token.create(line.substring(tokenStart, tokenEnd)); + line = line.slice(last, tokenStart) + "{^}" + + line.slice(tokenEnd, line.length); + last = tokenEnd; + } + } + + if (tokens.length) { + return zipArrays( + line.split(insertMarker).map((t): InlineToken => ({ + content: t, + type: "text", + })), + tokens, + ).filter((t) => t.content); + } + return originalLine; +}; diff --git a/lib/zip.ts b/lib/zip.ts new file mode 100644 index 0000000..3e09e27 --- /dev/null +++ b/lib/zip.ts @@ -0,0 +1,17 @@ +export function zipArrays(array1: T[], array2: U[]): (T | U)[] { + const zippedArray: (T | U)[] = []; + + const minLength = Math.min(array1.length, array2.length); + for (let i = 0; i < minLength; i++) { + zippedArray.push(array1[i], array2[i]); + } + + // Append remaining elements of the longer array + if (array1.length > array2.length) { + zippedArray.push(...array1.slice(minLength)); + } else if (array2.length > array1.length) { + zippedArray.push(...array2.slice(minLength)); + } + + return zippedArray; +} diff --git a/package.json b/package.json index 408e236..93df53d 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,10 @@ "lint": "next lint" }, "dependencies": { + "@heroicons/react": "^2.1.1", + "next": "14.1.0", "react": "^18", - "react-dom": "^18", - "next": "14.1.0" + "react-dom": "^18" }, "devDependencies": { "typescript": "^5", diff --git a/tailwind.config.ts b/tailwind.config.ts index 7e4bd91..98472cc 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -13,6 +13,32 @@ const config: Config = { "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", }, + colors: { + primary: { + "100": "#36005c", + "200": "#4d1f6d", + "300": "#633a7f", + "400": "#795491", + "500": "#8f6ea2", + "600": "#a58ab5", + }, + dark: { + "100": "#121212", + "200": "#282828", + "300": "#3f3f3f", + "400": "#575757", + "500": "#717171", + "600": "#8b8b8b", + }, + mixed: { + "100": "#1b1220", + "200": "#302735", + "300": "#463e4b", + "400": "#5e5762", + "500": "#77717a", + "600": "#918b93", + }, + }, }, }, plugins: [],