mirror of
https://github.com/ershisan99/DevToysWeb.git
synced 2026-02-01 05:02:09 +00:00
chore: upgrade to react 19 beta and next 14 canary
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -28,7 +28,7 @@ export default function Page() {
|
||||
yaml: "foo: bar",
|
||||
});
|
||||
|
||||
const setFormByJson = useCallback((text: string) => {
|
||||
const setFormByJson = (text: string) => {
|
||||
setForm(prev => ({
|
||||
...prev,
|
||||
json: text,
|
||||
@@ -36,9 +36,9 @@ export default function Page() {
|
||||
.map(x => yaml.dump(x, { indent: prev.indentation.length, quotingType: '"' }))
|
||||
.unwrapOr(""),
|
||||
}));
|
||||
}, []);
|
||||
};
|
||||
|
||||
const setFormByYaml = useCallback((text: string) => {
|
||||
const setFormByYaml = (text: string) => {
|
||||
setForm(prev => ({
|
||||
...prev,
|
||||
json: safeYamlParse(text)
|
||||
@@ -46,15 +46,15 @@ export default function Page() {
|
||||
.unwrapOr(""),
|
||||
yaml: text,
|
||||
}));
|
||||
}, []);
|
||||
};
|
||||
|
||||
const clearBoth = useCallback(() => {
|
||||
const clearBoth = () => {
|
||||
setForm(prev => ({
|
||||
...prev,
|
||||
json: "",
|
||||
yaml: "",
|
||||
}));
|
||||
}, []);
|
||||
};
|
||||
|
||||
const onIndentationChange: Select.Props["onValueChange"] = value => {
|
||||
const jsonYaml = safeJsonParse(form.json)
|
||||
@@ -73,7 +73,11 @@ export default function Page() {
|
||||
});
|
||||
};
|
||||
|
||||
// @ts-expect-error react 19 beta error
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
const onJsonChange: EditorProps["onChange"] = value => setFormByJson(value ?? "");
|
||||
// @ts-expect-error react 19 beta error
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
const onYamlChange: EditorProps["onChange"] = value => setFormByYaml(value ?? "");
|
||||
|
||||
const indentationConfig = (
|
||||
@@ -133,9 +137,11 @@ export default function Page() {
|
||||
</PageSection>
|
||||
<div className="flex flex-1 flex-col gap-x-4 gap-y-5 lg:flex-row">
|
||||
<PageSection className="min-h-[200px] flex-1" title="Json" control={jsonControl}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<Editor language="json" value={form.json} onChange={onJsonChange} />
|
||||
</PageSection>
|
||||
<PageSection className="min-h-[200px] flex-1" title="Yaml" control={yamlControl}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<Editor language="yaml" value={form.yaml} onChange={onYamlChange} />
|
||||
</PageSection>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -11,6 +12,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
import * as baselib from "@/lib/base";
|
||||
@@ -48,10 +48,10 @@ export default function Page() {
|
||||
}
|
||||
};
|
||||
|
||||
const trySetDec = useCallback((value: string) => trySetInt(10)(value), []);
|
||||
const trySetHex = useCallback((value: string) => trySetInt(16)(value), []);
|
||||
const trySetOct = useCallback((value: string) => trySetInt(8)(value), []);
|
||||
const trySetBin = useCallback((value: string) => trySetInt(2)(value), []);
|
||||
const trySetDec = (value: string) => trySetInt(10)(value);
|
||||
const trySetHex = (value: string) => trySetInt(16)(value);
|
||||
const trySetOct = (value: string) => trySetInt(8)(value);
|
||||
const trySetBin = (value: string) => trySetInt(2)(value);
|
||||
|
||||
const onDecChange: InputProps["onChange"] = e => trySetDec(e.currentTarget.value);
|
||||
const onHexChange: InputProps["onChange"] = e => trySetHex(e.currentTarget.value);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { decode, encode, isValid } from "js-base64";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -16,28 +16,28 @@ export default function Page() {
|
||||
encoded: "8J+YgPCfmILwn6Sj",
|
||||
});
|
||||
|
||||
const setFormByDecoded = useCallback((text: string) => {
|
||||
const setFormByDecoded = (text: string) => {
|
||||
setForm({
|
||||
decoded: text,
|
||||
encoded: encode(text),
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const setFormByEncoded = useCallback((text: string) => {
|
||||
const setFormByEncoded = (text: string) => {
|
||||
const newDecoded = decode(text);
|
||||
|
||||
setForm({
|
||||
decoded: isValid(text) && !newDecoded.includes("<22>") ? newDecoded : "",
|
||||
encoded: text,
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const clearBoth = useCallback(() => {
|
||||
const clearBoth = () => {
|
||||
setForm({
|
||||
decoded: "",
|
||||
encoded: "",
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const onDecodedChange: TextareaProps["onChange"] = e => setFormByDecoded(e.currentTarget.value);
|
||||
const onEncodedChange: TextareaProps["onChange"] = e => setFormByEncoded(e.currentTarget.value);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { escape, unescape } from "html-escaper";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -16,26 +16,26 @@ export default function Page() {
|
||||
encoded: "> It's "HTML escaping".",
|
||||
});
|
||||
|
||||
const setFormByDecoded = useCallback((text: string) => {
|
||||
const setFormByDecoded = (text: string) => {
|
||||
setForm({
|
||||
decoded: text,
|
||||
encoded: escape(text),
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const setFormByEncoded = useCallback((text: string) => {
|
||||
const setFormByEncoded = (text: string) => {
|
||||
setForm({
|
||||
decoded: unescape(text),
|
||||
encoded: text,
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const clearBoth = useCallback(() => {
|
||||
const clearBoth = () => {
|
||||
setForm({
|
||||
decoded: "",
|
||||
encoded: "",
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const onDecodedChange: TextareaProps["onChange"] = e => setFormByDecoded(e.currentTarget.value);
|
||||
const onEncodedChange: TextareaProps["onChange"] = e => setFormByEncoded(e.currentTarget.value);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
import { decode } from "@/lib/jwt";
|
||||
@@ -20,7 +20,7 @@ export default function Page() {
|
||||
const header = h.map(x => JSON.stringify(x, null, 2)).unwrapOr("");
|
||||
const payload = p.map(x => JSON.stringify(x, null, 2)).unwrapOr("");
|
||||
|
||||
const clearJwt = useCallback(() => setJwt(""), []);
|
||||
const clearJwt = () => setJwt("");
|
||||
|
||||
const onJwtChange: TextareaProps["onChange"] = e => setJwt(e.currentTarget.value);
|
||||
|
||||
@@ -49,9 +49,11 @@ export default function Page() {
|
||||
</PageSection>
|
||||
<div className="flex flex-col gap-3">
|
||||
<PageSection title="Header" control={heaederControl}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<Editor height={180} language="json" value={header} options={{ readOnly: true }} />
|
||||
</PageSection>
|
||||
<PageSection title="Payload" control={payloadControl}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<Editor height={180} language="json" value={payload} options={{ readOnly: true }} />
|
||||
</PageSection>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -11,6 +12,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
import { safeDecodeURIComponent, safeEncodeURIComponent } from "@/lib/uri";
|
||||
@@ -16,26 +16,26 @@ export default function Page() {
|
||||
encoded: "%3E%20It's%20%22URL%20encoding%22%3F",
|
||||
});
|
||||
|
||||
const setFormByDecoded = useCallback((text: string) => {
|
||||
const setFormByDecoded = (text: string) => {
|
||||
setForm({
|
||||
decoded: text,
|
||||
encoded: safeEncodeURIComponent(text).unwrapOr(""),
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const setFormByEncoded = useCallback((text: string) => {
|
||||
const setFormByEncoded = (text: string) => {
|
||||
setForm({
|
||||
decoded: safeDecodeURIComponent(text).unwrapOr(""),
|
||||
encoded: text,
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const clearBoth = useCallback(() => {
|
||||
const clearBoth = () => {
|
||||
setForm({
|
||||
decoded: "",
|
||||
encoded: "",
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const onDecodedChange: TextareaProps["onChange"] = e => setFormByDecoded(e.currentTarget.value);
|
||||
const onEncodedChange: TextareaProps["onChange"] = e => setFormByEncoded(e.currentTarget.value);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
import { safeJsonParse } from "@/lib/json";
|
||||
@@ -21,15 +21,26 @@ const indentations = {
|
||||
tab: "\t",
|
||||
};
|
||||
|
||||
const INDENTATION = {
|
||||
TWO_SPACES: "two",
|
||||
FOUR_SPACES: "four",
|
||||
TAB: "tab",
|
||||
ZERO: "zero",
|
||||
} as const;
|
||||
|
||||
type Indentation = (typeof INDENTATION)[keyof typeof INDENTATION];
|
||||
|
||||
export default function Page() {
|
||||
const [indentation, setIndentation] = useState(indentations.two);
|
||||
const [indentation, setIndentation] = useState<Indentation>(INDENTATION.TWO_SPACES);
|
||||
const [input, setInput] = useState('{\n"foo":"bar"\n}');
|
||||
|
||||
const parsed = safeJsonParse(input);
|
||||
const output = parsed.map(x => JSON.stringify(x, null, indentation)).unwrapOr("");
|
||||
const output = parsed.map(x => JSON.stringify(x, null, indentations[indentation])).unwrapOr("");
|
||||
|
||||
const clearInput = useCallback(() => setInput(""), []);
|
||||
const clearInput = () => setInput("");
|
||||
|
||||
// @ts-expect-error react 19 beta error
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
const onJsonChange: EditorProps["onChange"] = value => setInput(value ?? "");
|
||||
|
||||
const indentationConfig = (
|
||||
@@ -37,7 +48,10 @@ export default function Page() {
|
||||
icon={<icons.Space size={24} className="-translate-y-1.5" />}
|
||||
title="Indentation"
|
||||
control={
|
||||
<Select.Root value={indentation} onValueChange={setIndentation}>
|
||||
<Select.Root
|
||||
value={indentation}
|
||||
onValueChange={value => setIndentation(value as Indentation)}
|
||||
>
|
||||
<Select.Trigger
|
||||
className="w-28"
|
||||
aria-label="toggle open/close state of indentation selection"
|
||||
@@ -45,10 +59,10 @@ export default function Page() {
|
||||
<Select.Value placeholder={indentation} />
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value={indentations.two}>2 spaces</Select.Item>
|
||||
<Select.Item value={indentations.four}>4 spaces</Select.Item>
|
||||
<Select.Item value={indentations.tab}>1 tab</Select.Item>
|
||||
<Select.Item value={indentations.zero}>minified</Select.Item>
|
||||
<Select.Item value={INDENTATION.TWO_SPACES}>2 spaces</Select.Item>
|
||||
<Select.Item value={INDENTATION.FOUR_SPACES}>4 spaces</Select.Item>
|
||||
<Select.Item value={INDENTATION.TAB}>1 tab</Select.Item>
|
||||
<Select.Item value={INDENTATION.ZERO}>minified</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
}
|
||||
@@ -75,9 +89,11 @@ export default function Page() {
|
||||
</PageSection>
|
||||
<div className="flex flex-1 flex-col gap-x-4 gap-y-5 lg:flex-row">
|
||||
<PageSection className="min-h-[200px] flex-1" title="Input" control={inputControl}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<Editor language="json" value={input} onChange={onJsonChange} />
|
||||
</PageSection>
|
||||
<PageSection className="min-h-[200px] flex-1" title="Output" control={outputControl}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<Editor language="json" value={output} options={{ readOnly: true }} />
|
||||
</PageSection>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -11,6 +12,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import createHash from "create-hash";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -29,7 +29,7 @@ export default function Page() {
|
||||
const sha256 = uppercase ? newSha256.toUpperCase() : newSha256;
|
||||
const sha512 = uppercase ? newSha512.toUpperCase() : newSha512;
|
||||
|
||||
const clearInput = useCallback(() => setInput(""), []);
|
||||
const clearInput = () => setInput("");
|
||||
|
||||
const onInputChange: TextareaProps["onChange"] = e => setInput(e.currentTarget.value);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -11,6 +12,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { range } from "fp-ts/NonEmptyArray";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -41,21 +41,21 @@ export default function Page() {
|
||||
|
||||
const uuidsString = uuids.join("\n");
|
||||
|
||||
const clearUuids = useCallback(() => setUuids([]), []);
|
||||
const clearUuids = () => setUuids([]);
|
||||
|
||||
const onUuidVersionChange: NonNullable<Select.Props["onValueChange"]> = useCallback(value => {
|
||||
const onUuidVersionChange: NonNullable<Select.Props["onValueChange"]> = value => {
|
||||
if (isUuidVersion(value)) {
|
||||
setUuidVersion(value);
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
|
||||
const onGeneratesChange: NonNullable<InputProps["onChange"]> = useCallback(e => {
|
||||
const onGeneratesChange: NonNullable<InputProps["onChange"]> = e => {
|
||||
const newGenerates = Number(e.currentTarget.value);
|
||||
|
||||
if (newGenerates >= 1 && newGenerates <= 1000) {
|
||||
setGenerates(newGenerates);
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
|
||||
const onGenerateClick = () => {
|
||||
const newUuids = range(1, generates).map(_ => uuid(uuidVersion, hyphens, uppercase));
|
||||
@@ -136,7 +136,7 @@ export default function Page() {
|
||||
</div>
|
||||
</PageSection>
|
||||
<PageSection className="-mt-3" title="UUID(s)" control={uuidsControl}>
|
||||
<Textarea {...{ ref }} value={uuidsString} rows={10} readOnly />
|
||||
<Textarea ref={ref} value={uuidsString} rows={10} readOnly />
|
||||
</PageSection>
|
||||
</PageRootSection>
|
||||
);
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
import "@/styles/globals.css";
|
||||
|
||||
import { Metadata } from "next";
|
||||
import { CookiesProvider } from "next-client-cookies/server";
|
||||
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { fontMono, fontSans } from "@/lib/fonts";
|
||||
import { cn } from "@/lib/style";
|
||||
import { ClientLayout } from "@/components/client-layout";
|
||||
import { Providers } from "@/components/ui/providers";
|
||||
import { Sidebar } from "@/components/sidebar";
|
||||
import { SiteHeader } from "@/components/site-header";
|
||||
import { TailwindIndicator } from "@/components/tailwind-indicator";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { SearchTextProvider } from "@/contexts/search-text";
|
||||
import { SidebarProvider } from "@/contexts/sidebar";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(siteConfig.url),
|
||||
@@ -59,22 +52,11 @@ export default function RootLayout({ children }: RootLayoutProps) {
|
||||
fontMono.variable
|
||||
)}
|
||||
>
|
||||
<CookiesProvider>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" disableTransitionOnChange>
|
||||
<SearchTextProvider>
|
||||
<SidebarProvider>
|
||||
<ClientLayout>
|
||||
<SiteHeader className="col-span-full" />
|
||||
<Sidebar />
|
||||
<main className="overflow-y-auto rounded-tl-md border bg-page p-12">
|
||||
{children}
|
||||
</main>
|
||||
</ClientLayout>
|
||||
<TailwindIndicator />
|
||||
</SidebarProvider>
|
||||
</SearchTextProvider>
|
||||
</ThemeProvider>
|
||||
</CookiesProvider>
|
||||
<Providers attribute="class" defaultTheme="system" disableTransitionOnChange>
|
||||
<SiteHeader className="col-span-full" />
|
||||
<Sidebar />
|
||||
<main className="overflow-y-auto rounded-tl-md border bg-page p-12">{children}</main>
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
// TODO: use query param
|
||||
@@ -10,6 +11,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { singleTools } from "@/config/tools";
|
||||
@@ -11,6 +12,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { Panel, PanelGroup } from "react-resizable-panels";
|
||||
|
||||
import { PERSISTENCE_KEY } from "@/config/persistence-keys";
|
||||
@@ -27,71 +27,56 @@ export default function Page() {
|
||||
const [input2, setInput2] = useState<string | undefined>("Hello, World!");
|
||||
const [diffFullHeight, setDiffFullHeight] = useState(false);
|
||||
const [inlineMode, setInlineMode] = useState(false);
|
||||
const diffPanelMaxSize = useMemo(
|
||||
() => (diffFullHeight ? PANEL_FULL_SIZE : VERTICAL_PANEL_MAX_SIZE),
|
||||
[diffFullHeight]
|
||||
const diffPanelMaxSize = diffFullHeight ? PANEL_FULL_SIZE : VERTICAL_PANEL_MAX_SIZE;
|
||||
|
||||
const clearInput1 = () => setInput1("");
|
||||
const clearInput2 = () => setInput2("");
|
||||
const toggleFullHeight = () => setDiffFullHeight(prev => !prev);
|
||||
|
||||
const inlineModeConfig = (
|
||||
<Configuration
|
||||
icon={<icons.Rows size={24} />}
|
||||
title="Inline mode"
|
||||
control={
|
||||
<LabeledSwitch
|
||||
id="uppercase-switch"
|
||||
label={inlineMode ? "On" : "Off"}
|
||||
checked={inlineMode}
|
||||
onCheckedChange={setInlineMode}
|
||||
aria-label="toggle whether to show diff in inline mode"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
const input1Control = (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.Paste onClipboardRead={setInput1} />,
|
||||
<Button.File onFileRead={setInput1} iconOnly aria-label="load a file with old text" />,
|
||||
<Button.Clear onClick={clearInput1} iconOnly aria-label="clear old text input" />,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const clearInput1 = useCallback(() => setInput1(""), [setInput1]);
|
||||
const clearInput2 = useCallback(() => setInput2(""), [setInput2]);
|
||||
const toggleFullHeight = useCallback(() => setDiffFullHeight(prev => !prev), [setDiffFullHeight]);
|
||||
|
||||
const inlineModeConfig = useMemo(
|
||||
() => (
|
||||
<Configuration
|
||||
icon={<icons.Rows size={24} />}
|
||||
title="Inline mode"
|
||||
control={
|
||||
<LabeledSwitch
|
||||
id="uppercase-switch"
|
||||
label={inlineMode ? "On" : "Off"}
|
||||
checked={inlineMode}
|
||||
onCheckedChange={setInlineMode}
|
||||
aria-label="toggle whether to show diff in inline mode"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
[inlineMode, setInlineMode]
|
||||
);
|
||||
const input1Control = useMemo(
|
||||
() => (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.Paste onClipboardRead={setInput1} />,
|
||||
<Button.File onFileRead={setInput1} iconOnly aria-label="load a file with old text" />,
|
||||
<Button.Clear onClick={clearInput1} iconOnly aria-label="clear old text input" />,
|
||||
]}
|
||||
/>
|
||||
),
|
||||
[setInput1, clearInput1]
|
||||
const input2Control = (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.Paste onClipboardRead={setInput2} />,
|
||||
<Button.File onFileRead={setInput2} iconOnly aria-label="load a file with new text" />,
|
||||
<Button.Clear onClick={clearInput2} iconOnly aria-label="clear new text input" />,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const input2Control = useMemo(
|
||||
() => (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.Paste onClipboardRead={setInput2} />,
|
||||
<Button.File onFileRead={setInput2} iconOnly aria-label="load a file with new text" />,
|
||||
<Button.Clear onClick={clearInput2} iconOnly aria-label="clear new text input" />,
|
||||
]}
|
||||
/>
|
||||
),
|
||||
[setInput2, clearInput2]
|
||||
const diffControl = (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.ToggleFullSize iconOnly onClick={toggleFullHeight} expanded={diffFullHeight} />,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const diffControl = useMemo(
|
||||
() => (
|
||||
<ControlMenu
|
||||
list={[
|
||||
<Button.ToggleFullSize iconOnly onClick={toggleFullHeight} expanded={diffFullHeight} />,
|
||||
]}
|
||||
/>
|
||||
),
|
||||
[diffFullHeight, toggleFullHeight]
|
||||
);
|
||||
|
||||
const hiddenInFullHeightMode = useMemo(() => (diffFullHeight ? "hidden" : ""), [diffFullHeight]);
|
||||
const hiddenInFullHeightMode = diffFullHeight ? "hidden" : "";
|
||||
|
||||
return (
|
||||
<PageRootSection className="h-full" title={toolGroups.text.tools.diff.longTitle}>
|
||||
@@ -106,6 +91,7 @@ export default function Page() {
|
||||
>
|
||||
<Panel maxSize={HORIZONTAL_PANEL_MAX_SIZE}>
|
||||
<PageSection className="h-full" title="Old text" control={input1Control}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<Editor value={input1} onChange={setInput1} />
|
||||
</PageSection>
|
||||
</Panel>
|
||||
@@ -113,6 +99,7 @@ export default function Page() {
|
||||
|
||||
<Panel maxSize={HORIZONTAL_PANEL_MAX_SIZE}>
|
||||
<PageSection className="h-full" title="New text" control={input2Control}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<Editor value={input2} onChange={setInput2} />
|
||||
</PageSection>
|
||||
</Panel>
|
||||
@@ -121,9 +108,14 @@ export default function Page() {
|
||||
<PanelResizeHandle direction="horizontal" className={hiddenInFullHeightMode} />
|
||||
<Panel maxSize={diffPanelMaxSize}>
|
||||
<PageSection className="h-full" title="Difference" control={diffControl}>
|
||||
{/* @ts-expect-error react 19 beta error */}
|
||||
<DiffEditor
|
||||
// @ts-expect-error react 19 beta error
|
||||
original={input1}
|
||||
// @ts-expect-error react 19 beta error
|
||||
modified={input2}
|
||||
// @ts-expect-error react 19 beta error
|
||||
|
||||
options={{
|
||||
readOnly: true,
|
||||
renderSideBySide: !inlineMode,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -12,6 +13,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
import { noOp } from "@/lib/base";
|
||||
@@ -27,17 +27,14 @@ export default function Page() {
|
||||
|
||||
const output = transformText(input, mode);
|
||||
|
||||
const stats = useMemo(
|
||||
() => ({
|
||||
characters: countCharacters(input),
|
||||
words: countWords(input),
|
||||
lines: countLines(input),
|
||||
bytes: countBytes(input),
|
||||
}),
|
||||
[input]
|
||||
);
|
||||
const stats = {
|
||||
characters: countCharacters(input),
|
||||
words: countWords(input),
|
||||
lines: countLines(input),
|
||||
bytes: countBytes(input),
|
||||
};
|
||||
|
||||
const clearInput = useCallback(() => setInput(""), []);
|
||||
const clearInput = () => setInput("");
|
||||
|
||||
const onInputChange: TextareaProps["onChange"] = e => setInput(e.currentTarget.value);
|
||||
const onModeChange = (value: string) => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { toolGroups } from "@/config/tools";
|
||||
@@ -11,6 +12,6 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user