Skip to content
This repository has been archived by the owner on Apr 21, 2022. It is now read-only.

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
leomotors committed Feb 26, 2022
0 parents commit f26b7fd
Show file tree
Hide file tree
Showing 21 changed files with 2,769 additions and 0 deletions.
30 changes: 30 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"env": {
"es6": true,
"browser": true
},
"plugins": ["svelte3", "@typescript-eslint"],
"overrides": [
{
"files": ["*.svelte"],
"processor": "svelte3/svelte3"
},
{
"files": ["*.plugin.*"],
"rules": {
"no-console": "off"
}
}
],
"rules": {
"no-console": "warn"
},
"settings": {
"svelte3/typescript": true
}
}
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.vercel

# wtf
.env
src/config.ts
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.yml
*.json
*.md
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Svelte + TS + Vite

This template should help get you started developing with Svelte and TypeScript in Vite.

## Recommended IDE Setup

[VSCode](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).

## Need an official Svelte framework?

Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.

## Technical considerations

**Why use this over SvelteKit?**

- It brings its own routing solution which might not be preferable for some users.
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
`vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example.

This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.

Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.

**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**

Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.

**Why include `.vscode/extensions.json`?**

Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.

**Why enable `allowJs` in the TS template?**

While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.

**Why is HMR not preserving my local component state?**

HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).

If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.

```ts
// store.ts
// An extremely simple external store
import { writable } from 'svelte/store'
export default writable(0)
```
30 changes: 30 additions & 0 deletions api/classify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { VercelRequest, VercelResponse } from "@vercel/node";
import { getMLResult } from "../lib/tf";

export default async (req: VercelRequest, res: VercelResponse) => {
const { body, headers } = req;

if (!process.env.VITE_API_TOKEN) {
res.status(500).json({ message: "Cannot Authorize", error: 500 });
return;
}

if (headers.token != process.env.VITE_API_TOKEN) {
res.status(403).json({ message: "Unauthorized", error: 403 });
return;
}

if (!body.image) {
res.status(400).json({ message: "Missing image", error: 400 });
return;
}

if (!(body.image as string).startsWith("data:image/")) {
res.status(400).json({ message: "Not an image", error: 400 });
return;
}

const result = await getMLResult(body.image);

res.status(200).json(result);
};
14 changes: 14 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Demo of Food Busters' Prototype" />
<link rel="icon" href="somwua.webp" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Food Busters' Food Classifier V2</title>
</head>
<body class="bg-fb-tan min-h-screen">
<div id="app" class="p-8"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
40 changes: 40 additions & 0 deletions lib/tf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as tf from "@tensorflow/tfjs";
import * as tfnode from "@tensorflow/tfjs-node";

const labelMap = {
1: "Omelet Rice",
2: "Chicken Rice",
} as const;

type valueOf<T> = T[keyof T];

export interface MLResult {
type: valueOf<typeof labelMap>;
score: number;
}

export async function getMLResult(img: string) {
const net = await tf.loadGraphModel(
"https://foodbuster.s3.jp-tok.cloud-object-storage.appdomain.cloud/model.json"
);

const decodedImage = tfnode.node.decodeImage(
Buffer.from(img.replace(/^data:image\/.+;base64,/, ""), "base64")
);
const transformed = tf.image
.resizeBilinear(decodedImage, [640, 480])
.cast("int32")
.expandDims(0);
const obj = await net.executeAsync(transformed);

const result: MLResult = {
type: labelMap[(await obj[7].array())[0][0]],
score: (await obj[2].array())[0][0],
};

tf.dispose(decodedImage);
tf.dispose(transformed);
tf.dispose(obj);

return result;
}
39 changes: 39 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "food-classifier-v2",
"private": true,
"version": "2.0.0",
"type": "module",
"scripts": {
"dev": "vite --port $PORT",
"build": "yarn lm-config && vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.json",
"lint": "prettier --check \"src/**/*.{ts,svelte}\" && prettier --check \"*.{js,cjs,ts}\" && eslint src",
"format": "prettier --write \"src/**/*.{ts,svelte}\" && prettier --write \"*.{js,cjs,ts}\""
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.37",
"@tensorflow/tfjs": "^3.13.0",
"@tensorflow/tfjs-node": "^3.13.0",
"@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.1",
"@vercel/node": "^1.13.0",
"autoprefixer": "^10.4.2",
"eslint": "^8.10.0",
"eslint-plugin-svelte3": "^3.4.1",
"postcss": "^8.4.7",
"postcss-load-config": "^3.1.3",
"prettier": "^2.5.1",
"prettier-plugin-svelte": "^2.6.0",
"svelte": "^3.46.4",
"svelte-check": "^2.4.5",
"svelte-preprocess": "^4.10.4",
"tailwindcss": "^3.0.23",
"tslib": "^2.3.1",
"typescript": "^4.5.5",
"vite": "^2.8.4"
},
"dependencies": {
"@leomotors/scripts": "^3.0.7"
}
}
11 changes: 11 additions & 0 deletions postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const tailwindcss = require("tailwindcss");
const autoprefixer = require("autoprefixer");

module.exports = {
plugins: [
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
tailwindcss(),
//But others, like autoprefixer, need to run after,
autoprefixer,
],
};
Binary file added public/redsomwua.webp
Binary file not shown.
Binary file added public/somwua.webp
Binary file not shown.
115 changes: 115 additions & 0 deletions src/App.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<script lang="ts">
import type { MLResult } from "../lib/tf";
import { Version } from "./config";
let files: FileList;
let image: HTMLImageElement;
let showImage = false;
let submited = false;
let imageData = "";
let result: MLResult | APIError | undefined = undefined;
interface APIError {
message: string;
error: number;
}
function isSuccess(obj: MLResult | APIError): obj is MLResult {
return (obj as MLResult).type != undefined;
}
function onImgChange() {
const file = files[0];
if (file) {
showImage = true;
const reader = new FileReader();
reader.addEventListener("load", () => {
imageData = reader.result as string;
image.setAttribute("src", imageData);
submited = false;
result = undefined;
});
reader.readAsDataURL(file);
return;
}
}
async function handleSubmit() {
if (!imageData) return;
submited = true;
const obj = await fetch("/api/classify", {
method: "POST",
headers: {
"Content-Type": "application/json",
"app-version": `web-${Version.split(".")[2]}`,
token: import.meta.env.VITE_API_TOKEN as string,
},
body: JSON.stringify({
image: imageData,
}),
});
result = await obj.json();
}
</script>

<img class="mx-auto mb-5" src="redsomwua.webp" alt="Somwua" width="200" />

<main
class="flex flex-col gap-4 w-1/2 2xl:w-1/3 mx-auto p-4 pb-6 shadow-xl bg-white rounded-xl text-center"
>
<h1 class="font-bold big-p">Amazing Food Classifier</h1>
<hr />
<div class="flex flex-row justify-center items-baseline gap-2">
<label for="image" class="font-bold text-2xl">Your Image:</label>
<input type="file" accept="image/*" bind:files on:change={onImgChange} />
</div>

{#if showImage}
<p>Image Preview</p>
<img bind:this={image} src="" alt="Preview" />
{/if}

<button
class="rounded {submited
? 'bg-fb-lightgreen cursor-not-allowed'
: 'bg-fb-green hover:bg-fb-lightgreen cursor-pointer'}
p-2 my-2 font-bold text-2xl mx-auto"
on:click={handleSubmit}
disabled={submited}
>
Submit
</button>

{#if submited}
{#if result}
{#if isSuccess(result)}
<p class="text-2xl xl:text-4xl">The amazing AI says this picture is</p>
<p>✨{result.type ?? "Loading..."}✨</p>
<p>{(result.score ?? 0) * 100}% sure!</p>
{:else}
<p class="big-p text-red-600">Error {result.error}: {result.message}</p>
{/if}
{:else}
<p class="big-p">Loading...</p>
{/if}
{/if}
</main>

<style lang="postcss">
p {
@apply text-lg lg:text-xl xl:text-2xl font-bold;
}
.big-p {
@apply text-2xl xl:text-4xl;
}
</style>
4 changes: 4 additions & 0 deletions src/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Write your global styles here, in PostCSS syntax */
@tailwind base;
@tailwind components;
@tailwind utilities;
8 changes: 8 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "./app.css";
import App from "./App.svelte";

const app = new App({
target: document.getElementById("app"),
});

export default app;
2 changes: 2 additions & 0 deletions src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />
11 changes: 11 additions & 0 deletions svelte.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import sveltePreprocess from "svelte-preprocess";

export default {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: [
sveltePreprocess({
postcss: true,
}),
],
};
Loading

0 comments on commit f26b7fd

Please sign in to comment.