Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add actual, usable examples #11

Merged
merged 7 commits into from
Jun 23, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
let's move these over here instead
fx committed Jun 22, 2024
commit 3eb51d562143663c539615d1a1d0f687f2017d38
45 changes: 6 additions & 39 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -4,57 +4,24 @@ import React, { Suspense, useEffect, useState } from "react";
import "./globals.css";
import { GlobalContext, PHProvider, PostHogPageview } from "./providers";
import { Header } from "@/components/header";
import { encode, decode } from "@/lib/utils";

export const defaultSource = `title 'Shadow Priest WhackAura'
load spec: :shadow_priest
hide_ooc!

dynamic_group 'WhackAuras' do
debuff_missing 'Shadow Word: Pain'
end`;

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const hash = typeof window !== "undefined" ? window.location.hash : "";
const [source, setSource] = useState<string>(defaultSource);

useEffect(() => {
if (!source || source === "" || source === defaultSource) return;
const base64 = encode(source);
if (window?.location) window.location.hash = base64;
history.pushState(null, "", `#${base64}`);
}, [source]);

useEffect(() => {
const handleHashChange = () => {
if (!window?.location?.hash) return;
setSource(decode(window?.location?.hash));
};
window.addEventListener("hashchange", handleHashChange);
if (window?.location?.hash) handleHashChange();
return () => {
window.removeEventListener("hashchange", handleHashChange);
};
}, []);

return (
<html lang="en">
<Suspense>
<PostHogPageview />
</Suspense>
<PHProvider>
<GlobalContext.Provider value={{ source, setSource }}>
<body className={"min-h-screen bg-background font-sans antialiased"}>
<Header />
<section className="container grid flex-1 items-center gap-6">
{children}
</section>
</body>
</GlobalContext.Provider>
<body className={"min-h-screen bg-background font-sans antialiased"}>
<Header />
<section className="container grid flex-1 items-center gap-6">
{children}
</section>
</body>
</PHProvider>
</html>
);
26 changes: 9 additions & 17 deletions app/layout.test.tsx → app/page.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import {
afterAll,
afterEach,
beforeAll,
describe,
expect,
it,
vi,
} from "vitest";
import Layout, { defaultSource } from "./layout";
import { cleanup, render } from "@testing-library/react";
import { useContext } from "react";
import { afterEach, describe, expect, it, vi } from "vitest";
import Page, { defaultSource } from "./page";
import { GlobalContext } from "./providers";

const SourceTestComponent = () => {
@@ -21,27 +13,27 @@ const SourceTestComponent = () => {
);
};

