Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/fal-ai/serverless-js into f…
Browse files Browse the repository at this point in the history
…eature/file-upload-capabilities
  • Loading branch information
drochetti committed Nov 1, 2023
2 parents 46adce6 + 3a98fd6 commit 4c673aa
Show file tree
Hide file tree
Showing 47 changed files with 16,376 additions and 41,870 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ Thumbs.db

# Next.js
.next
*.local
*.local
40 changes: 40 additions & 0 deletions apps/demo-nextjs-app-router/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"extends": [
"plugin:@nx/react-typescript",
"next",
"next/core-web-vitals",
"../../.eslintrc.json"
],
"ignorePatterns": ["!**/*", ".next/**/*"],
"overrides": [
{
"files": ["*.*"],
"rules": {
"@next/next/no-html-link-for-pages": "off"
}
},
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"@next/next/no-html-link-for-pages": [
"error",
"apps/demo-nextjs-app-router/pages"
]
}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"],
"env": {
"jest": true
}
}
]
}
3 changes: 3 additions & 0 deletions apps/demo-nextjs-app-router/app/api/fal/proxy/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { route } from '@fal-ai/serverless-proxy/nextjs';

export const { GET, POST } = route;
File renamed without changes.
18 changes: 18 additions & 0 deletions apps/demo-nextjs-app-router/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import './global.css';

