├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── src ├── App.tsx ├── assets │ ├── blob.avif │ ├── blob.png │ ├── favicon.ico │ ├── fire_emoji.gif │ ├── laion_logo.avif │ ├── laion_logo.jpg │ ├── laion_logo.png │ ├── laionlarge.png │ ├── logo.svg │ ├── monkey.svg │ ├── monkey_hands.svg │ ├── monkey_happy.svg │ └── monkey_surprised.svg ├── components │ ├── animation.tsx │ ├── avatar.tsx │ ├── dropdown.tsx │ ├── options.tsx │ ├── passwordInput.tsx │ └── sidebar.tsx ├── contexts │ ├── config.tsx │ ├── data.tsx │ └── supabase.tsx ├── fonts │ ├── Dinish-Italic.woff │ ├── Dinish-Italic.woff2 │ ├── Dinish-Regular.otf │ ├── Dinish-Regular.woff │ ├── Dinish-Regular.woff2 │ ├── DinishCondensed-Bold.woff │ └── DinishCondensed-Bold.woff2 ├── index.tsx ├── pages │ ├── .vscode │ │ └── settings.json │ ├── hamburger.css │ ├── landing.tsx │ ├── login.tsx │ ├── recovery.tsx │ └── space │ │ ├── annotation.tsx │ │ ├── create.tsx │ │ ├── gallery.tsx │ │ └── horde.tsx └── shims.d.ts ├── tsconfig.json ├── unocss.config.ts ├── vercel.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .vscoode 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # TypeScript v1 declaration files 46 | typings/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Microbundle cache 58 | .rpt2_cache/ 59 | .rts2_cache_cjs/ 60 | .rts2_cache_es/ 61 | .rts2_cache_umd/ 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | 66 | # Output of 'npm pack' 67 | *.tgz 68 | 69 | # Yarn Integrity file 70 | .yarn-integrity 71 | 72 | # dotenv environment variables file 73 | .env 74 | .env.test 75 | 76 | # parcel-bundler cache (https://parceljs.org/) 77 | .cache 78 | 79 | # Next.js build output 80 | .next 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | .vercel 107 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nuxt.isNuxtApp": false 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Benjamin Trom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenDream 2 | Front for the OpenDream project by LAION (powered by Selas) 3 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OpenDream 8 | 9 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dave", 3 | "version": "0.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "vite", 7 | "dev": "vite", 8 | "build": "vite build", 9 | "serve": "vite preview" 10 | }, 11 | "license": "MIT", 12 | "devDependencies": { 13 | "@iconify/json": ">=2.1.76", 14 | "@types/uuid": ">=8.3.4", 15 | "@unocss/preset-tagify": "^0.45.15", 16 | "@unocss/preset-uno": "^0.45.13", 17 | "@unocss/preset-wind": "^0.45.15", 18 | "prettier": ">=2.7.1", 19 | "typescript": ">=4.6.4", 20 | "unocss": "^0.45.21", 21 | "unocss-preset-extra": "^0.4.3", 22 | "unocss-preset-scalpel": "^1.2.4", 23 | "vite": ">=2.9.9", 24 | "vite-plugin-solid": ">=2.2.6", 25 | "vite-plugin-solid-svg": "^0.5.0" 26 | }, 27 | "dependencies": { 28 | "@julr/unocss-preset-forms": "^0.0.2", 29 | "@solid-primitives/scheduled": "^1.0.1", 30 | "@supabase/supabase-js": "^2.0.0", 31 | "@thisbeyond/solid-dnd": "^0.7.2", 32 | "@unocss/preset-icons": "^0.45.26", 33 | "@unocss/reset": "^0.45.21", 34 | "bip39": "^3.0.4", 35 | "pixi.js": "^6.5.7", 36 | "selas": "^0.0.11", 37 | "solid-app-router": ">=0.3.3", 38 | "solid-boundaries": "^2.1.0", 39 | "solid-dexie": "^0.0.5", 40 | "solid-dismiss": ">=1.2.1", 41 | "solid-icons": ">=0.4.13", 42 | "solid-js": "^1.5.5", 43 | "solid-slider": "^1.3.9", 44 | "solid-use": ">=0.4.2", 45 | "string-hash": "^1.1.3", 46 | "tinycolor2": "^1.4.2", 47 | "unocss-preset-better-nested-colors": "^1.0.0", 48 | "unocss-preset-scrollbar": "^0.2.1" 49 | }, 50 | "type": "module" 51 | } 52 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { createClient, SupabaseClient } from "@supabase/supabase-js"; 2 | import { Outlet, Route, Routes, useLocation, useNavigate } from "solid-app-router"; 3 | import { Component, createEffect, createMemo, onMount, Show } from "solid-js"; 4 | 5 | import Landing from "./pages/landing"; 6 | import Login from "./pages/login"; 7 | 8 | import Logo from "../assets/logo.svg"; 9 | import { atom } from "solid-use"; 10 | import { Sidebar } from "./components/sidebar"; 11 | import { createOnAuthStateChange, ProfileProvider, SupabaseProvider, useProfile } from "./contexts/supabase"; 12 | import Gallery from "./pages/space/gallery"; 13 | import Create from "./pages/space/create"; 14 | import { DataProvider } from "./contexts/data"; 15 | import { ConfigProvider } from "./contexts/config"; 16 | import PasswordInput, { SecretPhraseInput } from "./components/passwordInput"; 17 | import Annotation from "./pages/space/annotation"; 18 | import Horde from "./pages/space/horde"; 19 | 20 | const App: Component = () => { 21 | const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; 22 | const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY; 23 | const supabase: SupabaseClient = createClient(supabaseUrl, supabaseAnonKey); 24 | 25 | return ( 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | export function Space() { 48 | const { secretPhrase } = useProfile(); 49 | const navigate = useNavigate(); 50 | 51 | const toggle = atom(true); 52 | const hide = atom(false); 53 | 54 | setTimeout(() => { 55 | if (!secretPhrase()) { 56 | navigate("/login"); 57 | } 58 | }, 1000); 59 | 60 | 61 | 62 | createEffect(() => { 63 | if (!toggle()) { 64 | setTimeout(() => { 65 | hide(true); 66 | }, 500); 67 | } else { 68 | hide(false); 69 | } 70 | }); 71 | 72 | return ( 73 | 74 | 75 |
76 | 77 | 78 |
79 | {/* Main section content */} 80 |
85 | 86 |
87 |
88 |
89 |
90 | ); 91 | } 92 | 93 | const FirstConnectPopup: Component = () => { 94 | const { firstConnect } = useProfile(); 95 | const disabled = atom(true); 96 | 97 | return ( 98 | 99 |
100 |
101 |

Welcome to OpenDream!

102 |

OpenDream is a community gathered around the open-source project LAION. Our goal is to gather annotations to create open-source datasets.

103 |

We are not collecting any data on the users, and please write down carefully your secret phrase as there is no way to get back your account if you lose it.

104 | 105 | 106 | 107 |
108 | disabled(!disabled)} class="cursor-pointer" type="checkbox" /> 109 | 110 |
111 | 112 | 113 | 114 |
115 |
116 |
117 | ) 118 | } 119 | 120 | export default App; 121 | -------------------------------------------------------------------------------- /src/assets/blob.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/assets/blob.avif -------------------------------------------------------------------------------- /src/assets/blob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/assets/blob.png -------------------------------------------------------------------------------- /src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/assets/favicon.ico -------------------------------------------------------------------------------- /src/assets/fire_emoji.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/assets/fire_emoji.gif -------------------------------------------------------------------------------- /src/assets/laion_logo.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/assets/laion_logo.avif -------------------------------------------------------------------------------- /src/assets/laion_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/assets/laion_logo.jpg -------------------------------------------------------------------------------- /src/assets/laion_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/assets/laion_logo.png -------------------------------------------------------------------------------- /src/assets/laionlarge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/assets/laionlarge.png -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/monkey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/monkey_hands.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /src/assets/monkey_happy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/assets/monkey_surprised.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/animation.tsx: -------------------------------------------------------------------------------- 1 | import { Component, onMount } from "solid-js"; 2 | import * as PIXI from "pixi.js"; 3 | 4 | export const AnimationImage: Component<{ width: number; height: number; image: string; depth: string; speed: number }> = ( 5 | props 6 | ) => { 7 | let ref; 8 | 9 | onMount(() => { 10 | const app = new PIXI.Application({ width: props.width / 1.03, height: props.height / 1.03, backgroundAlpha: 0 }); 11 | ref.appendChild(app.view); 12 | let sprite = PIXI.Sprite.from(props.image); 13 | app.stage.addChild(sprite); 14 | sprite.width = props.width; 15 | sprite.height = props.height; 16 | let depthMap = PIXI.Sprite.from(props.depth); 17 | app.stage.addChild(depthMap); 18 | 19 | let displacementFilter = new PIXI.filters.DisplacementFilter(depthMap); 20 | app.stage.filters = [displacementFilter]; 21 | 22 | window.onmousemove = function (e) { 23 | displacementFilter.scale.x = (window.innerWidth / 2 - e.clientX) / (100 / props.speed); 24 | displacementFilter.scale.y = (window.innerHeight / 2 - e.clientY) / (100 / props.speed); 25 | }; 26 | }); 27 | 28 | return ( 29 |
30 |
31 |
32 | ); 33 | }; -------------------------------------------------------------------------------- /src/components/avatar.tsx: -------------------------------------------------------------------------------- 1 | import { Component } from "solid-js" 2 | import hash from 'string-hash' 3 | import color from 'tinycolor2' 4 | 5 | const HashAvatar: Component<{name: string, height: number, width: number, radius: number}> = (props) => { 6 | 7 | const { name, height, width, radius } = props 8 | 9 | const n = hash(name) 10 | const c1 = color({ h: n % 360, s: 0.95, l: 0.5 }) 11 | const c1_ = c1.toHexString() 12 | const c2 = c1.triad()[1].toHexString() 13 | 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ) 27 | } 28 | 29 | export default HashAvatar; -------------------------------------------------------------------------------- /src/components/dropdown.tsx: -------------------------------------------------------------------------------- 1 | import { debounce, leading, throttle } from '@solid-primitives/scheduled'; 2 | import { User } from '@supabase/supabase-js'; 3 | import { Link, useNavigate } from 'solid-app-router'; 4 | 5 | import { Component, createEffect, JSX, onCleanup, Show } from 'solid-js'; 6 | import { createOnAuthStateChange, createSupabaseAuth } from '../contexts/supabase'; 7 | import { atom } from 'solid-use'; 8 | import HashAvatar from './avatar'; 9 | 10 | 11 | 12 | 13 | const DropdownAvatar: Component<{}> = (props) => { 14 | const auth = createSupabaseAuth(); 15 | const user = atom(null) 16 | const dropdown = atom(false) 17 | 18 | createOnAuthStateChange((event, session) => { 19 | if (event == 'SIGNED_OUT') { 20 | user(null) 21 | } 22 | else { 23 | user(session?.user) 24 | } 25 | }) 26 | 27 | function onKeyDown(event) { 28 | if (event.key === 'Escape' || event.key === 'Tab') { 29 | dropdown(false); 30 | } 31 | } 32 | 33 | const closeTimeout = atom(true) 34 | const closeTimeoutSecondary = atom(true) 35 | 36 | window.addEventListener('keydown', onKeyDown); 37 | 38 | createEffect(() => { 39 | if (dropdown() && closeTimeout() && closeTimeoutSecondary()) { 40 | setTimeout(() => { 41 | if (closeTimeout() && closeTimeoutSecondary()) { 42 | dropdown(false) 43 | } 44 | }, 3000) 45 | } 46 | }) 47 | 48 | const activateDropdown = leading(debounce, () => { 49 | if (!dropdown()) { dropdown(!dropdown()) } 50 | closeTimeout(false) 51 | }, 1000) 52 | 53 | const clickAvatar = () => { 54 | dropdown(!dropdown()) 55 | if (dropdown()) { 56 | closeTimeout(false) 57 | } 58 | } 59 | 60 | const navigate = useNavigate(); 61 | 62 | onCleanup(() => window.removeEventListener('keydown', onKeyDown)); 63 | 64 | 65 | return ( 66 | 67 |
68 | 71 | 72 |
closeTimeoutSecondary(true)} onClick={() => dropdown(false)} class="fixed z-50 top-0 left-0 w-screen h-screen bg-none">
73 |
closeTimeoutSecondary(false)} class="flex flex-col animated elevation-10 animated-slide-in-right animated-duration-400 shadow-op-30 absolute right-0px top-86px bg-white w-284px h-auto rounded-8px z-50"> 74 |
{user().email}
75 | 76 | 77 |
78 |
79 |
80 |
81 | ) 82 | } 83 | 84 | export default DropdownAvatar; -------------------------------------------------------------------------------- /src/components/options.tsx: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { 3 | DragDropProvider, 4 | DragDropSensors, 5 | DragOverlay, 6 | SortableProvider, 7 | createSortable, 8 | closestCenter, 9 | } from "@thisbeyond/solid-dnd"; 10 | import { createEffect, createMemo, createSignal, For, Switch } from "solid-js"; 11 | 12 | import "solid-slider/slider.css"; 13 | import { Slider, createSlider, SliderProvider, SliderButton } from "solid-slider"; 14 | import { useConfig } from "../contexts/config"; 15 | 16 | const InitCard = () => { 17 | const sortable = createSortable(2); 18 | return ( 19 | <> 20 |

Starting Image

21 |
22 | 23 | ); 24 | }; 25 | 26 | const DiffusionCard = () => { 27 | const sortable = createSortable(3); 28 | return ( 29 | <> 30 |

Diffusion Parameters

31 |
32 | 33 | ); 34 | }; 35 | 36 | const FormatCard = () => { 37 | const sortable = createSortable(1); 38 | const { diffusionConfig } = useConfig(); 39 | const options = { loop: true }; 40 | const [slider, { current, next, prev, moveTo }] = createSlider(options); 41 | 42 | enum Size { 43 | square, 44 | landscape, 45 | portrait, 46 | } 47 | 48 | // const width = createMemo(() => { 49 | // Size[current()] == "landscape" ? "768" : "512"; 50 | // }); 51 | // const height = createMemo(() => { 52 | // Size[current()] == "landscape" ? "768" : "512"; 53 | // }); 54 | 55 | createEffect(() => { 56 | const currentConfig = diffusionConfig(); 57 | currentConfig.width = Size[current()] == "landscape" ? "768" : "512"; 58 | currentConfig.height = Size[current()] == "portrait" ? "768" : "512"; 59 | 60 | console.log(currentConfig); 61 | diffusionConfig(currentConfig); 62 | }); 63 | 64 | 65 | return ( 66 | <> 67 | 68 |

Image Format

69 |
70 |
prev()} 72 | class="i-bxs:chevron-left transition self-center fs-1.6rem hover:color-blu hover:shadow-xl hover:scale-140 cursor-pointer" 73 | /> 74 |
75 |
76 |
77 |
Square
{" "} 78 |
79 |
80 |
81 |
82 |
Landscape
{" "} 83 |
84 |
85 |
86 |
87 |
Portrait
{" "} 88 |
89 |
90 |
91 |
next()} 93 | class="i-bxs:chevron-right transition self-center fs-1.6rem hover:color-blu hover:shadow-xl hover:scale-140 cursor-pointer" 94 | /> 95 |
96 | 97 | 98 | ); 99 | }; 100 | 101 | const Sortable = (props) => { 102 | const sortable = createSortable(props.item); 103 | 104 | return ( 105 |
110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | ); 120 | }; 121 | 122 | export const DND = () => { 123 | const [items, setItems] = createSignal([1, 2]); 124 | const [activeItem, setActiveItem] = createSignal(null); 125 | const ids = () => items(); 126 | 127 | const onDragStart = ({ draggable }) => setActiveItem(draggable.id); 128 | 129 | const onDragEnd = ({ draggable, droppable }) => { 130 | if (draggable && droppable) { 131 | const currentItems = ids(); 132 | const fromIndex = currentItems.indexOf(draggable.id); 133 | const toIndex = currentItems.indexOf(droppable.id); 134 | if (fromIndex !== toIndex) { 135 | const updatedItems = currentItems.slice(); 136 | updatedItems.splice(toIndex, 0, ...updatedItems.splice(fromIndex, 1)); 137 | setItems(updatedItems); 138 | } 139 | } 140 | setActiveItem(null); 141 | }; 142 | 143 | return ( 144 | 145 | 146 |
147 | 148 | {(item) => } 149 | 150 |
153 |
154 |
155 | 156 |
157 | 158 | 159 | 160 | 161 | ); 162 | }; 163 | -------------------------------------------------------------------------------- /src/components/passwordInput.tsx: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import Monkey from "../assets/monkey_surprised.svg"; 3 | import MonkeyHappy from "../assets/monkey_happy.svg"; 4 | import MonkeyHands from "../assets/monkey_hands.svg"; 5 | import { Component, Show } from "solid-js"; 6 | import { atom, Atom } from "solid-use"; 7 | import { useProfile } from "../contexts/supabase"; 8 | 9 | const PasswordInput: Component<{ password: Atom }> = (props) => { 10 | const showPassword = atom(false); 11 | 12 | return ( 13 |
14 | 15 | props.password(e.currentTarget.value)} 18 | class="input input border-blu/60 focus:border-blu h-48px" 19 | /> 20 |
showPassword(!showPassword())} class="cursor-pointer"> 21 | {/* 22 | // @ts-ignore */} 23 | } 26 | > 27 | {/* 28 | // @ts-ignore */} 29 | 30 | 31 | {/* 32 | // @ts-ignore */} 33 | 40 |
41 |
42 | ); 43 | }; 44 | 45 | 46 | export const SecretPhraseInput: Component<{}> = (props) => { 47 | const showPassword = atom(false); 48 | const { secretPhrase } = useProfile(); 49 | 50 | return ( 51 |
52 | 53 | 59 |
showPassword(!showPassword())} class="cursor-pointer"> 60 | {/* 61 | // @ts-ignore */} 62 | } 65 | > 66 | {/* 67 | // @ts-ignore */} 68 | 69 | 70 | {/* 71 | // @ts-ignore */} 72 | 79 |
80 |
81 | ); 82 | }; 83 | 84 | export default PasswordInput; 85 | -------------------------------------------------------------------------------- /src/components/sidebar.tsx: -------------------------------------------------------------------------------- 1 | import { Component, createEffect, For, Show } from "solid-js"; 2 | 3 | import Logo from "../assets/logo.svg"; 4 | import { Atom, atom } from "solid-use"; 5 | import { Link, Navigate, NavLink, useLocation, useNavigate } from "solid-app-router"; 6 | import HashAvatar from "./avatar"; 7 | import { useProfile } from "../contexts/supabase"; 8 | 9 | export const Sidebar: Component<{ toggle: Atom }> = (props) => { 10 | const { secretPhrase, credits } = useProfile(); 11 | const hide = atom(true); 12 | const navigate = useNavigate(); 13 | 14 | 15 | const handleLogout = () => { 16 | secretPhrase(null); 17 | navigate("/"); 18 | }; 19 | 20 | createEffect(() => { 21 | if (!props.toggle()) { 22 | setTimeout(() => { 23 | hide(true); 24 | }, 500); 25 | } else { 26 | hide(false); 27 | } 28 | }); 29 | 30 | return ( 31 |
32 |
33 | 34 |
39 | {/* Logo */} 40 |
41 | 42 |
43 | {/* 44 | // @ts-ignore */} 45 | 46 | OpenDream 47 |
48 | 49 |
50 | 51 | {/* Avatar and username + company */} 52 |
53 | 54 |
55 | 56 |
57 | 58 |
59 |
60 | {credits()} 61 |
62 |
63 | 64 | {/* Sidebar Menu*/} 65 |
66 | 67 | 68 | 69 | 70 |
71 | 72 | {/* Logout button */} 73 | 77 |
78 |
79 |
80 | ); 81 | }; 82 | 83 | const SidebarItem: Component<{ href?: string; title: string; icon: string }> = (props) => { 84 | const isHover = atom(false) 85 | 86 | if (!props.href) { 87 | props.href = props.title.toLowerCase(); 88 | } 89 | 90 | return ( 91 | isHover(true)} 93 | onMouseLeave={() => isHover(false)} 94 | href={props.href} 95 | class="flex transition pl-2.8rem hover:bg-gradient-to-r hover:from-transparent hover:to-blu/20 fw-500 fs-1.2rem rounded-16px py-1rem min-w-16rem shadow-blue/80 shadow-sm hover:shadow-lg" 96 | activeClass="hover:shadow-none bg-blu/90 color-white" 97 | > 98 |
99 |
100 | {props.title} 101 |
102 |
103 | ); 104 | }; 105 | 106 | const Hamburger: Component<{open: Atom}> = (props) => { 107 | 108 | const handleClick = () => { 109 | props.open(!props.open()); 110 | console.log(props.open()); 111 | }; 112 | 113 | return ( 114 | <> 115 | 131 | 132 | ); 133 | }; 134 | -------------------------------------------------------------------------------- /src/contexts/config.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from "solid-js"; 2 | import { atom, Atom } from "solid-use"; 3 | import { createStore } from "solid-js/store"; 4 | import { DiffusionConfig, IOConfig, Job, Result } from "selas/index"; 5 | 6 | 7 | 8 | const ConfigContext = createContext<{ioConfig: Atom, diffusionConfig: Atom}>(); 9 | 10 | export function ConfigProvider(props) { 11 | const ioConfig = atom({} as IOConfig); 12 | const diffusionConfig = atom({} as DiffusionConfig); 13 | 14 | return {props.children}; 15 | } 16 | 17 | export const useConfig = () => { 18 | return useContext(ConfigContext); 19 | }; 20 | -------------------------------------------------------------------------------- /src/contexts/data.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from "solid-js"; 2 | import { Atom } from "solid-use"; 3 | import { createStore } from "solid-js/store"; 4 | import Dexie, { Table } from "dexie"; 5 | import { Job, Result } from "selas/index"; 6 | import { createDexieArrayQuery } from "solid-dexie"; 7 | 8 | export class ScryprDexie extends Dexie { 9 | results: Table; 10 | jobs: Table; 11 | 12 | constructor() { 13 | super("scrypr"); 14 | this.version(1).stores({ 15 | results: "++id, job_id, user_id, uri, blurhash, text_data, data_type, created_at", 16 | jobs: "++id, created_at, status, user_id, accepted_at, completed_at, worker_name", 17 | }); 18 | } 19 | } 20 | 21 | const DataContext = createContext<{ 22 | db: ScryprDexie; 23 | results: Result[]; 24 | jobs: Job[]; 25 | addResult: (r: Result) => Promise; 26 | addJob: (j: Job) => Promise; 27 | }>(); 28 | 29 | export function DataProvider(props) { 30 | const db = new ScryprDexie(); 31 | 32 | const results = createDexieArrayQuery(() => db.results.orderBy("created_at").toArray()); 33 | 34 | const jobs = createDexieArrayQuery(() => db.jobs.toArray()); 35 | 36 | const addResult = async (result: Result) => { 37 | const id = await db.results.add(result); 38 | }; 39 | 40 | const addJob = async (job: Job) => { 41 | const id = await db.jobs.add(job); 42 | }; 43 | 44 | return {props.children}; 45 | } 46 | 47 | export const useData = () => { 48 | return useContext(DataContext); 49 | }; 50 | -------------------------------------------------------------------------------- /src/contexts/supabase.tsx: -------------------------------------------------------------------------------- 1 | import { Component, createEffect, JSX } from "solid-js"; 2 | import { createContext } from "solid-js"; 3 | import type { AuthChangeEvent, Session, SupabaseClient, User } from "@supabase/supabase-js"; 4 | import { createRenderEffect, onCleanup, useContext } from "solid-js"; 5 | import { Atom, atom } from "solid-use"; 6 | 7 | export const SupabaseContext = createContext(); 8 | 9 | interface Props { 10 | client: SupabaseClient; 11 | children?: JSX.Element; 12 | } 13 | 14 | 15 | const ProfileContext = createContext<{secretPhrase: Atom, firstConnect: Atom, credits: Atom}>(); 16 | 17 | export function ProfileProvider(props) { 18 | const secretPhrase = atom(localStorage.getItem("openDreamSecretPhrase")) 19 | const firstConnect = atom(false); 20 | const credits = atom(0); 21 | 22 | 23 | createEffect(() => { 24 | if (secretPhrase()) { 25 | localStorage.setItem("openDreamSecretPhrase", secretPhrase()); 26 | } 27 | else { 28 | localStorage.removeItem("openDreamSecretPhrase"); 29 | } 30 | }) 31 | 32 | 33 | return ( 34 | 35 | {props.children} 36 | 37 | ) 38 | } 39 | 40 | export const useProfile = () => { return useContext(ProfileContext); } 41 | 42 | 43 | export const SupabaseProvider: Component = (props) => { 44 | return {props.children}; 45 | }; 46 | 47 | export const createSupabase = () => { 48 | const ctx = useContext(SupabaseContext); 49 | 50 | if (!ctx) throw new Error("createSupabase must be used within a SupabaseContext.Provider"); 51 | 52 | return ctx; 53 | }; 54 | 55 | export function createSupabaseAuth(): SupabaseClient["auth"] { 56 | const supabase = createSupabase(); 57 | return supabase.auth; 58 | } 59 | 60 | export function createSupabaseStorage(): SupabaseClient["storage"] { 61 | const supabase = createSupabase(); 62 | return supabase.storage; 63 | } 64 | 65 | export function createSupabaseFrom(): SupabaseClient["from"] { 66 | const supabase = createSupabase(); 67 | return supabase.from; 68 | } 69 | 70 | type AuthChangeHandler = (event: AuthChangeEvent, session: Session | null) => void; 71 | 72 | export function createOnAuthStateChange(callback: AuthChangeHandler): void { 73 | const client = createSupabase(); 74 | 75 | const { data: authListener } = client.auth.onAuthStateChange((event, session) => { 76 | callback(event, session); 77 | }); 78 | 79 | createRenderEffect(() => { 80 | client.auth.getSession().then(({ data }) => { 81 | if (data.session) callback("SIGNED_IN", data.session); 82 | }); 83 | 84 | onCleanup(() => { 85 | authListener.subscription?.unsubscribe(); 86 | }); 87 | }); 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/fonts/Dinish-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/fonts/Dinish-Italic.woff -------------------------------------------------------------------------------- /src/fonts/Dinish-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/fonts/Dinish-Italic.woff2 -------------------------------------------------------------------------------- /src/fonts/Dinish-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/fonts/Dinish-Regular.otf -------------------------------------------------------------------------------- /src/fonts/Dinish-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/fonts/Dinish-Regular.woff -------------------------------------------------------------------------------- /src/fonts/Dinish-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/fonts/Dinish-Regular.woff2 -------------------------------------------------------------------------------- /src/fonts/DinishCondensed-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/fonts/DinishCondensed-Bold.woff -------------------------------------------------------------------------------- /src/fonts/DinishCondensed-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAION-AI/opendream/14d5b436cbd918f8f5199268c2d43cd1653f4e40/src/fonts/DinishCondensed-Bold.woff2 -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | import { Router } from "solid-app-router"; 3 | import { render } from "solid-js/web"; 4 | 5 | import "@unocss/reset/tailwind.css"; 6 | import "uno.css"; 7 | 8 | import App from "./App"; 9 | 10 | render( 11 | () => ( 12 | 13 | 14 | 15 | ), 16 | document.getElementById("root") as HTMLElement 17 | ); 18 | -------------------------------------------------------------------------------- /src/pages/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nuxt.isNuxtApp": false 3 | } -------------------------------------------------------------------------------- /src/pages/hamburger.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | background-color: transparent; 3 | border: none; 4 | cursor: pointer; 5 | display: flex; 6 | padding: 0; 7 | } 8 | .line { 9 | fill: none; 10 | stroke: black; 11 | stroke-width: 6; 12 | transition: stroke-dasharray 600ms cubic-bezier(0.4, 0, 0.2, 1), stroke-dashoffset 600ms cubic-bezier(0.4, 0, 0.2, 1); 13 | } 14 | .line1 { 15 | stroke-dasharray: 60 207; 16 | stroke-width: 6; 17 | } 18 | .line2 { 19 | stroke-dasharray: 60 60; 20 | stroke-width: 6; 21 | } 22 | .line3 { 23 | stroke-dasharray: 60 207; 24 | stroke-width: 6; 25 | } 26 | .opened .line1 { 27 | stroke-dasharray: 90 207; 28 | stroke-dashoffset: -134; 29 | stroke-width: 6; 30 | } 31 | .opened .line2 { 32 | stroke-dasharray: 1 60; 33 | stroke-dashoffset: -30; 34 | stroke-width: 6; 35 | } 36 | .opened .line3 { 37 | stroke-dasharray: 90 207; 38 | stroke-dashoffset: -134; 39 | stroke-width: 6; 40 | } 41 | -------------------------------------------------------------------------------- /src/pages/landing.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Show } from "solid-js"; 2 | 3 | import "./hamburger.css"; 4 | 5 | import Logo from "../assets/logo.svg"; 6 | import laion_logo from "../assets/laion_logo.png"; 7 | import fire from "../assets/fire_emoji.gif"; 8 | import blob from "../assets/blob.png"; 9 | 10 | import { atom } from "solid-use"; 11 | import { Link } from "solid-app-router"; 12 | import DropdownAvatar from "../components/dropdown"; 13 | import { useProfile } from "../contexts/supabase"; 14 | import { AnimationImage } from "../components/animation"; 15 | import { SecretPhraseInput } from "../components/passwordInput"; 16 | 17 | const Landing: Component<{}> = (props) => { 18 | return ( 19 |
20 | 21 | 22 |
23 | ); 24 | }; 25 | 26 | const Navbar: Component<{}> = (props) => { 27 | return ( 28 |
29 |
30 | {/* 31 | //@ts-ignore */} 32 | 33 | 36 |
37 | 38 | 39 | 40 |
41 | ); 42 | }; 43 | 44 | const Hamburger: Component<{}> = (props) => { 45 | const open = atom(false); 46 | 47 | const handleClick = () => { 48 | open(!open()); 49 | }; 50 | 51 | return ( 52 | <> 53 | 69 | 70 |
71 |
72 | 73 | Sign In 74 | 75 | 76 | Sign Up 77 | 78 |
79 |
80 |
81 | 82 | ); 83 | }; 84 | 85 | const Buttons: Component<{}> = (props) => { 86 | const { secretPhrase } = useProfile(); 87 | 88 | return ( 89 |
97 | } 98 | > 99 | 104 | 105 | ); 106 | }; 107 | 108 | const Hero: Component<{}> = (props) => { 109 | const { secretPhrase } = useProfile(); 110 | 111 | return ( 112 |
113 |
114 |
115 |
116 |
117 |
Create without limits
118 | 119 |
120 |
with the Open Source community
121 |
122 |
123 | OpenDream is an image generation tool powered by artificial intelligence. Laion AI is giving this tool to 124 | the public for free in exchange for annotations. 125 |
126 | 127 |
128 | 129 | Start right now 130 | 131 |
132 |
133 |
134 |
135 | 136 | 137 |
138 |
139 |
140 | ); 141 | }; 142 | 143 | export default Landing; 144 | -------------------------------------------------------------------------------- /src/pages/login.tsx: -------------------------------------------------------------------------------- 1 | import { Link, useNavigate } from "solid-app-router"; 2 | import { Component } from "solid-js"; 3 | import { atom } from "solid-use"; 4 | 5 | import Logo from "../assets/logo.svg"; 6 | 7 | import PasswordInput from "../components/passwordInput"; 8 | import { createSupabase, useProfile } from "../contexts/supabase"; 9 | 10 | const Login: Component<{}> = (props) => { 11 | const supabase = createSupabase(); 12 | const password = atom(""); 13 | const navigate = useNavigate(); 14 | const { secretPhrase, firstConnect, credits } = useProfile(); 15 | 16 | const handleSignin = async (e: Event) => { 17 | e.preventDefault(); 18 | 19 | const { data, error } = await supabase.rpc("check_credits", {phrase_arg: password()}); 20 | 21 | if (error) { 22 | alert(error.message); 23 | } 24 | else if (data===null) { 25 | alert("Invalid secret phrase"); 26 | } 27 | else { 28 | secretPhrase(password()); 29 | credits(data); 30 | navigate("/space/create"); 31 | } 32 | 33 | }; 34 | 35 | const handleRegister = async (e: Event) => { 36 | e.preventDefault(); 37 | // const { data, error } = await supabase.from("users").insert({}).select(); 38 | const { data, error } = await supabase.rpc("create_user"); 39 | 40 | 41 | if (error) { 42 | alert(error.message); 43 | } else { 44 | secretPhrase(data); 45 | firstConnect(true); 46 | navigate("/space/create"); 47 | } 48 | }; 49 | 50 | return ( 51 |
52 |
53 | 54 | {/* 55 | // @ts-ignore */} 56 | 57 | 58 |

Login to OpenDream

59 |
60 | Now begins your amazing creative journey with AI. Don't yet have an account?{" "} 61 | 62 | {" "} 63 | Register now 64 | {" "} 65 | and get free credits! 66 |
67 |
68 |
72 | 73 |
74 |
75 | 76 | 77 |
78 |
79 | 82 | 83 |
84 | ); 85 | }; 86 | 87 | export default Login; 88 | -------------------------------------------------------------------------------- /src/pages/recovery.tsx: -------------------------------------------------------------------------------- 1 | import { Component } from "solid-js"; 2 | 3 | 4 | const Recovery: Component<{}> = (props) => { 5 | 6 | return
; 7 | }; 8 | 9 | export default Recovery; -------------------------------------------------------------------------------- /src/pages/space/annotation.tsx: -------------------------------------------------------------------------------- 1 | import { Component, For, Show } from "solid-js"; 2 | import { atom, Atom } from "solid-use"; 3 | 4 | const Annotation: Component<{}> = (props) => { 5 | const currentRating = atom(null); 6 | const showHistory = atom(false); 7 | 8 | return ( 9 |
10 |
11 |

Annotation

12 |

13 | Please annotate the aesthetic rating of the image below on a scale from 1 to 10, where 1 is the least 14 | aesthetic and 10 is the most aesthetic. 15 |

16 |
17 | {/* History button */} 18 |
showHistory(!showHistory())}> 19 |
24 |
25 | 26 |
27 |

History

28 |
29 | 30 | {(item, index) => ( 31 |
{showHistory(false); currentRating(5)}}> 32 | 36 |
37 |

5

38 |
39 |
40 | )} 41 |
42 |
43 |
44 |
45 | 46 | 47 |
48 |
49 | 50 | {(item) => } 51 | 52 |
53 | 54 |
55 | {/* */} 56 |
57 | ); 58 | }; 59 | 60 | const HistoricBar: Component<{}> = (props) => { 61 | return ( 62 |
63 |
64 |
65 |
66 |
67 |

Annotation History

68 |
69 |
70 | ); 71 | }; 72 | 73 | const RatingButton: Component<{ number: number; currentRating: Atom }> = (props) => { 74 | return ( 75 | 85 | ); 86 | }; 87 | 88 | export default Annotation; 89 | -------------------------------------------------------------------------------- /src/pages/space/create.tsx: -------------------------------------------------------------------------------- 1 | import { Component, createEffect, For, onMount, Show, Suspense } from "solid-js"; 2 | import { createSelasClient, Job, Result, TextPrompt } from "selas"; 3 | import { Atom, atom } from "solid-use"; 4 | import { useData } from "../../contexts/data"; 5 | import { createMemo } from "solid-js"; 6 | import { createDexieArrayQuery } from "solid-dexie"; 7 | import { DND } from "../../components/options"; 8 | import { useConfig } from "../../contexts/config"; 9 | 10 | const Create: Component<{}> = (props) => { 11 | const selas = createSelasClient(); 12 | const { jobs, results, addJob, addResult } = useData(); 13 | 14 | return ( 15 |
16 |
17 | 18 | 19 |
20 | 21 |
22 | ); 23 | }; 24 | 25 | const SimplePrompt: Component<{ prompt: Atom }> = (props) => { 26 | const { diffusionConfig } = useConfig(); 27 | const { jobs, results, addJob, addResult } = useData(); 28 | const selas = createSelasClient(); 29 | const prompt = atom("A cute calico cat in the grass on a beautiful day, artstation, storybook art"); 30 | 31 | const handleNewResult = async (payload) => { 32 | const result: Result = payload.new; 33 | await addResult(result); 34 | }; 35 | 36 | onMount(async () => { 37 | const selasEmail = import.meta.env.VITE_SELAS_EMAIL; 38 | const selasPassword = import.meta.env.VITE_SELAS_PASSWORD; 39 | await selas.signIn(selasEmail, selasPassword); 40 | }); 41 | 42 | 43 | 44 | const submitJob = async (e: Event) => { 45 | e.preventDefault(); 46 | const {diffusionConfig} = useConfig(); 47 | 48 | const postJob = async () => { 49 | const currentConfig = diffusionConfig(); 50 | 51 | const { data: job } = await selas.postJob({diffusion: currentConfig}); 52 | 53 | await addJob(job); 54 | await selas.subscribeToResults(job.id, handleNewResult); 55 | }; 56 | 57 | for (let i = 0; i < parseInt("1"); i++) postJob(); 58 | }; 59 | 60 | return ( 61 |
62 |
63 |

64 | Write a detailed description of the image you want 65 |

66 |
67 |