describe("Layout", () => {
describe("Page", () => {
afterEach(cleanup);
afterEach(() => {
vi.clearAllMocks();
});

it("renders", async () => {
const { getByText } = render(
<Layout>
<Page>
<p>hi</p>
</Layout>
</Page>
);
expect(getByText("hi")).toBeDefined();
});

it("should not update hash with default source", async () => {
vi.spyOn(history, "pushState");
const { getByTestId } = render(
<Layout>
<Page>
<SourceTestComponent />
</Layout>
</Page>
);
const source = getByTestId("source");
expect(source.innerHTML).toMatch(defaultSource);
@@ -54,9 +46,9 @@ describe("Layout", () => {
window.location.hash = encoded;

const { getByTestId } = render(
<Layout>
<Page>
<SourceTestComponent />
</Layout>
</Page>
);
const source = getByTestId("source");
expect(source.innerHTML).toMatch(decoded);
59 changes: 45 additions & 14 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -2,23 +2,24 @@

import { useCallback, useEffect, useState } from "react";
import { LuaEngine, LuaFactory } from "wasmoon";
import { DefaultRubyVM } from "@ruby/wasm-wasi/dist/browser";

import indexLua from "../public/lua/index.lua";
import encodeLua from "../public/lua/encode.lua";
import dkjsonLua from "../public/lua/dkjson.lua";
import inspectLua from "../public/lua/inspect.lua";
import libDeflateLua from "../public/lua/LibDeflate.lua";
import libSerializeLua from "../public/lua/LibSerialize.lua";
import dkjsonLua from "../public/lua/dkjson.lua";
import encodeLua from "../public/lua/encode.lua";
import indexLua from "../public/lua/index.lua";
import inspectLua from "../public/lua/inspect.lua";

import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Check, Loader2 } from "lucide-react";
import { RubyVM } from "@ruby/wasm-wasi";
import { Editor } from "@monaco-editor/react";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { WeakAuraEditor } from "@/components/weak-aura-editor";
import { initRuby } from "@/lib/compiler";
import { Editor } from "@monaco-editor/react";
import { RubyVM } from "@ruby/wasm-wasi";
import { Check, Loader2 } from "lucide-react";
import { GlobalContext } from "./providers";
import { decodeHash, encodeHash } from "@/lib/utils";

async function init() {
const factory = new LuaFactory();
@@ -41,14 +42,32 @@ async function init() {
return lua;
}

export default function Page() {
export const defaultSource = `title 'Shadow Priest WhackAura'
load spec: :shadow_priest
hide_ooc!

dynamic_group 'WhackAuras' do
debuff_missing 'Shadow Word: Pain'
end`;

export default function Page({ children }) {
const [lua, setLua] = useState<LuaEngine>();
const [ruby, setRuby] = useState<RubyVM>();
const [json, _setJson] = useState<string>('{"d": "test"}');
const [weakaura, setWeakaura] = useState<string>();
const [source, setSource] = useState<string>(defaultSource);

useEffect(() => {
if (!source || source === "" || source === defaultSource) return;
const base64 = encodeHash(source);
if (window?.location) window.location.hash = base64;
history.pushState(null, "", `#${base64}`);
}, [source]);

useEffect(() => {}, []);

const setJson = useCallback(
(json) => _setJson(JSON.stringify(JSON.parse(json), null, 2)),
(json: string) => _setJson(JSON.stringify(JSON.parse(json), null, 2)),
[]
);

@@ -75,10 +94,20 @@ export default function Page() {
window.ruby = r;
})
.catch((e) => console.error(e));

const handleHashChange = () => {
if (!window?.location?.hash) return;
setSource(decodeHash(window?.location?.hash));
};
window.addEventListener("hashchange", handleHashChange);
if (window?.location?.hash) handleHashChange();
return () => {
window.removeEventListener("hashchange", handleHashChange);
};
}, []);

return (
<>
<GlobalContext.Provider value={{ source, setSource }}>
<div>
<div className="font-bold text-xs py-2 px-4 rounded-full bg-gray-100 dark:bg-gray-800 inline-flex align-middle">
{lua ? (
@@ -134,6 +163,8 @@ export default function Page() {
/>
</div>
</div>
</>

{children}
</GlobalContext.Provider>
);
}
8 changes: 4 additions & 4 deletions lib/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { describe, expect, it } from "vitest";
import { decode, encode } from "./utils";
import { decodeHash, encodeHash } from "./utils";

describe("utils", () => {
describe("encode/decode", () => {
const encoded = "eJxzy89XSMxLUXBKLNJRSMzJUUgsUSjJSFUoTsxNVSjJzE1VBADCFAs-";
const decoded = "Foo and Bar, all at the same time!";

it("should encode", () => expect(encode(decoded)).toEqual(encoded));
it("should decode", () => expect(decode(encoded)).toEqual(decoded));
it("should encode", () => expect(encodeHash(decoded)).toEqual(encoded));
it("should decode", () => expect(decodeHash(encoded)).toEqual(decoded));
it("should not throw, but returned undefined when decoding invalid input", () =>
expect(decode("foo")).toBeUndefined());
expect(decodeHash("foo")).toBeUndefined());
});
});
4 changes: 2 additions & 2 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -7,13 +7,13 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

export const encode = (source: string) => {
export const encodeHash = (source: string) => {
const input = new TextEncoder().encode(source);
const output = pako.deflate(input);
return bytesToBase64URL(output);
};

export const decode = (hash: string) => {
export const decodeHash = (hash: string) => {
try {
const input = base64URLToBytes(hash.replace(/^#/, ""));
const output = pako.inflate(input);
1 change: 1 addition & 0 deletions vitest.config.mts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import { resolve } from "path";

export default defineConfig({
plugins: [react()],
assetsInclude: ["**/*.lua", "**/*.rb", "**/*.wasm"],
resolve: {
alias: [{ find: "@", replacement: resolve(__dirname) }],
},