export const metadata = {
title: 'Welcome to demo-nextjs-app-router',
description: 'Generated by create-nx-workspace',
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
File renamed without changes.
170 changes: 170 additions & 0 deletions apps/demo-nextjs-app-router/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
'use client';

import * as fal from '@fal-ai/serverless-client';
import { useMemo, useState } from 'react';

// @snippet:start(client.config)
fal.config({
requestMiddleware: fal.withProxy({
targetUrl: '/api/fal/proxy', // the built-int nextjs proxy
// targetUrl: 'http://localhost:3333/api/_fal/proxy', // or your own external proxy
}),
});
// @snippet:end

// @snippet:start(client.result.type)
type Image = {
url: string;
file_name: string;
file_size: number;
};
type Result = {
images: Image[];
};
// @snippet:end

type ErrorProps = {
error: any;
};

function Error(props: ErrorProps) {
if (!props.error) {
return null;
}
return (
<div
className="p-4 mb-4 text-sm text-red-800 rounded bg-red-50 dark:bg-gray-800 dark:text-red-400"
role="alert"
>
<span className="font-medium">Error</span> {props.error.message}
</div>
);
}

const DEFAULT_PROMPT =
'a city landscape of a cyberpunk metropolis, raining, purple, pink and teal neon lights, highly detailed, uhd';

export default function Home() {
// @snippet:start("client.ui.state")
// Input state
const [prompt, setPrompt] = useState<string>(DEFAULT_PROMPT);
// Result state
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const [result, setResult] = useState<Result | null>(null);
const [logs, setLogs] = useState<string[]>([]);
const [elapsedTime, setElapsedTime] = useState<number>(0);
// @snippet:end
const image = useMemo(() => {
if (!result) {
return null;
}
return result.images[0];
}, [result]);

const reset = () => {
setLoading(false);
setError(null);
setResult(null);
setLogs([]);
setElapsedTime(0);
};

const generateImage = async () => {
reset();
// @snippet:start("client.queue.subscribe")
setLoading(true);
const start = Date.now();
try {
const result: Result = await fal.subscribe('110602490-lora', {
input: {
prompt,
model_name: 'stabilityai/stable-diffusion-xl-base-1.0',
image_size: 'square_hd',
},
pollInterval: 5000, // Default is 1000 (every 1s)
logs: true,
onQueueUpdate(update) {
setElapsedTime(Date.now() - start);
if (
update.status === 'IN_PROGRESS' ||
update.status === 'COMPLETED'
) {
setLogs((update.logs || []).map((log) => log.message));
}
},
});
setResult(result);
} catch (error: any) {
setError(error);
} finally {
setLoading(false);
setElapsedTime(Date.now() - start);
}
// @snippet:end
};
return (
<div className="min-h-screen dark:bg-gray-900 bg-gray-100">
<main className="container dark:text-gray-50 text-gray-900 flex flex-col items-center justify-center w-full flex-1 py-10 space-y-8">
<h1 className="text-4xl font-bold mb-8">
Hello <code className="font-light text-pink-600">fal</code>
</h1>
<div className="text-lg w-full">
<label htmlFor="prompt" className="block mb-2 text-current">
Prompt
</label>
<input
className="w-full text-lg p-2 rounded bg-black/10 dark:bg-white/5 border border-black/20 dark:border-white/10"
id="prompt"
name="prompt"
placeholder="Imagine..."
value={prompt}
autoComplete="off"
onChange={(e) => setPrompt(e.target.value)}
onBlur={(e) => setPrompt(e.target.value.trim())}
/>
</div>

<button
onClick={(e) => {
e.preventDefault();
generateImage();
}}
className="bg-indigo-600 hover:bg-indigo-700 text-white font-bold text-lg py-3 px-6 mx-auto rounded focus:outline-none focus:shadow-outline"
disabled={loading}
>
{loading ? 'Generating...' : 'Generate Image'}
</button>

<Error error={error} />

<div className="w-full flex flex-col space-y-4">
<div className="mx-auto">
{image && (
// eslint-disable-next-line @next/next/no-img-element
<img src={image.url} alt="" />
)}
</div>
<div className="space-y-2">
<h3 className="text-xl font-light">JSON Result</h3>
<p className="text-sm text-current/80">
{`Elapsed Time (seconds): ${(elapsedTime / 1000).toFixed(2)}`}
</p>
<pre className="text-sm bg-black/70 text-white/80 font-mono h-60 rounded whitespace-pre overflow-auto w-full">
{result
? JSON.stringify(result, null, 2)
: '// result pending...'}
</pre>
</div>

<div className="space-y-2">
<h3 className="text-xl font-light">Logs</h3>
<pre className="text-sm bg-black/70 text-white/80 font-mono h-60 rounded whitespace-pre overflow-auto w-full">
{logs.filter(Boolean).join('\n')}
</pre>
</div>
</div>
</main>
</div>
);
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable */
export default {
displayName: 'demo-nextjs-app',
displayName: 'demo-nextjs-app-router',
preset: '../../jest.preset.js',
transform: {
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/next/babel'] }],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/apps/demo-nextjs-app',
coverageDirectory: '../../coverage/apps/demo-nextjs-app-router',
};
File renamed without changes.
22 changes: 22 additions & 0 deletions apps/demo-nextjs-app-router/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//@ts-check

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { composePlugins, withNx } = require('@nx/next');

/**
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
**/
const nextConfig = {
nx: {
// Set this to true if you would like to use SVGR
// See: https://github.com/gregberge/svgr
svgr: false,
},
};

const plugins = [
// Add more Next.js plugins to this list if needed.
withNx,
];

module.exports = composePlugins(...plugins)(nextConfig);
File renamed without changes.
68 changes: 68 additions & 0 deletions apps/demo-nextjs-app-router/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"name": "demo-nextjs-app-router",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/demo-nextjs-app-router",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/next:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"outputPath": "dist/apps/demo-nextjs-app-router"
},
"configurations": {
"development": {
"outputPath": "apps/demo-nextjs-app-router"
},
"production": {}
}
},
"serve": {
"executor": "@nx/next:server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "demo-nextjs-app-router:build",
"dev": true
},
"configurations": {
"development": {
"buildTarget": "demo-nextjs-app-router:build:development",
"dev": true
},
"production": {
"buildTarget": "demo-nextjs-app-router:build:production",
"dev": false
}
}
},
"export": {
"executor": "@nx/next:export",
"options": {
"buildTarget": "demo-nextjs-app-router:build:production"
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/demo-nextjs-app-router/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/demo-nextjs-app-router/**/*.{ts,tsx,js,jsx}"]
}
}
},
"tags": []
}
File renamed without changes.
Binary file added apps/demo-nextjs-app-router/public/favicon.ico
Binary file not shown.
18 changes: 18 additions & 0 deletions apps/demo-nextjs-app-router/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
const { join } = require('path');

/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
join(
__dirname,
'{src,pages,components,app}/**/*!(*.stories|*.spec).{ts,tsx,html}'
),
...createGlobPatternsForDependencies(__dirname),
],
darkMode: 'class',
theme: {
extend: {},
},
plugins: [],
};
Loading

0 comments on commit 4c673aa

Please sign in to comment.