Duplicate from huggingface-projects/color-palette-generator-sd
Browse filesCo-authored-by: Radamés Ajna <[email protected]>
This view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +31 -0
- .gitignore +19 -0
- Makefile +11 -0
- README.md +14 -0
- app.py +163 -0
- extract.py +31 -0
- frontend/.env.example +2 -0
- frontend/.eslintignore +13 -0
- frontend/.eslintrc.cjs +20 -0
- frontend/.gitignore +12 -0
- frontend/.npmrc +1 -0
- frontend/.prettierignore +13 -0
- frontend/.prettierrc +8 -0
- frontend/README.md +38 -0
- frontend/package.json +45 -0
- frontend/postcss.config.cjs +6 -0
- frontend/src/app.css +3 -0
- frontend/src/app.d.ts +15 -0
- frontend/src/app.html +13 -0
- frontend/src/lib/ArrowLeft.svelte +13 -0
- frontend/src/lib/ArrowRight.svelte +13 -0
- frontend/src/lib/ColorPalette.svelte +50 -0
- frontend/src/lib/Palette.svelte +92 -0
- frontend/src/lib/store.ts +3 -0
- frontend/src/lib/types.ts +15 -0
- frontend/src/lib/utils.ts +118 -0
- frontend/src/routes/+layout.svelte +5 -0
- frontend/src/routes/+layout.ts +1 -0
- frontend/src/routes/+page.svelte +307 -0
- frontend/static/favicon.png +0 -0
- frontend/static/robots.txt +3 -0
- frontend/static/svelte-welcome.png +0 -0
- frontend/static/svelte-welcome.webp +0 -0
- frontend/svelte.config.js +26 -0
- frontend/tailwind.config.cjs +6 -0
- frontend/tsconfig.json +17 -0
- frontend/vite.config.dev.ts +18 -0
- frontend/vite.config.ts +8 -0
- requirements.txt +8 -0
- run.py +3 -0
- static/_app/immutable/assets/_layout-e397cbf6.css +1 -0
- static/_app/immutable/assets/_page-fd1176fc.css +1 -0
- static/_app/immutable/chunks/0-17a4fec1.js +1 -0
- static/_app/immutable/chunks/1-26e94021.js +1 -0
- static/_app/immutable/chunks/2-fc64cbc2.js +1 -0
- static/_app/immutable/chunks/_layout-1daba58d.js +1 -0
- static/_app/immutable/chunks/index-3bda1050.js +1 -0
- static/_app/immutable/chunks/index-5559954d.js +1 -0
- static/_app/immutable/chunks/singletons-e4c31a41.js +1 -0
- static/_app/immutable/components/error.svelte-c31c95c4.js +1 -0
.gitattributes
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
23 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
26 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
|
10 |
+
# Ignore files for PNPM, NPM and YARN
|
11 |
+
pnpm-lock.yaml
|
12 |
+
package-lock.json
|
13 |
+
yarn.lock
|
14 |
+
venv/
|
15 |
+
__pycache__/
|
16 |
+
flagged/
|
17 |
+
data
|
18 |
+
data.db
|
19 |
+
data.json
|
Makefile
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
build-client:
|
2 |
+
cd frontend && npm install && PUBLIC_DEV_MODE=PROD npm run build && rm -rf ../static && cp -r build/ ../static/
|
3 |
+
build-dev:
|
4 |
+
cd frontend && npm install && PUBLIC_DEV_MODE=DEV npm run build-dev && rm -rf ../static && cp -r build/ ../static/
|
5 |
+
run-front-dev:
|
6 |
+
cd frontend && npm install && PUBLIC_DEV_MODE=DEV npm run dev
|
7 |
+
run-dev:
|
8 |
+
rm -rf .data/ && FLASK_DEBUG=development python app.py
|
9 |
+
run-prod:
|
10 |
+
python app.py
|
11 |
+
build-all: run-prod
|
README.md
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Sd Color Palette Generator
|
3 |
+
emoji: 📖🎨⚙️
|
4 |
+
colorFrom: yellow
|
5 |
+
colorTo: gray
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 3.3
|
8 |
+
app_file: run.py
|
9 |
+
fullWidth: true
|
10 |
+
pinned: false
|
11 |
+
duplicated_from: huggingface-projects/color-palette-generator-sd
|
12 |
+
---
|
13 |
+
|
14 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from pathlib import Path
|
3 |
+
import json
|
4 |
+
from flask import Flask, request, jsonify, g
|
5 |
+
from flask_expects_json import expects_json
|
6 |
+
from flask_cors import CORS
|
7 |
+
from PIL import Image
|
8 |
+
from huggingface_hub import Repository
|
9 |
+
from flask_apscheduler import APScheduler
|
10 |
+
import shutil
|
11 |
+
import sqlite3
|
12 |
+
import subprocess
|
13 |
+
from jsonschema import ValidationError
|
14 |
+
|
15 |
+
MODE = os.environ.get('FLASK_ENV', 'production')
|
16 |
+
IS_DEV = MODE == 'development'
|
17 |
+
app = Flask(__name__, static_url_path='/static')
|
18 |
+
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
|
19 |
+
|
20 |
+
schema = {
|
21 |
+
"type": "object",
|
22 |
+
"properties": {
|
23 |
+
"prompt": {"type": "string"},
|
24 |
+
"images": {
|
25 |
+
"type": "array",
|
26 |
+
"items": {
|
27 |
+
"type": "object",
|
28 |
+
"minProperties": 2,
|
29 |
+
"maxProperties": 2,
|
30 |
+
"properties": {
|
31 |
+
"colors": {
|
32 |
+
"type": "array",
|
33 |
+
"items": {
|
34 |
+
"type": "string"
|
35 |
+
},
|
36 |
+
"maxItems": 5,
|
37 |
+
"minItems": 5
|
38 |
+
},
|
39 |
+
"imgURL": {"type": "string"}}
|
40 |
+
}
|
41 |
+
}
|
42 |
+
},
|
43 |
+
"minProperties": 2,
|
44 |
+
"maxProperties": 2
|
45 |
+
}
|
46 |
+
|
47 |
+
CORS(app)
|
48 |
+
|
49 |
+
DB_FILE = Path("./data.db")
|
50 |
+
TOKEN = os.environ.get('HUGGING_FACE_HUB_TOKEN')
|
51 |
+
repo = Repository(
|
52 |
+
local_dir="data",
|
53 |
+
repo_type="dataset",
|
54 |
+
clone_from="huggingface-projects/color-palettes-sd",
|
55 |
+
use_auth_token=TOKEN
|
56 |
+
)
|
57 |
+
repo.git_pull()
|
58 |
+
# copy db on db to local path
|
59 |
+
shutil.copyfile("./data/data.db", DB_FILE)
|
60 |
+
|
61 |
+
db = sqlite3.connect(DB_FILE)
|
62 |
+
try:
|
63 |
+
data = db.execute("SELECT * FROM palettes").fetchall()
|
64 |
+
if IS_DEV:
|
65 |
+
print(f"Loaded {len(data)} palettes from local db")
|
66 |
+
db.close()
|
67 |
+
except sqlite3.OperationalError:
|
68 |
+
db.execute(
|
69 |
+
'CREATE TABLE palettes (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, data json, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL)')
|
70 |
+
db.commit()
|
71 |
+
|
72 |
+
|
73 |
+
def get_db():
|
74 |
+
db = getattr(g, '_database', None)
|
75 |
+
if db is None:
|
76 |
+
db = g._database = sqlite3.connect(DB_FILE)
|
77 |
+
db.row_factory = sqlite3.Row
|
78 |
+
return db
|
79 |
+
|
80 |
+
|
81 |
+
@app.teardown_appcontext
|
82 |
+
def close_connection(exception):
|
83 |
+
db = getattr(g, '_database', None)
|
84 |
+
if db is not None:
|
85 |
+
db.close()
|
86 |
+
|
87 |
+
|
88 |
+
def update_repository():
|
89 |
+
repo.git_pull()
|
90 |
+
# copy db on db to local path
|
91 |
+
shutil.copyfile(DB_FILE, "./data/data.db")
|
92 |
+
|
93 |
+
with sqlite3.connect("./data/data.db") as db:
|
94 |
+
db.row_factory = sqlite3.Row
|
95 |
+
palettes = db.execute("SELECT * FROM palettes").fetchall()
|
96 |
+
data = [{'id': row['id'], 'data': json.loads(
|
97 |
+
row['data']), 'created_at': row['created_at']} for row in palettes]
|
98 |
+
|
99 |
+
with open('./data/data.json', 'w') as f:
|
100 |
+
json.dump(data, f, separators=(',', ':'))
|
101 |
+
|
102 |
+
print("Updating repository")
|
103 |
+
subprocess.Popen(
|
104 |
+
"git add . && git commit --amend -m 'update' && git push --force", cwd="./data", shell=True)
|
105 |
+
repo.push_to_hub(blocking=False)
|
106 |
+
|
107 |
+
|
108 |
+
@app.route('/')
|
109 |
+
def index():
|
110 |
+
return app.send_static_file('index.html')
|
111 |
+
|
112 |
+
|
113 |
+
@app.route('/force_push')
|
114 |
+
def push():
|
115 |
+
if (request.headers['token'] == TOKEN):
|
116 |
+
update_repository()
|
117 |
+
return jsonify({'success': True})
|
118 |
+
else:
|
119 |
+
return "Error", 401
|
120 |
+
|
121 |
+
|
122 |
+
def getAllData():
|
123 |
+
palettes = get_db().execute("SELECT * FROM palettes").fetchall()
|
124 |
+
data = [{'id': row['id'], 'data': json.loads(
|
125 |
+
row['data']), 'created_at': row['created_at']} for row in palettes]
|
126 |
+
return data
|
127 |
+
|
128 |
+
|
129 |
+
@app.route('/data')
|
130 |
+
def getdata():
|
131 |
+
return jsonify(getAllData())
|
132 |
+
|
133 |
+
|
134 |
+
@app.route('/new_palette', methods=['POST'])
|
135 |
+
@expects_json(schema)
|
136 |
+
def create():
|
137 |
+
data = g.data
|
138 |
+
db = get_db()
|
139 |
+
cursor = db.cursor()
|
140 |
+
cursor.execute("INSERT INTO palettes(data) VALUES (?)", [json.dumps(data)])
|
141 |
+
db.commit()
|
142 |
+
return jsonify(getAllData())
|
143 |
+
|
144 |
+
|
145 |
+
@app.errorhandler(400)
|
146 |
+
def bad_request(error):
|
147 |
+
if isinstance(error.description, ValidationError):
|
148 |
+
original_error = error.description
|
149 |
+
return jsonify({'error': original_error.message}), 400
|
150 |
+
return error
|
151 |
+
|
152 |
+
|
153 |
+
if __name__ == '__main__':
|
154 |
+
if not IS_DEV:
|
155 |
+
print("Starting scheduler -- Running Production")
|
156 |
+
scheduler = APScheduler()
|
157 |
+
scheduler.add_job(id='Update Dataset Repository',
|
158 |
+
func=update_repository, trigger='interval', hours=1)
|
159 |
+
scheduler.start()
|
160 |
+
else:
|
161 |
+
print("Not Starting scheduler -- Running Development")
|
162 |
+
app.run(host='0.0.0.0', port=int(
|
163 |
+
os.environ.get('PORT', 7860)), debug=True, use_reloader=IS_DEV)
|
extract.py
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from colorthief import ColorThief
|
2 |
+
from pathlib import Path
|
3 |
+
import json
|
4 |
+
from PIL import Image
|
5 |
+
|
6 |
+
|
7 |
+
images_path = Path('frontend/static/images')
|
8 |
+
images = images_path.glob("*.[jpeg jpg png]*")
|
9 |
+
print(images)
|
10 |
+
data = {}
|
11 |
+
for image in images:
|
12 |
+
print(image.stem)
|
13 |
+
image_pil = Image.open(image)
|
14 |
+
color_thief = ColorThief(image)
|
15 |
+
image_pil.save(Path.joinpath(images_path, (image.stem + ".jpg")), optimize=True, quality=95)
|
16 |
+
prompt = image.stem.split("-")[2]
|
17 |
+
try:
|
18 |
+
type(data[prompt]) == list
|
19 |
+
except:
|
20 |
+
data[prompt] = []
|
21 |
+
|
22 |
+
colors = color_thief.get_palette(color_count=5, quality=1)
|
23 |
+
colors_hex = ['#%02x%02x%02x' % (color) for color in colors]
|
24 |
+
data[prompt].append({
|
25 |
+
"colors": colors_hex,
|
26 |
+
"imgURL": "static/images/" + image.stem + ".jpg"
|
27 |
+
})
|
28 |
+
prompts = [{"prompt": prompt, "images": values}
|
29 |
+
for (prompt, values) in data.items()]
|
30 |
+
with open('frontend/static/data.json', 'w') as f:
|
31 |
+
json.dump(prompts, f)
|
frontend/.env.example
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
PUBLIC_WS_ENDPOINT="wss://spaces.huggingface.tech/stabilityai/stable-diffusion-1/queue/join"
|
2 |
+
PUBLIC_API=""
|
frontend/.eslintignore
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
|
10 |
+
# Ignore files for PNPM, NPM and YARN
|
11 |
+
pnpm-lock.yaml
|
12 |
+
package-lock.json
|
13 |
+
yarn.lock
|
frontend/.eslintrc.cjs
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module.exports = {
|
2 |
+
root: true,
|
3 |
+
parser: '@typescript-eslint/parser',
|
4 |
+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
5 |
+
plugins: ['svelte3', '@typescript-eslint'],
|
6 |
+
ignorePatterns: ['*.cjs'],
|
7 |
+
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
8 |
+
settings: {
|
9 |
+
'svelte3/typescript': () => require('typescript')
|
10 |
+
},
|
11 |
+
parserOptions: {
|
12 |
+
sourceType: 'module',
|
13 |
+
ecmaVersion: 2020
|
14 |
+
},
|
15 |
+
env: {
|
16 |
+
browser: true,
|
17 |
+
es2017: true,
|
18 |
+
node: true
|
19 |
+
}
|
20 |
+
};
|
frontend/.gitignore
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
.vercel
|
10 |
+
.output
|
11 |
+
venv/
|
12 |
+
__pycache__/
|
frontend/.npmrc
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
engine-strict=true
|
frontend/.prettierignore
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
|
10 |
+
# Ignore files for PNPM, NPM and YARN
|
11 |
+
pnpm-lock.yaml
|
12 |
+
package-lock.json
|
13 |
+
yarn.lock
|
frontend/.prettierrc
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"useTabs": true,
|
3 |
+
"singleQuote": true,
|
4 |
+
"trailingComma": "none",
|
5 |
+
"printWidth": 100,
|
6 |
+
"pluginSearchDirs": ["."],
|
7 |
+
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
8 |
+
}
|
frontend/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# create-svelte
|
2 |
+
|
3 |
+
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
4 |
+
|
5 |
+
## Creating a project
|
6 |
+
|
7 |
+
If you're seeing this, you've probably already done this step. Congrats!
|
8 |
+
|
9 |
+
```bash
|
10 |
+
# create a new project in the current directory
|
11 |
+
npm create svelte@latest
|
12 |
+
|
13 |
+
# create a new project in my-app
|
14 |
+
npm create svelte@latest my-app
|
15 |
+
```
|
16 |
+
|
17 |
+
## Developing
|
18 |
+
|
19 |
+
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
20 |
+
|
21 |
+
```bash
|
22 |
+
npm run dev
|
23 |
+
|
24 |
+
# or start the server and open the app in a new browser tab
|
25 |
+
npm run dev -- --open
|
26 |
+
```
|
27 |
+
|
28 |
+
## Building
|
29 |
+
|
30 |
+
To create a production version of your app:
|
31 |
+
|
32 |
+
```bash
|
33 |
+
npm run build
|
34 |
+
```
|
35 |
+
|
36 |
+
You can preview the production build with `npm run preview`.
|
37 |
+
|
38 |
+
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
frontend/package.json
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "frontend",
|
3 |
+
"version": "0.0.1",
|
4 |
+
"scripts": {
|
5 |
+
"dev": "NODE_ENV=development vite --config vite.config.dev.ts dev",
|
6 |
+
"build-dev": "vite build --mode development",
|
7 |
+
"build": "vite build",
|
8 |
+
"preview": "vite preview",
|
9 |
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
10 |
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
11 |
+
"lint": "prettier --check . && eslint .",
|
12 |
+
"format": "prettier --write ."
|
13 |
+
},
|
14 |
+
"devDependencies": {
|
15 |
+
"@sveltejs/adapter-static": "^1.0.0-next.43",
|
16 |
+
"@sveltejs/kit": "next",
|
17 |
+
"@tailwindcss/forms": "^0.5.3",
|
18 |
+
"@tailwindcss/line-clamp": "^0.4.2",
|
19 |
+
"@types/cookie": "^0.5.1",
|
20 |
+
"@types/d3-color": "^3.1.0",
|
21 |
+
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
22 |
+
"@typescript-eslint/parser": "^5.27.0",
|
23 |
+
"autoprefixer": "^10.4.11",
|
24 |
+
"d3-color": "^3.1.0",
|
25 |
+
"eslint": "^8.16.0",
|
26 |
+
"eslint-config-prettier": "^8.3.0",
|
27 |
+
"eslint-plugin-svelte3": "^4.0.0",
|
28 |
+
"postcss": "^8.4.16",
|
29 |
+
"prettier": "^2.6.2",
|
30 |
+
"prettier-plugin-svelte": "^2.7.0",
|
31 |
+
"svelte": "^3.46.0",
|
32 |
+
"svelte-check": "^2.7.1",
|
33 |
+
"svelte-preprocess": "^4.10.7",
|
34 |
+
"tailwindcss": "^3.1.8",
|
35 |
+
"tslib": "^2.3.1",
|
36 |
+
"typescript": "^4.7.4",
|
37 |
+
"vite": "^3.1.0"
|
38 |
+
},
|
39 |
+
"type": "module",
|
40 |
+
"dependencies": {
|
41 |
+
"@fontsource/fira-mono": "^4.5.0",
|
42 |
+
"quantize": "^1.0.2",
|
43 |
+
"scroll-into-view-if-needed": "^2.2.29"
|
44 |
+
}
|
45 |
+
}
|
frontend/postcss.config.cjs
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module.exports = {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
frontend/src/app.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
frontend/src/app.d.ts
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// See https://kit.svelte.dev/docs/types#app
|
2 |
+
// for information about these interfaces
|
3 |
+
// and what to do when importing types
|
4 |
+
|
5 |
+
declare global {
|
6 |
+
namespace App {
|
7 |
+
// interface Locals {}
|
8 |
+
// interface PageData {}
|
9 |
+
// interface PageError {}
|
10 |
+
// interface Platform {}
|
11 |
+
interface Window {
|
12 |
+
parentIFrame: unknown;
|
13 |
+
}
|
14 |
+
}
|
15 |
+
}
|
frontend/src/app.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="utf-8" />
|
5 |
+
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
6 |
+
<meta name="viewport" content="width=device-width" />
|
7 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script>
|
8 |
+
%sveltekit.head%
|
9 |
+
</head>
|
10 |
+
<body class="dark:bg-[rgb(11,15,25)] bg-white dark:text-white text-black">
|
11 |
+
<div>%sveltekit.body%</div>
|
12 |
+
</body>
|
13 |
+
</html>
|
frontend/src/lib/ArrowLeft.svelte
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg
|
2 |
+
class="mr-1.5"
|
3 |
+
xmlns="http://www.w3.org/2000/svg"
|
4 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
5 |
+
aria-hidden="true"
|
6 |
+
focusable="false"
|
7 |
+
role="img"
|
8 |
+
width="1em"
|
9 |
+
height="1em"
|
10 |
+
preserveAspectRatio="xMidYMid meet"
|
11 |
+
viewBox="0 0 32 32"
|
12 |
+
><path d="M10 16L20 6l1.4 1.4l-8.6 8.6l8.6 8.6L20 26z" fill="currentColor" /></svg
|
13 |
+
>
|
frontend/src/lib/ArrowRight.svelte
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg
|
2 |
+
class="ml-1.5 transform rotate-180"
|
3 |
+
xmlns="http://www.w3.org/2000/svg"
|
4 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
5 |
+
aria-hidden="true"
|
6 |
+
focusable="false"
|
7 |
+
role="img"
|
8 |
+
width="1em"
|
9 |
+
height="1em"
|
10 |
+
preserveAspectRatio="xMidYMid meet"
|
11 |
+
viewBox="0 0 32 32"
|
12 |
+
><path d="M10 16L20 6l1.4 1.4l-8.6 8.6l8.6 8.6L20 26z" fill="currentColor" /></svg
|
13 |
+
>
|
frontend/src/lib/ColorPalette.svelte
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import type { Color } from 'd3-color';
|
3 |
+
import * as d3 from 'd3-color';
|
4 |
+
|
5 |
+
export let colors: Color[];
|
6 |
+
const n = colors.length;
|
7 |
+
function getLabelColor(c: Color): string {
|
8 |
+
const color = d3.hcl(c);
|
9 |
+
if (color.l > 50) {
|
10 |
+
return d3.hcl(color.h, color.c, 0).formatHex();
|
11 |
+
}
|
12 |
+
return d3.hcl(color.h, color.c, 100).formatHex();
|
13 |
+
}
|
14 |
+
let isCopying = -1;
|
15 |
+
|
16 |
+
async function copyStringToClipboard(text: string, n: number) {
|
17 |
+
// usingo Clipboard API
|
18 |
+
if (isCopying > -1) return;
|
19 |
+
isCopying = n;
|
20 |
+
// await navigator.permissions.query({ name: 'clipboard-write' });
|
21 |
+
await navigator.clipboard.write([
|
22 |
+
new ClipboardItem({ 'text/plain': new Blob([text], { type: 'text/plain' }) })
|
23 |
+
]);
|
24 |
+
setTimeout(() => {
|
25 |
+
isCopying = -1;
|
26 |
+
}, 800);
|
27 |
+
}
|
28 |
+
</script>
|
29 |
+
|
30 |
+
<div class="flex flex-col items-center">
|
31 |
+
<div class="flex">
|
32 |
+
{#each colors as color, i}
|
33 |
+
<div
|
34 |
+
class="{isCopying === i ? '' : 'cursor-pointer'} aspect-square relative"
|
35 |
+
style="background-color: {color.formatHex()}"
|
36 |
+
on:click={() => copyStringToClipboard(color.formatHex(), i)}
|
37 |
+
>
|
38 |
+
<svg class="block max-w-full aspect-square" width="100" viewBox="0 0 50 50">
|
39 |
+
<rect x="0" y="0" width="50" height="50" fill={color.formatHex()} />
|
40 |
+
</svg>
|
41 |
+
<span
|
42 |
+
title="Copy single color"
|
43 |
+
class="absolute bottom-0 text-center text-xs pl-1 font-bold uppercase"
|
44 |
+
style="color:{getLabelColor(color)}"
|
45 |
+
>{isCopying === i ? 'copied' : color.formatHex()}</span
|
46 |
+
>
|
47 |
+
</div>
|
48 |
+
{/each}
|
49 |
+
</div>
|
50 |
+
</div>
|
frontend/src/lib/Palette.svelte
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import * as d3 from 'd3-color';
|
3 |
+
import { createEventDispatcher } from 'svelte';
|
4 |
+
import type { ColorsPrompt } from '$lib/types';
|
5 |
+
import ColorPalette from '$lib/ColorPalette.svelte';
|
6 |
+
|
7 |
+
const dispatch = createEventDispatcher();
|
8 |
+
|
9 |
+
export let promptData: ColorsPrompt;
|
10 |
+
|
11 |
+
let seletecdImage = 0;
|
12 |
+
|
13 |
+
$: prompt = promptData?.prompt;
|
14 |
+
$: colors = promptData?.images[seletecdImage]?.colors.map((e) => d3.rgb(e)) || [];
|
15 |
+
$: imageSrc = promptData?.images[seletecdImage]?.imgURL;
|
16 |
+
let isCopying = false;
|
17 |
+
|
18 |
+
async function copyStringToClipboard(text: string) {
|
19 |
+
// usingo Clipboard API
|
20 |
+
if (isCopying) return;
|
21 |
+
isCopying = true;
|
22 |
+
// await navigator.permissions.query({ name: 'clipboard-write' });
|
23 |
+
await navigator.clipboard.write([
|
24 |
+
new ClipboardItem({ 'text/plain': new Blob([text], { type: 'text/plain' }) })
|
25 |
+
]);
|
26 |
+
setTimeout(() => {
|
27 |
+
isCopying = false;
|
28 |
+
}, 1000);
|
29 |
+
}
|
30 |
+
</script>
|
31 |
+
|
32 |
+
<div class="grid grid-cols-6 gap-3">
|
33 |
+
<blockquote
|
34 |
+
class="row-start-1 mx-auto col-span-6 italic font-semibold max-w-prose text-base text-center line-clamp-3 my-3"
|
35 |
+
title={prompt}
|
36 |
+
>
|
37 |
+
<p>{prompt}</p>
|
38 |
+
</blockquote>
|
39 |
+
<div class="row-start-3 md:row-start-2 col-span-6 md:col-span-4 flex items-center justify-center">
|
40 |
+
{#if colors}
|
41 |
+
<ColorPalette {colors} />
|
42 |
+
{/if}
|
43 |
+
</div>
|
44 |
+
<div class="row-start-2 col-span-6 md:col-span-2 flex justify-center md:justify-end pb-3">
|
45 |
+
<div class="relative">
|
46 |
+
<img
|
47 |
+
loading="lazy"
|
48 |
+
class="relative max-w-[100px] w-full aspect-square"
|
49 |
+
src={imageSrc}
|
50 |
+
alt={prompt}
|
51 |
+
/>
|
52 |
+
<div class="absolute flex justify-around w-full">
|
53 |
+
{#each promptData.images as image, i}
|
54 |
+
<button
|
55 |
+
class="{seletecdImage === i
|
56 |
+
? 'bg-black dark:bg-white'
|
57 |
+
: 'bg-white dark:bg-black'} dark:bg-slate-300 rounded-full h-3 w-3 m-1 border border-black dark:border-white"
|
58 |
+
on:click={() => (seletecdImage = i)}
|
59 |
+
on:mousemove={() => (seletecdImage = i)}
|
60 |
+
/>
|
61 |
+
{/each}
|
62 |
+
</div>
|
63 |
+
</div>
|
64 |
+
</div>
|
65 |
+
<div
|
66 |
+
class="row-start-4 col-span-6 md:col-span-2 md:col-start-5 flex justify-center md:justify-end"
|
67 |
+
>
|
68 |
+
<div class="flex justify-center items-center">
|
69 |
+
<button
|
70 |
+
class="button"
|
71 |
+
title="Send this prompt to input so you can remix it"
|
72 |
+
on:click={() => dispatch('remix', { prompt })}
|
73 |
+
>
|
74 |
+
Remix
|
75 |
+
</button>
|
76 |
+
<button
|
77 |
+
class="button"
|
78 |
+
title="Copy all colors to clipboard"
|
79 |
+
disabled={isCopying}
|
80 |
+
on:click={() => copyStringToClipboard(colors.map((color) => color.formatHex()).join(', '))}
|
81 |
+
>
|
82 |
+
{isCopying ? 'Copied' : 'Copy'}
|
83 |
+
</button>
|
84 |
+
</div>
|
85 |
+
</div>
|
86 |
+
</div>
|
87 |
+
|
88 |
+
<style lang="postcss" scoped>
|
89 |
+
.button {
|
90 |
+
@apply min-w-[9ch] bg-black text-white border-2 dark:border-white border-black rounded-2xl ml-2 px-2 py-2 shadow-sm text-xs font-bold focus:outline-none focus:border-gray-400;
|
91 |
+
}
|
92 |
+
</style>
|
frontend/src/lib/store.ts
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
import { writable } from 'svelte/store';
|
2 |
+
export const loadingState = writable<string>('');
|
3 |
+
export const isLoading = writable<boolean>(false);
|
frontend/src/lib/types.ts
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import type { Color } from 'd3-color';
|
2 |
+
|
3 |
+
export interface ColorsImage {
|
4 |
+
colors: Color[];
|
5 |
+
imgURL: string;
|
6 |
+
}
|
7 |
+
export interface ColorsPrompt {
|
8 |
+
prompt: string;
|
9 |
+
images: ColorsImage[];
|
10 |
+
}
|
11 |
+
|
12 |
+
export interface LoadingState {
|
13 |
+
loadingMessage: string;
|
14 |
+
isLoading: boolean;
|
15 |
+
}
|
frontend/src/lib/utils.ts
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import quantize from 'quantize';
|
2 |
+
import * as d3 from 'd3-color';
|
3 |
+
import type { Color } from 'd3-color';
|
4 |
+
import { dev } from '$app/environment';
|
5 |
+
|
6 |
+
export function randomSeed() {
|
7 |
+
return BigInt(13248873089935215612 & (((1 << 63) - 1) * Math.random()));
|
8 |
+
}
|
9 |
+
|
10 |
+
function sortColors(colors: Color[]): Color[] {
|
11 |
+
const reverse = true;
|
12 |
+
return colors
|
13 |
+
.map((color) => d3.hcl(color))
|
14 |
+
.sort((a, b) => {
|
15 |
+
const aa = a.h;
|
16 |
+
const bb = b.h;
|
17 |
+
|
18 |
+
return !reverse ? aa - bb || isNaN(aa) - isNaN(bb) : bb - aa || isNaN(bb) - isNaN(aa);
|
19 |
+
});
|
20 |
+
}
|
21 |
+
|
22 |
+
function createPixelArray(imgData: Uint8ClampedArray, pixelCount: number, quality: number) {
|
23 |
+
// from https://github.com/lokesh/color-thief
|
24 |
+
const pixels = imgData;
|
25 |
+
const pixelArray = [];
|
26 |
+
|
27 |
+
for (let i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
|
28 |
+
offset = i * 4;
|
29 |
+
r = pixels[offset + 0];
|
30 |
+
g = pixels[offset + 1];
|
31 |
+
b = pixels[offset + 2];
|
32 |
+
a = pixels[offset + 3];
|
33 |
+
|
34 |
+
// If pixel is mostly opaque and not white
|
35 |
+
if (typeof a === 'undefined' || a >= 125) {
|
36 |
+
if (!(r > 250 && g > 250 && b > 250)) {
|
37 |
+
pixelArray.push([r, g, b]);
|
38 |
+
}
|
39 |
+
}
|
40 |
+
}
|
41 |
+
return pixelArray;
|
42 |
+
}
|
43 |
+
|
44 |
+
export function extractPalette(
|
45 |
+
base64image: string,
|
46 |
+
colorCount = 5,
|
47 |
+
quality = 1
|
48 |
+
): Promise<{ colors: Color[]; imgBlob: Blob }> {
|
49 |
+
return new Promise((resolve) => {
|
50 |
+
const img = new Image();
|
51 |
+
img.onload = async () => {
|
52 |
+
const w = img.width;
|
53 |
+
const h = img.height;
|
54 |
+
const canvas = document.createElement('canvas');
|
55 |
+
canvas.width = w;
|
56 |
+
canvas.height = h;
|
57 |
+
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
58 |
+
ctx.drawImage(img, 0, 0, w, h);
|
59 |
+
const imageData = ctx.getImageData(0, 0, w, h);
|
60 |
+
const pixelArray = createPixelArray(imageData.data, w * h, quality);
|
61 |
+
const cmap = quantize(pixelArray, colorCount);
|
62 |
+
const colors: number[][] = cmap.palette();
|
63 |
+
const tempCanvas = document.createElement('canvas');
|
64 |
+
tempCanvas.width = w / 5;
|
65 |
+
tempCanvas.height = h / 5;
|
66 |
+
const tempCtx = tempCanvas.getContext('2d') as CanvasRenderingContext2D;
|
67 |
+
tempCtx.drawImage(img, 0, 0, w, h, 0, 0, w / 5, h / 5);
|
68 |
+
|
69 |
+
const imgBlob: Blob = await new Promise((_resolve) =>
|
70 |
+
tempCanvas.toBlob(_resolve, 'image/jpeg', 0.8)
|
71 |
+
);
|
72 |
+
const colorsRGB = colors.map((color) => d3.rgb(...(color as [number, number, number])));
|
73 |
+
resolve({
|
74 |
+
colors: sortColors(colorsRGB),
|
75 |
+
imgBlob
|
76 |
+
});
|
77 |
+
};
|
78 |
+
img.src = base64image;
|
79 |
+
});
|
80 |
+
}
|
81 |
+
|
82 |
+
export async function uploadImage(imagBlob: Blob, prompt: string): string {
|
83 |
+
// simple regex slugify string for file name
|
84 |
+
const promptSlug = slugify(prompt);
|
85 |
+
const UPLOAD_URL = dev ? 'moon/uploads' : 'https://huggingface.co/uploads';
|
86 |
+
|
87 |
+
const hash = crypto.randomUUID().split('-')[0];
|
88 |
+
const fileName = `color-palette-${hash}-${promptSlug}.jpeg`;
|
89 |
+
|
90 |
+
const file = new File([imagBlob], fileName, { type: 'image/jpeg' });
|
91 |
+
|
92 |
+
console.log('uploading image', file);
|
93 |
+
|
94 |
+
const response = await fetch(UPLOAD_URL, {
|
95 |
+
method: 'POST',
|
96 |
+
headers: {
|
97 |
+
'Content-Type': file.type,
|
98 |
+
'X-Requested-With': 'XMLHttpRequest'
|
99 |
+
},
|
100 |
+
body: file /// <- File inherits from Blob
|
101 |
+
});
|
102 |
+
const url = await response.text();
|
103 |
+
|
104 |
+
console.log('uploaded images', url);
|
105 |
+
return url;
|
106 |
+
}
|
107 |
+
|
108 |
+
function slugify(text: string) {
|
109 |
+
if (!text) return '';
|
110 |
+
return text
|
111 |
+
.toString()
|
112 |
+
.toLowerCase()
|
113 |
+
.replace(/\s+/g, '-')
|
114 |
+
.replace(/[^\w\-]+/g, '')
|
115 |
+
.replace(/\-\-+/g, '-')
|
116 |
+
.replace(/^-+/, '')
|
117 |
+
.replace(/-+$/, '');
|
118 |
+
}
|
frontend/src/routes/+layout.svelte
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script>
|
2 |
+
import '../app.css';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<slot />
|
frontend/src/routes/+layout.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
export const prerender = true;
|
frontend/src/routes/+page.svelte
ADDED
@@ -0,0 +1,307 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount } from 'svelte';
|
3 |
+
import type { ColorsPrompt, ColorsImage } from '$lib/types';
|
4 |
+
import { randomSeed, extractPalette, uploadImage } from '$lib/utils';
|
5 |
+
import { isLoading, loadingState } from '$lib/store';
|
6 |
+
import { PUBLIC_WS_ENDPOINT, PUBLIC_API} from '$env/static/public';
|
7 |
+
import Pallette from '$lib/Palette.svelte';
|
8 |
+
import ArrowRight from '$lib/ArrowRight.svelte';
|
9 |
+
import ArrowLeft from '$lib/ArrowLeft.svelte';
|
10 |
+
|
11 |
+
let promptsData: ColorsPrompt[] = [];
|
12 |
+
let prompt: string;
|
13 |
+
let promptInputEl: HTMLElement;
|
14 |
+
|
15 |
+
onMount(() => {
|
16 |
+
fetchData();
|
17 |
+
const interval = window.setInterval(fetchData, 5000);
|
18 |
+
return () => {
|
19 |
+
clearInterval(interval);
|
20 |
+
};
|
21 |
+
});
|
22 |
+
|
23 |
+
async function fetchData() {
|
24 |
+
const palettes = await fetch(PUBLIC_API + '/data').then((d) => d.json());
|
25 |
+
if (!promptsData || palettes?.length > promptsData?.length) {
|
26 |
+
promptsData = sortData(palettes);
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
$: promptsTotal = promptsData?.length || null;
|
31 |
+
|
32 |
+
let page: number = 0;
|
33 |
+
const maxPerPage: number = 10;
|
34 |
+
$: totalPages = Math.ceil(promptsData?.length / maxPerPage) || 0;
|
35 |
+
$: promptsDataPage = [...promptsData].slice(page * maxPerPage, (page + 1) * maxPerPage);
|
36 |
+
let pagesLinks: number[] = [];
|
37 |
+
$: if (totalPages) {
|
38 |
+
const pagesNums = Array(totalPages)
|
39 |
+
.fill([])
|
40 |
+
.map((_, i) => ({ value: i, label: i + 1 }));
|
41 |
+
pagesLinks = pagesNums
|
42 |
+
.slice(0, 3)
|
43 |
+
.concat([{ value: -1, label: '...' }])
|
44 |
+
.concat(pagesNums.length > 3 ? pagesNums.slice(-1) : []);
|
45 |
+
console.log(pagesLinks);
|
46 |
+
}
|
47 |
+
|
48 |
+
function sortData(_promptData: ColorsPrompt[]) {
|
49 |
+
return _promptData
|
50 |
+
.sort((a, b) => b.id - a.id)
|
51 |
+
.map((p) => p.data)
|
52 |
+
.filter((d) => d.images.length > 0);
|
53 |
+
}
|
54 |
+
async function savePaletteDB(colorPrompt: ColorsPrompt) {
|
55 |
+
try {
|
56 |
+
const newPalettes: ColorsPrompt[] = await fetch(PUBLIC_API + '/new_palette', {
|
57 |
+
method: 'POST',
|
58 |
+
headers: {
|
59 |
+
'Content-Type': 'application/json'
|
60 |
+
},
|
61 |
+
body: JSON.stringify({
|
62 |
+
prompt: colorPrompt.prompt,
|
63 |
+
images: colorPrompt.images.map((i) => ({
|
64 |
+
imgURL: i.imgURL,
|
65 |
+
colors: i.colors.map((c) => c.formatHex())
|
66 |
+
}))
|
67 |
+
})
|
68 |
+
}).then((d) => d.json());
|
69 |
+
|
70 |
+
promptsData = sortData(newPalettes);
|
71 |
+
} catch (e) {
|
72 |
+
console.error(e);
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
async function generatePalette(_prompt: string) {
|
77 |
+
if (!_prompt || $isLoading == true) return;
|
78 |
+
$loadingState = 'Pending';
|
79 |
+
$isLoading = true;
|
80 |
+
|
81 |
+
const sessionHash = crypto.randomUUID();
|
82 |
+
|
83 |
+
const hashpayload = {
|
84 |
+
fn_index: 2,
|
85 |
+
session_hash: sessionHash
|
86 |
+
};
|
87 |
+
|
88 |
+
const datapayload = {
|
89 |
+
data: [_prompt]
|
90 |
+
};
|
91 |
+
|
92 |
+
const websocket = new WebSocket(PUBLIC_WS_ENDPOINT);
|
93 |
+
// websocket.onopen = async function (event) {
|
94 |
+
// websocket.send(JSON.stringify({ hash: sessionHash }));
|
95 |
+
// };
|
96 |
+
websocket.onclose = (evt) => {
|
97 |
+
if (!evt.wasClean) {
|
98 |
+
$loadingState = 'Error';
|
99 |
+
$isLoading = false;
|
100 |
+
}
|
101 |
+
};
|
102 |
+
websocket.onmessage = async function (event) {
|
103 |
+
try {
|
104 |
+
const data = JSON.parse(event.data);
|
105 |
+
$loadingState = '';
|
106 |
+
switch (data.msg) {
|
107 |
+
case 'send_hash':
|
108 |
+
websocket.send(JSON.stringify(hashpayload));
|
109 |
+
break;
|
110 |
+
case 'send_data':
|
111 |
+
$loadingState = 'Sending Data';
|
112 |
+
websocket.send(JSON.stringify({ ...hashpayload, ...datapayload }));
|
113 |
+
break;
|
114 |
+
case 'queue_full':
|
115 |
+
$loadingState = 'Queue full';
|
116 |
+
websocket.close();
|
117 |
+
$isLoading = false;
|
118 |
+
return;
|
119 |
+
case 'estimation':
|
120 |
+
const { msg, rank, queue_size } = data;
|
121 |
+
$loadingState = `On queue ${rank}/${queue_size}`;
|
122 |
+
break;
|
123 |
+
case 'process_generating':
|
124 |
+
$loadingState = data.success ? 'Generating' : 'Error';
|
125 |
+
break;
|
126 |
+
case 'process_completed':
|
127 |
+
try {
|
128 |
+
const images = await extractColorsImages(data.output.data[0], _prompt);
|
129 |
+
savePaletteDB({
|
130 |
+
prompt: _prompt,
|
131 |
+
images
|
132 |
+
});
|
133 |
+
$loadingState = data.success ? 'Complete' : 'Error';
|
134 |
+
} catch (e) {
|
135 |
+
$loadingState = e.message;
|
136 |
+
}
|
137 |
+
websocket.close();
|
138 |
+
$isLoading = false;
|
139 |
+
return;
|
140 |
+
case 'process_starts':
|
141 |
+
$loadingState = 'Processing';
|
142 |
+
break;
|
143 |
+
}
|
144 |
+
} catch (e) {
|
145 |
+
console.error(e);
|
146 |
+
$isLoading = false;
|
147 |
+
$loadingState = 'Error';
|
148 |
+
}
|
149 |
+
};
|
150 |
+
}
|
151 |
+
async function extractColorsImages(images: string[], _prompt: string): Promise<ColorsImage[]> {
|
152 |
+
const nsfwColors = ['#040404', '#B7B7B7', '#565656', '#747474', '#6C6C6C'];
|
153 |
+
|
154 |
+
const colorImages = [];
|
155 |
+
let isNSFW = false;
|
156 |
+
for (const base64img of images) {
|
157 |
+
const { colors, imgBlob } = await extractPalette(base64img);
|
158 |
+
if (
|
159 |
+
!colors.map((color) => color.formatHex().toUpperCase()).every((c) => nsfwColors.includes(c))
|
160 |
+
) {
|
161 |
+
const url = await uploadImage(imgBlob, _prompt);
|
162 |
+
const colorsImage: ColorsImage = {
|
163 |
+
colors,
|
164 |
+
imgURL: url
|
165 |
+
};
|
166 |
+
colorImages.push(colorsImage);
|
167 |
+
} else {
|
168 |
+
isNSFW = true;
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
if (colorImages.length === 0 && isNSFW) {
|
173 |
+
console.error('Possible NSFW image');
|
174 |
+
throw new Error('Possible NSFW image');
|
175 |
+
}
|
176 |
+
return colorImages;
|
177 |
+
}
|
178 |
+
function remix(e: CustomEvent) {
|
179 |
+
prompt = e.detail.prompt;
|
180 |
+
promptInputEl.scrollIntoView({ behavior: 'smooth' });
|
181 |
+
scrollTop();
|
182 |
+
}
|
183 |
+
function scrollTop() {
|
184 |
+
window.scrollTo(0, 0);
|
185 |
+
if ('parentIFrame' in window) {
|
186 |
+
window.parentIFrame.scrollTo(0, promptInputEl.offsetTop);
|
187 |
+
}
|
188 |
+
}
|
189 |
+
</script>
|
190 |
+
|
191 |
+
<div class="max-w-screen-md mx-auto px-3 py-8 relative z-0">
|
192 |
+
<h1 class="text-3xl font-bold leading-normal">Palette generation with Stable Diffusion</h1>
|
193 |
+
<p class="text-sm">
|
194 |
+
Original ideas:
|
195 |
+
|
196 |
+
<a
|
197 |
+
class="link"
|
198 |
+
target="_blank"
|
199 |
+
rel="nofollow noopener"
|
200 |
+
href="https://twitter.com/mattdesl/status/1569457653298139136"
|
201 |
+
>
|
202 |
+
Matt DesLauriers
|
203 |
+
</a>,
|
204 |
+
<a class="link" href="https://drib.net/homage"> dribnet </a>
|
205 |
+
</p>
|
206 |
+
<div class="relative top-0 z-50 bg-white dark:bg-black py-3">
|
207 |
+
<form class="grid grid-cols-6" on:submit|preventDefault={() => generatePalette(prompt)}>
|
208 |
+
<input
|
209 |
+
bind:this={promptInputEl}
|
210 |
+
class="input"
|
211 |
+
placeholder="A photo of a beautiful sunset in San Francisco"
|
212 |
+
title="Input prompt to generate image and obtain palette"
|
213 |
+
type="text"
|
214 |
+
name="prompt"
|
215 |
+
bind:value={prompt}
|
216 |
+
disabled={$isLoading}
|
217 |
+
/>
|
218 |
+
<button
|
219 |
+
class="button"
|
220 |
+
on:click|preventDefault={() => generatePalette(prompt)}
|
221 |
+
disabled={$isLoading}
|
222 |
+
title="Generate Palette"
|
223 |
+
>
|
224 |
+
Create Palette
|
225 |
+
</button>
|
226 |
+
</form>
|
227 |
+
{#if $loadingState}
|
228 |
+
<h3 class="text-xs font-bold ml-3 inline-block">{$loadingState}</h3>
|
229 |
+
{#if $isLoading}
|
230 |
+
<svg
|
231 |
+
xmlns="http://www.w3.org/2000/svg"
|
232 |
+
fill="none"
|
233 |
+
viewBox="0 0 24 24"
|
234 |
+
class="animate-spin max-w-[1rem] inline-block"
|
235 |
+
>
|
236 |
+
<path
|
237 |
+
fill="currentColor"
|
238 |
+
d="M20 12a8 8 0 0 1-8 8v4a12 12 0 0 0 12-12h-4Zm-2-5.3a8 8 0 0 1 2 5.3h4c0-3-1.1-5.8-3-8l-3 2.7Z"
|
239 |
+
/>
|
240 |
+
</svg>
|
241 |
+
{/if}
|
242 |
+
{/if}
|
243 |
+
</div>
|
244 |
+
|
245 |
+
<div class="flex items-center gap-4 my-10">
|
246 |
+
<div class="font-bold text-sm">
|
247 |
+
{promptsTotal ? `${promptsTotal} submitted palettes` : 'Loading...'}
|
248 |
+
</div>
|
249 |
+
<div class="grow border-b border-gray-200" />
|
250 |
+
</div>
|
251 |
+
|
252 |
+
{#if promptsDataPage}
|
253 |
+
<div>
|
254 |
+
{#each promptsDataPage as promptData}
|
255 |
+
<Pallette {promptData} on:remix={remix} />
|
256 |
+
<div class="border-b border-gray-200 py-2" />
|
257 |
+
{/each}
|
258 |
+
</div>
|
259 |
+
<nav role="navigation">
|
260 |
+
<ul
|
261 |
+
class="items-center sm:justify-center space-x-2 select-none w-full flex justify-center mt-6 mb-4"
|
262 |
+
>
|
263 |
+
<li />
|
264 |
+
<li>
|
265 |
+
<a
|
266 |
+
on:click|preventDefault={() => {
|
267 |
+
page = page - 1 < 0 ? 0 : page - 1;
|
268 |
+
scrollTop();
|
269 |
+
}}
|
270 |
+
class="px-2.5 py-1 hover:bg-gray-50 dark:hover:bg-gray-800 flex items-center rounded-lg"
|
271 |
+
href="#"
|
272 |
+
><ArrowLeft /> Previous
|
273 |
+
</a>
|
274 |
+
</li>
|
275 |
+
<li class="text-sm">
|
276 |
+
<span class="inline-block min-w-[3ch] text-right">{page + 1} </span>/<span
|
277 |
+
class="inline-block min-w-[3ch]"
|
278 |
+
>{totalPages}
|
279 |
+
</span>
|
280 |
+
</li>
|
281 |
+
<li>
|
282 |
+
<a
|
283 |
+
on:click|preventDefault={() => {
|
284 |
+
page = page + 1 >= totalPages - 1 ? totalPages - 1 : page + 1;
|
285 |
+
scrollTop();
|
286 |
+
}}
|
287 |
+
class="px-2.5 py-1 hover:bg-gray-50 dark:hover:bg-gray-800 flex items-center rounded-lg"
|
288 |
+
href="#"
|
289 |
+
>Next <ArrowRight />
|
290 |
+
</a>
|
291 |
+
</li>
|
292 |
+
</ul>
|
293 |
+
</nav>
|
294 |
+
{/if}
|
295 |
+
</div>
|
296 |
+
|
297 |
+
<style lang="postcss" scoped>
|
298 |
+
.link {
|
299 |
+
@apply text-xs underline font-bold hover:no-underline hover:text-gray-500 visited:text-gray-500;
|
300 |
+
}
|
301 |
+
.input {
|
302 |
+
@apply text-sm disabled:opacity-50 col-span-4 md:col-span-5 italic dark:placeholder:text-black placeholder:text-white text-white dark:text-black placeholder:text-opacity-30 dark:placeholder:text-opacity-10 dark:bg-white bg-slate-900 border-2 border-black rounded-2xl px-2 shadow-sm focus:outline-none focus:border-gray-400 focus:ring-1;
|
303 |
+
}
|
304 |
+
.button {
|
305 |
+
@apply disabled:opacity-50 col-span-2 md:col-span-1 dark:bg-white dark:text-black border-2 border-black rounded-2xl ml-2 px-2 py-2 text-xs shadow-sm font-bold focus:outline-none focus:border-gray-400;
|
306 |
+
}
|
307 |
+
</style>
|
frontend/static/favicon.png
ADDED
![]() |
frontend/static/robots.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# https://www.robotstxt.org/robotstxt.html
|
2 |
+
User-agent: *
|
3 |
+
Disallow:
|
frontend/static/svelte-welcome.png
ADDED
![]() |
frontend/static/svelte-welcome.webp
ADDED
![]() |
frontend/svelte.config.js
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import adapter from '@sveltejs/adapter-static';
|
2 |
+
import preprocess from 'svelte-preprocess';
|
3 |
+
|
4 |
+
const dev = process.env.NODE_ENV === 'development';
|
5 |
+
console.log(dev)
|
6 |
+
/** @type {import('@sveltejs/kit').Config} */
|
7 |
+
const config = {
|
8 |
+
preprocess: [
|
9 |
+
preprocess({
|
10 |
+
postcss: true
|
11 |
+
})
|
12 |
+
],
|
13 |
+
kit: {
|
14 |
+
paths: {
|
15 |
+
base: '/static'
|
16 |
+
},
|
17 |
+
adapter: adapter({
|
18 |
+
pages: 'build',
|
19 |
+
assets: 'build',
|
20 |
+
fallback: null,
|
21 |
+
precompress: false
|
22 |
+
})
|
23 |
+
}
|
24 |
+
};
|
25 |
+
|
26 |
+
export default config;
|
frontend/tailwind.config.cjs
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('tailwindcss').Config} */
|
2 |
+
module.exports = {
|
3 |
+
content: ['./src/**/*.{html,js,svelte,ts}'],
|
4 |
+
theme: {},
|
5 |
+
plugins: [require('@tailwindcss/forms'), require('@tailwindcss/line-clamp')]
|
6 |
+
};
|
frontend/tsconfig.json
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"extends": "./.svelte-kit/tsconfig.json",
|
3 |
+
"compilerOptions": {
|
4 |
+
"allowJs": true,
|
5 |
+
"checkJs": true,
|
6 |
+
"esModuleInterop": true,
|
7 |
+
"forceConsistentCasingInFileNames": true,
|
8 |
+
"resolveJsonModule": true,
|
9 |
+
"skipLibCheck": true,
|
10 |
+
"sourceMap": true,
|
11 |
+
"strict": true
|
12 |
+
}
|
13 |
+
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
14 |
+
//
|
15 |
+
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
16 |
+
// from the referenced tsconfig.json - TypeScript does not merge them in
|
17 |
+
}
|
frontend/vite.config.dev.ts
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { sveltekit } from '@sveltejs/kit/vite';
|
2 |
+
import type { UserConfig } from 'vite';
|
3 |
+
|
4 |
+
const config: UserConfig = {
|
5 |
+
plugins: [sveltekit()],
|
6 |
+
server: {
|
7 |
+
proxy: {
|
8 |
+
'/moon': {
|
9 |
+
target: 'https://huggingface.co',
|
10 |
+
changeOrigin: true,
|
11 |
+
cookieDomainRewrite: 'localhost',
|
12 |
+
rewrite: (path) => path.replace(/^\/moon/, '')
|
13 |
+
}
|
14 |
+
}
|
15 |
+
}
|
16 |
+
};
|
17 |
+
|
18 |
+
export default config;
|
frontend/vite.config.ts
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { sveltekit } from '@sveltejs/kit/vite';
|
2 |
+
import type { UserConfig } from 'vite';
|
3 |
+
|
4 |
+
const config: UserConfig = {
|
5 |
+
plugins: [sveltekit()]
|
6 |
+
};
|
7 |
+
|
8 |
+
export default config;
|
requirements.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
datasets
|
2 |
+
flask
|
3 |
+
flask_cors
|
4 |
+
flask_expects_json
|
5 |
+
requests
|
6 |
+
Pillow
|
7 |
+
gradio
|
8 |
+
Flask-APScheduler
|
run.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
import subprocess
|
2 |
+
|
3 |
+
subprocess.run(["make", "build-all"], shell=False)
|
static/_app/immutable/assets/_layout-e397cbf6.css
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[type=text],[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::-webkit-backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.absolute{position:absolute}.relative{position:relative}.bottom-0{bottom:0px}.top-0{top:0px}.z-0{z-index:0}.z-50{z-index:50}.col-span-6{grid-column:span 6 / span 6}.col-span-4{grid-column:span 4 / span 4}.col-span-2{grid-column:span 2 / span 2}.row-start-1{grid-row-start:1}.row-start-3{grid-row-start:3}.row-start-2{grid-row-start:2}.row-start-4{grid-row-start:4}.m-1{margin:.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.mr-1\.5{margin-right:.375rem}.mr-1{margin-right:.25rem}.ml-1\.5{margin-left:.375rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mt-6{margin-top:1.5rem}.mb-4{margin-bottom:1rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.grid{display:grid}.aspect-square{aspect-ratio:1 / 1}.h-3{height:.75rem}.w-full{width:100%}.w-3{width:.75rem}.min-w-\[9ch\]{min-width:9ch}.min-w-\[3ch\]{min-width:3ch}.max-w-full{max-width:100%}.max-w-prose{max-width:65ch}.max-w-\[100px\]{max-width:100px}.max-w-screen-md{max-width:768px}.max-w-\[1rem\]{max-width:1rem}.grow{flex-grow:1}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@-webkit-keyframes spin{to{transform:rotate(360deg)}}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-around{justify-content:space-around}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.rounded-full{border-radius:9999px}.rounded-2xl{border-radius:1rem}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-black{--tw-border-opacity: 1;border-color:rgb(0 0 0 / var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity))}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.px-2{padding-left:.5rem;padding-right:.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.pl-1{padding-left:.25rem}.pb-3{padding-bottom:.75rem}.text-center{text-align:center}.text-right{text-align:right}.text-xs{font-size:.75rem;line-height:1rem}.text-base{font-size:1rem;line-height:1.5rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-sm{font-size:.875rem;line-height:1.25rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-normal{line-height:1.5}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.placeholder\:text-white::-moz-placeholder{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.placeholder\:text-white::placeholder{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.placeholder\:text-opacity-30::-moz-placeholder{--tw-text-opacity: .3}.placeholder\:text-opacity-30::placeholder{--tw-text-opacity: .3}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.hover\:no-underline:hover{text-decoration-line:none}.focus\:border-gray-400:focus{--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:opacity-50:disabled{opacity:.5}@media (prefers-color-scheme: dark){.dark\:border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}.dark\:bg-\[rgb\(11\,15\,25\)\]{--tw-bg-opacity: 1;background-color:rgb(11 15 25 / var(--tw-bg-opacity))}.dark\:bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.dark\:bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity))}.dark\:bg-slate-300{--tw-bg-opacity: 1;background-color:rgb(203 213 225 / var(--tw-bg-opacity))}.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\:text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.dark\:placeholder\:text-black::-moz-placeholder{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.dark\:placeholder\:text-black::placeholder{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.dark\:placeholder\:text-opacity-10::-moz-placeholder{--tw-text-opacity: .1}.dark\:placeholder\:text-opacity-10::placeholder{--tw-text-opacity: .1}.dark\:hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}}@media (min-width: 640px){.sm\:justify-center{justify-content:center}}@media (min-width: 768px){.md\:col-span-4{grid-column:span 4 / span 4}.md\:col-span-2{grid-column:span 2 / span 2}.md\:col-span-5{grid-column:span 5 / span 5}.md\:col-span-1{grid-column:span 1 / span 1}.md\:col-start-5{grid-column-start:5}.md\:row-start-2{grid-row-start:2}.md\:justify-end{justify-content:flex-end}}
|
static/_app/immutable/assets/_page-fd1176fc.css
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
.button.svelte-8zu88a{margin-left:.5rem;min-width:9ch;border-radius:1rem;border-width:2px;--tw-border-opacity:1;border-color:rgb(0 0 0 / var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(0 0 0 / var(--tw-bg-opacity));padding:.5rem;font-size:.75rem;line-height:1rem;font-weight:700;--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity));--tw-shadow:0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.button.svelte-8zu88a:focus{--tw-border-opacity:1;border-color:rgb(156 163 175 / var(--tw-border-opacity));outline:2px solid transparent;outline-offset:2px}@media (prefers-color-scheme: dark){.button.svelte-8zu88a{--tw-border-opacity:1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}}.link.svelte-zbscw1{font-size:.75rem;line-height:1rem;font-weight:700;text-decoration-line:underline}.link.svelte-zbscw1:visited{color:#6b7280}.link.svelte-zbscw1:hover{--tw-text-opacity:1;color:rgb(107 114 128 / var(--tw-text-opacity));text-decoration-line:none}.input.svelte-zbscw1{grid-column:span 4 / span 4;border-radius:1rem;border-width:2px;--tw-border-opacity:1;border-color:rgb(0 0 0 / var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(15 23 42 / var(--tw-bg-opacity));padding-left:.5rem;padding-right:.5rem;font-size:.875rem;line-height:1.25rem;font-style:italic;--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity));--tw-shadow:0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.input.svelte-zbscw1::-moz-placeholder{color:rgb(255 255 255 / var(--tw-text-opacity));--tw-text-opacity:.3 }.input.svelte-zbscw1::placeholder{color:rgb(255 255 255 / var(--tw-text-opacity));--tw-text-opacity:.3 }.input.svelte-zbscw1:focus{--tw-border-opacity:1;border-color:rgb(156 163 175 / var(--tw-border-opacity));outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.input.svelte-zbscw1:disabled{opacity:.5}@media (prefers-color-scheme: dark){.input.svelte-zbscw1{--tw-bg-opacity:1;background-color:rgb(255 255 255 / var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(0 0 0 / var(--tw-text-opacity))}.input.svelte-zbscw1::-moz-placeholder{color:rgb(0 0 0 / var(--tw-text-opacity));--tw-text-opacity:.1 }.input.svelte-zbscw1::placeholder{color:rgb(0 0 0 / var(--tw-text-opacity));--tw-text-opacity:.1 }}@media (min-width: 768px){.input.svelte-zbscw1{grid-column:span 5 / span 5}}.button.svelte-zbscw1{grid-column:span 2 / span 2;margin-left:.5rem;border-radius:1rem;border-width:2px;--tw-border-opacity:1;border-color:rgb(0 0 0 / var(--tw-border-opacity));padding:.5rem;font-size:.75rem;line-height:1rem;font-weight:700;--tw-shadow:0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.button.svelte-zbscw1:focus{--tw-border-opacity:1;border-color:rgb(156 163 175 / var(--tw-border-opacity));outline:2px solid transparent;outline-offset:2px}.button.svelte-zbscw1:disabled{opacity:.5}@media (prefers-color-scheme: dark){.button.svelte-zbscw1{--tw-bg-opacity:1;background-color:rgb(255 255 255 / var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(0 0 0 / var(--tw-text-opacity))}}@media (min-width: 768px){.button.svelte-zbscw1{grid-column:span 1 / span 1}}
|
static/_app/immutable/chunks/0-17a4fec1.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
import{_ as r}from"./_layout-1daba58d.js";import{default as t}from"../components/pages/_layout.svelte-55da5a4f.js";export{t as component,r as shared};
|
static/_app/immutable/chunks/1-26e94021.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
import{default as t}from"../components/error.svelte-c31c95c4.js";export{t as component};
|
static/_app/immutable/chunks/2-fc64cbc2.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
import{default as t}from"../components/pages/_page.svelte-3643b29c.js";export{t as component};
|
static/_app/immutable/chunks/_layout-1daba58d.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
const e=!0,r=Object.freeze(Object.defineProperty({__proto__:null,prerender:!0},Symbol.toStringTag,{value:"Module"}));export{r as _,e as p};
|
static/_app/immutable/chunks/index-3bda1050.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
import{A as f,s as l}from"./index-5559954d.js";const e=[];function h(n,u=f){let o;const i=new Set;function r(t){if(l(n,t)&&(n=t,o)){const c=!e.length;for(const s of i)s[1](),e.push(s,n);if(c){for(let s=0;s<e.length;s+=2)e[s][0](e[s+1]);e.length=0}}}function b(t){r(t(n))}function p(t,c=f){const s=[t,c];return i.add(s),i.size===1&&(o=u(r)||f),t(n),()=>{i.delete(s),i.size===0&&(o(),o=null)}}return{set:r,update:b,subscribe:p}}export{h as w};
|
static/_app/immutable/chunks/index-5559954d.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
function S(){}function H(t,n){for(const e in n)t[e]=n[e];return t}function B(t){return t()}function M(){return Object.create(null)}function p(t){t.forEach(B)}function I(t){return typeof t=="function"}function st(t,n){return t!=t?n==n:t!==n||t&&typeof t=="object"||typeof t=="function"}let g;function ot(t,n){return g||(g=document.createElement("a")),g.href=n,t===g.href}function G(t){return Object.keys(t).length===0}function J(t,...n){if(t==null)return S;const e=t.subscribe(...n);return e.unsubscribe?()=>e.unsubscribe():e}function at(t,n,e){t.$$.on_destroy.push(J(n,e))}function ft(t,n,e,i){if(t){const r=D(t,n,e,i);return t[0](r)}}function D(t,n,e,i){return t[1]&&i?H(e.ctx.slice(),t[1](i(n))):e.ctx}function _t(t,n,e,i){if(t[2]&&i){const r=t[2](i(e));if(n.dirty===void 0)return r;if(typeof r=="object"){const o=[],c=Math.max(n.dirty.length,r.length);for(let s=0;s<c;s+=1)o[s]=n.dirty[s]|r[s];return o}return n.dirty|r}return n.dirty}function dt(t,n,e,i,r,o){if(r){const c=D(n,e,i,o);t.p(c,r)}}function ht(t){if(t.ctx.length>32){const n=[],e=t.ctx.length/32;for(let i=0;i<e;i++)n[i]=-1;return n}return-1}function mt(t,n,e){return t.set(e),n}let $=!1;function K(){$=!0}function Q(){$=!1}function R(t,n,e,i){for(;t<n;){const r=t+(n-t>>1);e(r)<=i?t=r+1:n=r}return t}function W(t){if(t.hydrate_init)return;t.hydrate_init=!0;let n=t.childNodes;if(t.nodeName==="HEAD"){const u=[];for(let l=0;l<n.length;l++){const f=n[l];f.claim_order!==void 0&&u.push(f)}n=u}const e=new Int32Array(n.length+1),i=new Int32Array(n.length);e[0]=-1;let r=0;for(let u=0;u<n.length;u++){const l=n[u].claim_order,f=(r>0&&n[e[r]].claim_order<=l?r+1:R(1,r,y=>n[e[y]].claim_order,l))-1;i[u]=e[f]+1;const a=f+1;e[a]=u,r=Math.max(a,r)}const o=[],c=[];let s=n.length-1;for(let u=e[r]+1;u!=0;u=i[u-1]){for(o.push(n[u-1]);s>=u;s--)c.push(n[s]);s--}for(;s>=0;s--)c.push(n[s]);o.reverse(),c.sort((u,l)=>u.claim_order-l.claim_order);for(let u=0,l=0;u<c.length;u++){for(;l<o.length&&c[u].claim_order>=o[l].claim_order;)l++;const f=l<o.length?o[l]:null;t.insertBefore(c[u],f)}}function U(t,n){if($){for(W(t),(t.actual_end_child===void 0||t.actual_end_child!==null&&t.actual_end_child.parentNode!==t)&&(t.actual_end_child=t.firstChild);t.actual_end_child!==null&&t.actual_end_child.claim_order===void 0;)t.actual_end_child=t.actual_end_child.nextSibling;n!==t.actual_end_child?(n.claim_order!==void 0||n.parentNode!==t)&&t.insertBefore(n,t.actual_end_child):t.actual_end_child=n.nextSibling}else(n.parentNode!==t||n.nextSibling!==null)&&t.appendChild(n)}function pt(t,n,e){$&&!e?U(t,n):(n.parentNode!==t||n.nextSibling!=e)&&t.insertBefore(n,e||null)}function V(t){t.parentNode.removeChild(t)}function yt(t,n){for(let e=0;e<t.length;e+=1)t[e]&&t[e].d(n)}function X(t){return document.createElement(t)}function Y(t){return document.createElementNS("http://www.w3.org/2000/svg",t)}function A(t){return document.createTextNode(t)}function gt(){return A(" ")}function xt(){return A("")}function bt(t,n,e,i){return t.addEventListener(n,e,i),()=>t.removeEventListener(n,e,i)}function vt(t){return function(n){return n.preventDefault(),t.call(this,n)}}function $t(t,n,e){e==null?t.removeAttribute(n):t.getAttribute(n)!==e&&t.setAttribute(n,e)}function Z(t){return Array.from(t.childNodes)}function tt(t){t.claim_info===void 0&&(t.claim_info={last_index:0,total_claimed:0})}function L(t,n,e,i,r=!1){tt(t);const o=(()=>{for(let c=t.claim_info.last_index;c<t.length;c++){const s=t[c];if(n(s)){const u=e(s);return u===void 0?t.splice(c,1):t[c]=u,r||(t.claim_info.last_index=c),s}}for(let c=t.claim_info.last_index-1;c>=0;c--){const s=t[c];if(n(s)){const u=e(s);return u===void 0?t.splice(c,1):t[c]=u,r?u===void 0&&t.claim_info.last_index--:t.claim_info.last_index=c,s}}return i()})();return o.claim_order=t.claim_info.total_claimed,t.claim_info.total_claimed+=1,o}function O(t,n,e,i){return L(t,r=>r.nodeName===n,r=>{const o=[];for(let c=0;c<r.attributes.length;c++){const s=r.attributes[c];e[s.name]||o.push(s.name)}o.forEach(c=>r.removeAttribute(c))},()=>i(n))}function Et(t,n,e){return O(t,n,e,X)}function wt(t,n,e){return O(t,n,e,Y)}function nt(t,n){return L(t,e=>e.nodeType===3,e=>{const i=""+n;if(e.data.startsWith(i)){if(e.data.length!==i.length)return e.splitText(i.length)}else e.data=i},()=>A(n),!0)}function Nt(t){return nt(t," ")}function St(t,n){n=""+n,t.wholeText!==n&&(t.data=n)}function At(t,n){t.value=n==null?"":n}function jt(t,n,e,i){e===null?t.style.removeProperty(n):t.style.setProperty(n,e,i?"important":"")}function et(t,n,{bubbles:e=!1,cancelable:i=!1}={}){const r=document.createEvent("CustomEvent");return r.initCustomEvent(t,e,i,n),r}let m;function h(t){m=t}function j(){if(!m)throw new Error("Function called outside component initialization");return m}function Ct(t){j().$$.on_mount.push(t)}function kt(t){j().$$.after_update.push(t)}function Mt(){const t=j();return(n,e,{cancelable:i=!1}={})=>{const r=t.$$.callbacks[n];if(r){const o=et(n,e,{cancelable:i});return r.slice().forEach(c=>{c.call(t,o)}),!o.defaultPrevented}return!0}}const d=[],P=[],b=[],q=[],T=Promise.resolve();let w=!1;function z(){w||(w=!0,T.then(F))}function Pt(){return z(),T}function N(t){b.push(t)}const E=new Set;let x=0;function F(){const t=m;do{for(;x<d.length;){const n=d[x];x++,h(n),it(n.$$)}for(h(null),d.length=0,x=0;P.length;)P.pop()();for(let n=0;n<b.length;n+=1){const e=b[n];E.has(e)||(E.add(e),e())}b.length=0}while(d.length);for(;q.length;)q.pop()();w=!1,E.clear(),h(t)}function it(t){if(t.fragment!==null){t.update(),p(t.before_update);const n=t.dirty;t.dirty=[-1],t.fragment&&t.fragment.p(t.ctx,n),t.after_update.forEach(N)}}const v=new Set;let _;function qt(){_={r:0,c:[],p:_}}function Bt(){_.r||p(_.c),_=_.p}function rt(t,n){t&&t.i&&(v.delete(t),t.i(n))}function Dt(t,n,e,i){if(t&&t.o){if(v.has(t))return;v.add(t),_.c.push(()=>{v.delete(t),i&&(e&&t.d(1),i())}),t.o(n)}else i&&i()}function Lt(t){t&&t.c()}function Ot(t,n){t&&t.l(n)}function ct(t,n,e,i){const{fragment:r,on_mount:o,on_destroy:c,after_update:s}=t.$$;r&&r.m(n,e),i||N(()=>{const u=o.map(B).filter(I);c?c.push(...u):p(u),t.$$.on_mount=[]}),s.forEach(N)}function ut(t,n){const e=t.$$;e.fragment!==null&&(p(e.on_destroy),e.fragment&&e.fragment.d(n),e.on_destroy=e.fragment=null,e.ctx=[])}function lt(t,n){t.$$.dirty[0]===-1&&(d.push(t),z(),t.$$.dirty.fill(0)),t.$$.dirty[n/31|0]|=1<<n%31}function Tt(t,n,e,i,r,o,c,s=[-1]){const u=m;h(t);const l=t.$$={fragment:null,ctx:null,props:o,update:S,not_equal:r,bound:M(),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],after_update:[],context:new Map(n.context||(u?u.$$.context:[])),callbacks:M(),dirty:s,skip_bound:!1,root:n.target||u.$$.root};c&&c(l.root);let f=!1;if(l.ctx=e?e(t,n.props||{},(a,y,...C)=>{const k=C.length?C[0]:y;return l.ctx&&r(l.ctx[a],l.ctx[a]=k)&&(!l.skip_bound&&l.bound[a]&&l.bound[a](k),f&<(t,a)),y}):[],l.update(),f=!0,p(l.before_update),l.fragment=i?i(l.ctx):!1,n.target){if(n.hydrate){K();const a=Z(n.target);l.fragment&&l.fragment.l(a),a.forEach(V)}else l.fragment&&l.fragment.c();n.intro&&rt(t.$$.fragment),ct(t,n.target,n.anchor,n.customElement),Q(),F()}h(u)}class zt{$destroy(){ut(this,1),this.$destroy=S}$on(n,e){const i=this.$$.callbacks[n]||(this.$$.callbacks[n]=[]);return i.push(e),()=>{const r=i.indexOf(e);r!==-1&&i.splice(r,1)}}$set(n){this.$$set&&!G(n)&&(this.$$.skip_bound=!0,this.$$set(n),this.$$.skip_bound=!1)}}export{S as A,ft as B,dt as C,ht as D,_t as E,U as F,at as G,yt as H,Y as I,wt as J,bt as K,ot as L,p as M,Mt as N,At as O,vt as P,mt as Q,P as R,zt as S,gt as a,pt as b,Nt as c,Bt as d,xt as e,rt as f,qt as g,V as h,Tt as i,kt as j,X as k,Et as l,Z as m,$t as n,Ct as o,jt as p,A as q,nt as r,st as s,Dt as t,St as u,Lt as v,Ot as w,ct as x,ut as y,Pt as z};
|
static/_app/immutable/chunks/singletons-e4c31a41.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
import{w as u}from"./index-3bda1050.js";let f="",b="";function g(n){f=n.base,b=n.assets||f}function m(n){let e=n.baseURI;if(!e){const t=n.getElementsByTagName("base");e=t.length?t[0].href:n.URL}return e}function _(){return{x:pageXOffset,y:pageYOffset}}function k(n){let e,t=null,r=null,a=null;for(const s of n.composedPath())s instanceof Element&&(!e&&s.nodeName.toUpperCase()==="A"&&(e=s),t===null&&(t=l(s,"data-sveltekit-noscroll")),r===null&&(r=l(s,"data-sveltekit-prefetch")),a===null&&(a=l(s,"data-sveltekit-reload")));const o=e&&new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI);return{a:e,url:o,options:{noscroll:t,prefetch:r,reload:a}}}function l(n,e){const t=n.getAttribute(e);return t===null?t:t===""?!0:(t==="off",!1)}function d(n){const e=u(n);let t=!0;function r(){t=!0,e.update(s=>s)}function a(s){t=!1,e.set(s)}function o(s){let i;return e.subscribe(c=>{(i===void 0||t&&c!==i)&&s(i=c)})}return{notify:r,set:a,subscribe:o}}function p(){const{set:n,subscribe:e}=u(!1);let t;async function r(){clearTimeout(t);const a=await fetch(`${b}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(a.ok){const{version:o}=await a.json(),s=o!=="1666722090902";return s&&(n(!0),clearTimeout(t)),s}else throw new Error(`Version check failed: ${a.status}`)}return{subscribe:e,check:r}}function v(n){n.client}const w={url:d({}),page:d({}),navigating:u(null),updated:p()};export{_ as a,g as b,k as f,m as g,v as i,w as s};
|
static/_app/immutable/components/error.svelte-c31c95c4.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
import{S as A,i as C,s as F,k as v,q as k,a as h,e as q,l as g,m as E,r as $,h as p,c as R,b as u,F as P,u as S,A as w,G}from"../chunks/index-5559954d.js";import{s as H}from"../chunks/singletons-e4c31a41.js";const O=()=>{const t=H,s={page:{subscribe:t.page.subscribe},navigating:{subscribe:t.navigating.subscribe},updated:t.updated};return Object.defineProperties(s,{preloading:{get(){return console.error("stores.preloading is deprecated; use stores.navigating instead"),{subscribe:t.navigating.subscribe}},enumerable:!1},session:{get(){return B(),{}},enumerable:!1}}),s},z={subscribe(t){return O().page.subscribe(t)}};function B(){throw new Error("stores.session is no longer available. See https://github.com/sveltejs/kit/discussions/5883")}function N(t){let s,i=t[0].error.frame+"",o;return{c(){s=v("pre"),o=k(i)},l(r){s=g(r,"PRE",{});var a=E(s);o=$(a,i),a.forEach(p)},m(r,a){u(r,s,a),P(s,o)},p(r,a){a&1&&i!==(i=r[0].error.frame+"")&&S(o,i)},d(r){r&&p(s)}}}function y(t){let s,i=t[0].error.stack+"",o;return{c(){s=v("pre"),o=k(i)},l(r){s=g(r,"PRE",{});var a=E(s);o=$(a,i),a.forEach(p)},m(r,a){u(r,s,a),P(s,o)},p(r,a){a&1&&i!==(i=r[0].error.stack+"")&&S(o,i)},d(r){r&&p(s)}}}function D(t){let s,i=t[0].status+"",o,r,a,b=t[0].error.message+"",_,d,c,m,l=t[0].error.frame&&N(t),n=t[0].error.stack&&y(t);return{c(){s=v("h1"),o=k(i),r=h(),a=v("pre"),_=k(b),d=h(),l&&l.c(),c=h(),n&&n.c(),m=q()},l(e){s=g(e,"H1",{});var f=E(s);o=$(f,i),f.forEach(p),r=R(e),a=g(e,"PRE",{});var j=E(a);_=$(j,b),j.forEach(p),d=R(e),l&&l.l(e),c=R(e),n&&n.l(e),m=q()},m(e,f){u(e,s,f),P(s,o),u(e,r,f),u(e,a,f),P(a,_),u(e,d,f),l&&l.m(e,f),u(e,c,f),n&&n.m(e,f),u(e,m,f)},p(e,[f]){f&1&&i!==(i=e[0].status+"")&&S(o,i),f&1&&b!==(b=e[0].error.message+"")&&S(_,b),e[0].error.frame?l?l.p(e,f):(l=N(e),l.c(),l.m(c.parentNode,c)):l&&(l.d(1),l=null),e[0].error.stack?n?n.p(e,f):(n=y(e),n.c(),n.m(m.parentNode,m)):n&&(n.d(1),n=null)},i:w,o:w,d(e){e&&p(s),e&&p(r),e&&p(a),e&&p(d),l&&l.d(e),e&&p(c),n&&n.d(e),e&&p(m)}}}function I(t,s,i){let o;return G(t,z,r=>i(0,o=r)),[o]}class L extends A{constructor(s){super(),C(this,s,I,D,F,{})}}export{L as default};
|