mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-11-16 08:08:56 -05:00
feat: add primary category and tags
This commit is contained in:
parent
5fe54a80da
commit
f350aaa897
41
bin/init.js
41
bin/init.js
@ -185,7 +185,44 @@ export async function init() {
|
|||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
message: 'Give a one-line description of your mini app (optional):',
|
message: 'Give a one-line description of your mini app (optional):',
|
||||||
default: 'A Farcaster mini-app created with Neynar'
|
default: 'A Farcaster mini app created with Neynar'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'list',
|
||||||
|
name: 'primaryCategory',
|
||||||
|
message: 'It is strongly recommended to choose a primary category and tags to help users discover your mini app.\n\nSelect a primary category:',
|
||||||
|
choices: [
|
||||||
|
{ name: 'Games', value: 'games' },
|
||||||
|
{ name: 'Social', value: 'social' },
|
||||||
|
{ name: 'Finance', value: 'finance' },
|
||||||
|
{ name: 'Utility', value: 'utility' },
|
||||||
|
{ name: 'Productivity', value: 'productivity' },
|
||||||
|
{ name: 'Health & Fitness', value: 'health-fitness' },
|
||||||
|
{ name: 'News & Media', value: 'news-media' },
|
||||||
|
{ name: 'Music', value: 'music' },
|
||||||
|
{ name: 'Shopping', value: 'shopping' },
|
||||||
|
{ name: 'Education', value: 'education' },
|
||||||
|
{ name: 'Developer Tools', value: 'developer-tools' },
|
||||||
|
{ name: 'Entertainment', value: 'entertainment' },
|
||||||
|
{ name: 'Art & Creativity', value: 'art-creativity' },
|
||||||
|
new inquirer.Separator(),
|
||||||
|
{ name: 'Skip (not recommended)', value: null }
|
||||||
|
],
|
||||||
|
default: 'social'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
name: 'tags',
|
||||||
|
message: 'Enter tags for your mini app (separate with spaces or commas, optional):',
|
||||||
|
default: '',
|
||||||
|
filter: (input) => {
|
||||||
|
if (!input.trim()) return [];
|
||||||
|
// Split by both spaces and commas, trim whitespace, and filter out empty strings
|
||||||
|
return input
|
||||||
|
.split(/[,\s]+/)
|
||||||
|
.map(tag => tag.trim())
|
||||||
|
.filter(tag => tag.length > 0);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@ -333,6 +370,8 @@ export async function init() {
|
|||||||
// Append all remaining environment variables
|
// Append all remaining environment variables
|
||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_NAME="${answers.projectName}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_NAME="${answers.projectName}"`);
|
||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_DESCRIPTION="${answers.description}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_DESCRIPTION="${answers.description}"`);
|
||||||
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_PRIMARY_CATEGORY="${answers.primaryCategory}"`);
|
||||||
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_TAGS="${answers.tags.join(',')}"`);
|
||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_BUTTON_TEXT="${answers.buttonText}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_BUTTON_TEXT="${answers.buttonText}"`);
|
||||||
fs.appendFileSync(envPath, `\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"`);
|
fs.appendFileSync(envPath, `\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"`);
|
||||||
if (useNeynar && neynarApiKey && neynarClientId) {
|
if (useNeynar && neynarApiKey && neynarClientId) {
|
||||||
|
|||||||
2
index.d.ts
vendored
2
index.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Initialize a new Farcaster mini-app project
|
* Initialize a new Farcaster mini app project
|
||||||
* @returns Promise<void>
|
* @returns Promise<void>
|
||||||
*/
|
*/
|
||||||
export function init(): Promise<void>;
|
export function init(): Promise<void>;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@neynar/create-farcaster-mini-app",
|
"name": "@neynar/create-farcaster-mini-app",
|
||||||
"version": "1.2.24",
|
"version": "1.2.25",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": false,
|
"private": false,
|
||||||
"access": "public",
|
"access": "public",
|
||||||
@ -22,6 +22,9 @@
|
|||||||
"frame",
|
"frame",
|
||||||
"frames-v2",
|
"frames-v2",
|
||||||
"farcaster-frames",
|
"farcaster-frames",
|
||||||
|
"miniapps",
|
||||||
|
"miniapp",
|
||||||
|
"mini-apps",
|
||||||
"mini-app",
|
"mini-app",
|
||||||
"neynar",
|
"neynar",
|
||||||
"web3"
|
"web3"
|
||||||
|
|||||||
@ -161,6 +161,8 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase
|
|||||||
});
|
});
|
||||||
const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url');
|
const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url');
|
||||||
|
|
||||||
|
const tags = process.env.NEXT_PUBLIC_FRAME_TAGS?.split(',');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accountAssociation: {
|
accountAssociation: {
|
||||||
header: encodedHeader,
|
header: encodedHeader,
|
||||||
@ -177,6 +179,9 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase
|
|||||||
splashImageUrl: `https://${domain}/splash.png`,
|
splashImageUrl: `https://${domain}/splash.png`,
|
||||||
splashBackgroundColor: "#f7f7f7",
|
splashBackgroundColor: "#f7f7f7",
|
||||||
webhookUrl,
|
webhookUrl,
|
||||||
|
description: process.env.NEXT_PUBLIC_FRAME_DESCRIPTION,
|
||||||
|
primaryCategory: process.env.NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY,
|
||||||
|
tags,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -346,6 +351,8 @@ async function main() {
|
|||||||
// Frame metadata
|
// Frame metadata
|
||||||
`NEXT_PUBLIC_FRAME_NAME="${frameName}"`,
|
`NEXT_PUBLIC_FRAME_NAME="${frameName}"`,
|
||||||
`NEXT_PUBLIC_FRAME_DESCRIPTION="${process.env.NEXT_PUBLIC_FRAME_DESCRIPTION || ''}"`,
|
`NEXT_PUBLIC_FRAME_DESCRIPTION="${process.env.NEXT_PUBLIC_FRAME_DESCRIPTION || ''}"`,
|
||||||
|
`NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY="${process.env.NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY || ''}"`,
|
||||||
|
`NEXT_PUBLIC_FRAME_TAGS="${process.env.NEXT_PUBLIC_FRAME_TAGS || ''}"`,
|
||||||
`NEXT_PUBLIC_FRAME_BUTTON_TEXT="${buttonText}"`,
|
`NEXT_PUBLIC_FRAME_BUTTON_TEXT="${buttonText}"`,
|
||||||
|
|
||||||
// Neynar configuration (if it exists in current env)
|
// Neynar configuration (if it exists in current env)
|
||||||
|
|||||||
@ -72,6 +72,8 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase
|
|||||||
});
|
});
|
||||||
const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url');
|
const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url');
|
||||||
|
|
||||||
|
const tags = process.env.NEXT_PUBLIC_FRAME_TAGS?.split(',');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accountAssociation: {
|
accountAssociation: {
|
||||||
header: encodedHeader,
|
header: encodedHeader,
|
||||||
@ -80,14 +82,17 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase
|
|||||||
},
|
},
|
||||||
frame: {
|
frame: {
|
||||||
version: "1",
|
version: "1",
|
||||||
name: process.env.NEXT_PUBLIC_FRAME_NAME?.trim(),
|
name: process.env.NEXT_PUBLIC_FRAME_NAME,
|
||||||
iconUrl: `https://${trimmedDomain}/icon.png`,
|
iconUrl: `https://${trimmedDomain}/icon.png`,
|
||||||
homeUrl: `https://${trimmedDomain}`,
|
homeUrl: `https://${trimmedDomain}`,
|
||||||
imageUrl: `https://${trimmedDomain}/api/opengraph-image`,
|
imageUrl: `https://${trimmedDomain}/api/opengraph-image`,
|
||||||
buttonTitle: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT?.trim(),
|
buttonTitle: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT,
|
||||||
splashImageUrl: `https://${trimmedDomain}/splash.png`,
|
splashImageUrl: `https://${trimmedDomain}/splash.png`,
|
||||||
splashBackgroundColor: "#f7f7f7",
|
splashBackgroundColor: "#f7f7f7",
|
||||||
webhookUrl: webhookUrl?.trim(),
|
webhookUrl: webhookUrl?.trim(),
|
||||||
|
description: process.env.NEXT_PUBLIC_FRAME_DESCRIPTION,
|
||||||
|
primaryCategory: process.env.NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY,
|
||||||
|
tags,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -113,6 +118,8 @@ async function loadEnvLocal() {
|
|||||||
'SEED_PHRASE',
|
'SEED_PHRASE',
|
||||||
'NEXT_PUBLIC_FRAME_NAME',
|
'NEXT_PUBLIC_FRAME_NAME',
|
||||||
'NEXT_PUBLIC_FRAME_DESCRIPTION',
|
'NEXT_PUBLIC_FRAME_DESCRIPTION',
|
||||||
|
'NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY',
|
||||||
|
'NEXT_PUBLIC_FRAME_TAGS',
|
||||||
'NEXT_PUBLIC_FRAME_BUTTON_TEXT',
|
'NEXT_PUBLIC_FRAME_BUTTON_TEXT',
|
||||||
'NEYNAR_API_KEY',
|
'NEYNAR_API_KEY',
|
||||||
'NEYNAR_CLIENT_ID'
|
'NEYNAR_CLIENT_ID'
|
||||||
|
|||||||
@ -101,7 +101,7 @@ async function startDev() {
|
|||||||
1. Open the localtunnel URL in your browser: ${tunnel.url}
|
1. Open the localtunnel URL in your browser: ${tunnel.url}
|
||||||
2. Enter your IP address in the password field${ip ? `: ${ip}` : ''} (note that this IP may be incorrect if you are using a VPN)
|
2. Enter your IP address in the password field${ip ? `: ${ip}` : ''} (note that this IP may be incorrect if you are using a VPN)
|
||||||
3. Click "Click to Submit" -- your mini app should now load in the browser
|
3. Click "Click to Submit" -- your mini app should now load in the browser
|
||||||
4. Navigate to the Warpcast Mini App Developer Tools: https://warpcast.com/~/developers/mini-apps
|
4. Navigate to the Warpcast Mini App Developer Tools: https://warpcast.com/~/developers
|
||||||
5. Enter your mini app URL: ${tunnel.url}
|
5. Enter your mini app URL: ${tunnel.url}
|
||||||
6. Click "Preview" to launch your mini app within Warpcast (note that it may take ~10 seconds to load)
|
6. Click "Preview" to launch your mini app within Warpcast (note that it may take ~10 seconds to load)
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ async function startDev() {
|
|||||||
frameUrl = 'http://localhost:3000';
|
frameUrl = 'http://localhost:3000';
|
||||||
console.log(`
|
console.log(`
|
||||||
💻 To test your mini app:
|
💻 To test your mini app:
|
||||||
1. Open the Warpcast Mini App Developer Tools: https://warpcast.com/~/developers/mini-apps
|
1. Open the Warpcast Mini App Developer Tools: https://warpcast.com/~/developers
|
||||||
2. Scroll down to the "Preview Mini App" tool
|
2. Scroll down to the "Preview Mini App" tool
|
||||||
3. Enter this URL: ${frameUrl}
|
3. Enter this URL: ${frameUrl}
|
||||||
4. Click "Preview" to test your mini app (note that it may take ~5 seconds to load the first time)
|
4. Click "Preview" to test your mini app (note that it may take ~5 seconds to load the first time)
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
export const APP_URL = process.env.NEXT_PUBLIC_URL!;
|
export const APP_URL = process.env.NEXT_PUBLIC_URL!;
|
||||||
export const APP_NAME = process.env.NEXT_PUBLIC_FRAME_NAME;
|
export const APP_NAME = process.env.NEXT_PUBLIC_FRAME_NAME;
|
||||||
export const APP_DESCRIPTION = process.env.NEXT_PUBLIC_FRAME_DESCRIPTION;
|
export const APP_DESCRIPTION = process.env.NEXT_PUBLIC_FRAME_DESCRIPTION;
|
||||||
|
export const APP_PRIMARY_CATEGORY = process.env.NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY;
|
||||||
|
export const APP_TAGS = process.env.NEXT_PUBLIC_FRAME_TAGS?.split(',');
|
||||||
export const APP_ICON_URL = `${APP_URL}/icon.png`;
|
export const APP_ICON_URL = `${APP_URL}/icon.png`;
|
||||||
export const APP_OG_IMAGE_URL = `${APP_URL}/api/opengraph-image`;
|
export const APP_OG_IMAGE_URL = `${APP_URL}/api/opengraph-image`;
|
||||||
export const APP_SPLASH_URL = `${APP_URL}/splash.png`;
|
export const APP_SPLASH_URL = `${APP_URL}/splash.png`;
|
||||||
|
|||||||
@ -1,26 +1,31 @@
|
|||||||
import { type ClassValue, clsx } from 'clsx';
|
import { type ClassValue, clsx } from 'clsx';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
import { mnemonicToAccount } from 'viem/accounts';
|
import { mnemonicToAccount } from 'viem/accounts';
|
||||||
import { APP_BUTTON_TEXT, APP_ICON_URL, APP_NAME, APP_OG_IMAGE_URL, APP_SPLASH_BACKGROUND_COLOR, APP_URL, APP_WEBHOOK_URL } from './constants';
|
import { APP_BUTTON_TEXT, APP_DESCRIPTION, APP_ICON_URL, APP_NAME, APP_OG_IMAGE_URL, APP_PRIMARY_CATEGORY, APP_SPLASH_BACKGROUND_COLOR, APP_TAGS, APP_URL, APP_WEBHOOK_URL } from './constants';
|
||||||
import { APP_SPLASH_URL } from './constants';
|
import { APP_SPLASH_URL } from './constants';
|
||||||
|
|
||||||
interface FrameMetadata {
|
interface FrameMetadata {
|
||||||
|
version: string;
|
||||||
|
name: string;
|
||||||
|
iconUrl: string;
|
||||||
|
homeUrl: string;
|
||||||
|
imageUrl?: string;
|
||||||
|
buttonTitle?: string;
|
||||||
|
splashImageUrl?: string;
|
||||||
|
splashBackgroundColor?: string;
|
||||||
|
webhookUrl?: string;
|
||||||
|
description?: string;
|
||||||
|
primaryCategory?: string;
|
||||||
|
tags?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
interface FrameManifest {
|
||||||
accountAssociation?: {
|
accountAssociation?: {
|
||||||
header: string;
|
header: string;
|
||||||
payload: string;
|
payload: string;
|
||||||
signature: string;
|
signature: string;
|
||||||
};
|
};
|
||||||
frame: {
|
frame: FrameMetadata;
|
||||||
version: string;
|
|
||||||
name: string;
|
|
||||||
iconUrl: string;
|
|
||||||
homeUrl: string;
|
|
||||||
imageUrl: string;
|
|
||||||
buttonTitle: string;
|
|
||||||
splashImageUrl: string;
|
|
||||||
splashBackgroundColor: string;
|
|
||||||
webhookUrl: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
@ -51,12 +56,15 @@ export function getFrameEmbedMetadata(ogImageUrl?: string) {
|
|||||||
splashImageUrl: APP_SPLASH_URL,
|
splashImageUrl: APP_SPLASH_URL,
|
||||||
iconUrl: APP_ICON_URL,
|
iconUrl: APP_ICON_URL,
|
||||||
splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR,
|
splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR,
|
||||||
|
description: APP_DESCRIPTION,
|
||||||
|
primaryCategory: APP_PRIMARY_CATEGORY,
|
||||||
|
tags: APP_TAGS,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFarcasterMetadata(): Promise<FrameMetadata> {
|
export async function getFarcasterMetadata(): Promise<FrameManifest> {
|
||||||
// First check for FRAME_METADATA in .env and use that if it exists
|
// First check for FRAME_METADATA in .env and use that if it exists
|
||||||
if (process.env.FRAME_METADATA) {
|
if (process.env.FRAME_METADATA) {
|
||||||
try {
|
try {
|
||||||
@ -123,6 +131,9 @@ export async function getFarcasterMetadata(): Promise<FrameMetadata> {
|
|||||||
splashImageUrl: APP_SPLASH_URL,
|
splashImageUrl: APP_SPLASH_URL,
|
||||||
splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR,
|
splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR,
|
||||||
webhookUrl: APP_WEBHOOK_URL,
|
webhookUrl: APP_WEBHOOK_URL,
|
||||||
|
description: APP_DESCRIPTION,
|
||||||
|
primaryCategory: APP_PRIMARY_CATEGORY,
|
||||||
|
tags: APP_TAGS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user