mirror of
https://github.com/MPCStats/zk-stats-demo.git
synced 2026-01-06 20:33:56 -05:00
feat: support verify and use next
This commit is contained in:
@@ -1,4 +1,29 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
// Ref: https://github.com/zkonduit/ezkljs-engine/tree/main?tab=readme-ov-file#cross-origin-isolation
|
||||
const nextConfig = {
|
||||
// webpack: (config) => {
|
||||
// config.resolve.alias['@ezkljs/verify'] = '@ezkljs/verify/dist/esm/index.js';
|
||||
// return config;
|
||||
// },
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/(.*)',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cross-Origin-Embedder-Policy',
|
||||
value: 'require-corp',
|
||||
},
|
||||
{
|
||||
key: 'Cross-Origin-Opener-Policy',
|
||||
value: 'same-origin',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
58
package-lock.json
generated
58
package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "demo-next",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@ezkljs/engine": "5.0.8",
|
||||
"next": "14.0.4",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
@@ -113,6 +114,15 @@
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ezkljs/engine": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@ezkljs/engine/-/engine-5.0.8.tgz",
|
||||
"integrity": "sha512-HY7Qv0O/ZmMIqPIMnUZ+xKDFEdt4fWOPuUW/NWtDM5Q2oTe6FIU+KzHnZE/bfdnvGs09GDUkbExW2MyJ7lRcoA==",
|
||||
"dependencies": {
|
||||
"@types/json-bigint": "^1.0.1",
|
||||
"json-bigint": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
|
||||
@@ -392,6 +402,11 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/json-bigint": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.4.tgz",
|
||||
"integrity": "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag=="
|
||||
},
|
||||
"node_modules/@types/json5": {
|
||||
"version": "0.0.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||
@@ -866,6 +881,14 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/bignumber.js": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
|
||||
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
@@ -2660,6 +2683,14 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||
"dependencies": {
|
||||
"bignumber.js": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
@@ -4442,6 +4473,15 @@
|
||||
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
|
||||
"dev": true
|
||||
},
|
||||
"@ezkljs/engine": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@ezkljs/engine/-/engine-5.0.8.tgz",
|
||||
"integrity": "sha512-HY7Qv0O/ZmMIqPIMnUZ+xKDFEdt4fWOPuUW/NWtDM5Q2oTe6FIU+KzHnZE/bfdnvGs09GDUkbExW2MyJ7lRcoA==",
|
||||
"requires": {
|
||||
"@types/json-bigint": "^1.0.1",
|
||||
"json-bigint": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
|
||||
@@ -4612,6 +4652,11 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"@types/json-bigint": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.4.tgz",
|
||||
"integrity": "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag=="
|
||||
},
|
||||
"@types/json5": {
|
||||
"version": "0.0.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||
@@ -4946,6 +4991,11 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"bignumber.js": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
|
||||
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
@@ -6254,6 +6304,14 @@
|
||||
"argparse": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||
"requires": {
|
||||
"bignumber.js": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
|
||||
11
package.json
11
package.json
@@ -9,19 +9,20 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ezkljs/engine": "5.0.8",
|
||||
"next": "14.0.4",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"next": "14.0.4"
|
||||
"react-dom": "^18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"autoprefixer": "^10.0.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.0.4",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.3.0",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.0.4"
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/assets/kzg.srs
Normal file
BIN
public/assets/kzg.srs
Normal file
Binary file not shown.
BIN
public/assets/model.onnx
Normal file
BIN
public/assets/model.onnx
Normal file
Binary file not shown.
1
public/assets/model.pf
Normal file
1
public/assets/model.pf
Normal file
File diff suppressed because one or more lines are too long
BIN
public/assets/model.vk
Normal file
BIN
public/assets/model.vk
Normal file
Binary file not shown.
1
public/assets/settings.json
Normal file
1
public/assets/settings.json
Normal file
@@ -0,0 +1 @@
|
||||
{"run_args":{"tolerance":{"val":0.0,"scale":1.0},"input_scale":5,"param_scale":5,"scale_rebase_multiplier":10,"lookup_range":[0,9594],"logrows":14,"num_inner_cols":1,"variables":[["batch_size",1]],"input_visibility":{"Hashed":{"hash_is_public":true,"outlets":[]}},"output_visibility":"Public","param_visibility":"Private"},"num_rows":14432,"total_assignments":606,"total_const_size":0,"model_instance_shapes":[[1],[1]],"model_output_scales":[0,5],"model_input_scales":[5],"module_sizes":{"kzg":[],"poseidon":[14432,[1]],"elgamal":[0,[0]]},"required_lookups":["Abs",{"GreaterThan":{"a":0.0}}],"check_mode":"UNSAFE","version":"5.0.8","num_blinding_factors":null}
|
||||
37
src/app/ezkl.ts
Normal file
37
src/app/ezkl.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import init, { init_panic_hook, init_logger, verify } from '@ezkljs/engine/web'
|
||||
|
||||
async function loadFileToBuffer(path: string) {
|
||||
return await fetch(path).then((res) => res.arrayBuffer());
|
||||
}
|
||||
|
||||
export async function initialize() {
|
||||
// Initialize the WASM module. Here we are overiding the default memory allocation with the recommend allocation for the best performance across all mobile browsers.
|
||||
await init(
|
||||
undefined,
|
||||
new WebAssembly.Memory({ initial: 20, maximum: 4096, shared: true }),
|
||||
)
|
||||
// Initialize the panic hook and logger
|
||||
init_panic_hook()
|
||||
init_logger()
|
||||
}
|
||||
|
||||
export async function verifyProof(
|
||||
proofPath: string,
|
||||
settingsPath: string,
|
||||
verificationKeyPath: string,
|
||||
srsPath: string,
|
||||
) {
|
||||
const [proof, settings, verificationKey, srs] = await Promise.all([
|
||||
loadFileToBuffer(proofPath),
|
||||
loadFileToBuffer(settingsPath),
|
||||
loadFileToBuffer(verificationKeyPath),
|
||||
loadFileToBuffer(srsPath),
|
||||
]);
|
||||
const output = verify(
|
||||
new Uint8ClampedArray(proof),
|
||||
new Uint8ClampedArray(verificationKey),
|
||||
new Uint8ClampedArray(settings),
|
||||
new Uint8ClampedArray(srs),
|
||||
);
|
||||
return output
|
||||
}
|
||||
261
src/app/page.tsx
261
src/app/page.tsx
@@ -1,113 +1,162 @@
|
||||
import Image from 'next/image'
|
||||
'use client';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { initialize, verifyProof } from "./ezkl";
|
||||
|
||||
const host = "http://localhost:3000";
|
||||
const assetsURL = `${host}/assets`;
|
||||
const proofPath = `${assetsURL}/model.pf`;
|
||||
const settingsPath = `${assetsURL}/settings.json`;
|
||||
const verificationKeyPath = `${assetsURL}/model.vk`;
|
||||
const srsPath = `${assetsURL}/kzg.srs`;
|
||||
|
||||
const Page = () => {
|
||||
useEffect(() => {
|
||||
// Call verify function here
|
||||
const v = async () => {
|
||||
try {
|
||||
|
||||
await initialize();
|
||||
console.log("!@# loading files")
|
||||
console.log("!@# proofPath=", proofPath)
|
||||
console.log("!@# settingsPath=", settingsPath)
|
||||
console.log("!@# verificationKeyPath=", verificationKeyPath)
|
||||
console.log("!@# srsPath=", srsPath)
|
||||
const result = await verifyProof(
|
||||
proofPath,
|
||||
settingsPath,
|
||||
verificationKeyPath,
|
||||
srsPath,
|
||||
);
|
||||
console.log("Verification result:", result);
|
||||
} catch (error) {
|
||||
console.error("Verification failed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
v();
|
||||
}, []);
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||
<div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
|
||||
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
||||
Get started by editing
|
||||
<code className="font-mono font-bold">src/app/page.tsx</code>
|
||||
</p>
|
||||
<div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
|
||||
<a
|
||||
className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
By{' '}
|
||||
<Image
|
||||
src="/vercel.svg"
|
||||
alt="Vercel Logo"
|
||||
className="dark:invert"
|
||||
width={100}
|
||||
height={24}
|
||||
priority
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Verification Page</h1>
|
||||
<p>This page will call the verify function when it loads.</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
<div className="relative flex place-items-center before:absolute before:h-[300px] before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
|
||||
<Image
|
||||
className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js Logo"
|
||||
width={180}
|
||||
height={37}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
export default Page;
|
||||
|
||||
<div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Docs{' '}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Find in-depth information about Next.js features and API.
|
||||
</p>
|
||||
</a>
|
||||
// export default function Home() {
|
||||
// return (
|
||||
// <main className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||
// <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
|
||||
// <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
||||
// Get started by editing
|
||||
// <code className="font-mono font-bold">src/app/page.tsx</code>
|
||||
// </p>
|
||||
// <div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
|
||||
// <a
|
||||
// className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
|
||||
// href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// By{' '}
|
||||
// <Image
|
||||
// src="/vercel.svg"
|
||||
// alt="Vercel Logo"
|
||||
// className="dark:invert"
|
||||
// width={100}
|
||||
// height={24}
|
||||
// priority
|
||||
// />
|
||||
// </a>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Learn{' '}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Learn about Next.js in an interactive course with quizzes!
|
||||
</p>
|
||||
</a>
|
||||
// <div className="relative flex place-items-center before:absolute before:h-[300px] before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
|
||||
// <Image
|
||||
// className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
|
||||
// src="/next.svg"
|
||||
// alt="Next.js Logo"
|
||||
// width={180}
|
||||
// height={37}
|
||||
// priority
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Templates{' '}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Explore starter templates for Next.js.
|
||||
</p>
|
||||
</a>
|
||||
// <div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
|
||||
// <a
|
||||
// href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
// Docs{' '}
|
||||
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
// ->
|
||||
// </span>
|
||||
// </h2>
|
||||
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
// Find in-depth information about Next.js features and API.
|
||||
// </p>
|
||||
// </a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Deploy{' '}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
// <a
|
||||
// href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
// Learn{' '}
|
||||
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
// ->
|
||||
// </span>
|
||||
// </h2>
|
||||
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
// Learn about Next.js in an interactive course with quizzes!
|
||||
// </p>
|
||||
// </a>
|
||||
|
||||
// <a
|
||||
// href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
// Templates{' '}
|
||||
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
// ->
|
||||
// </span>
|
||||
// </h2>
|
||||
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
// Explore starter templates for Next.js.
|
||||
// </p>
|
||||
// </a>
|
||||
|
||||
// <a
|
||||
// href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
// Deploy{' '}
|
||||
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
// ->
|
||||
// </span>
|
||||
// </h2>
|
||||
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
// Instantly deploy your Next.js site to a shareable URL with Vercel.
|
||||
// </p>
|
||||
// </a>
|
||||
// </div>
|
||||
// {/* This will only be executed on the client side */}
|
||||
// <ClientSideComponent />
|
||||
// </main>
|
||||
// )
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user