mirror of
https://github.com/osmarks/meme-search-engine.git
synced 2025-04-27 21:13:11 +00:00
restore stashed code
This commit is contained in:
parent
fcd28a5ede
commit
3e568ff613
62
Cargo.lock
generated
62
Cargo.lock
generated
@ -416,6 +416,18 @@ version = "2.5.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452"
|
checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitvec"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||||
|
dependencies = [
|
||||||
|
"funty",
|
||||||
|
"radium",
|
||||||
|
"tap",
|
||||||
|
"wyz",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@ -1117,6 +1129,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "funty"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -2850,6 +2868,12 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radium"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -3274,6 +3298,12 @@ version = "4.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seahash"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.11.1"
|
version = "2.11.1"
|
||||||
@ -3873,6 +3903,12 @@ dependencies = [
|
|||||||
"version-compare",
|
"version-compare",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tap"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.16"
|
version = "0.12.16"
|
||||||
@ -3958,6 +3994,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio 1.0.2",
|
"mio 1.0.2",
|
||||||
|
"mio 1.0.2",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
@ -4162,6 +4199,17 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tqdm"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa2d2932240205a99b65f15d9861992c95fbb8c9fb280b3a1f17a92db6dc611f"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"crossterm",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.40"
|
version = "0.1.40"
|
||||||
@ -4206,6 +4254,17 @@ dependencies = [
|
|||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-subscriber"
|
name = "tracing-subscriber"
|
||||||
version = "0.3.18"
|
version = "0.3.18"
|
||||||
@ -4214,14 +4273,17 @@ checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"matchers",
|
"matchers",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
|
"nu-ansi-term",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
"smallvec",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
453
clipfront2/package-lock.json
generated
453
clipfront2/package-lock.json
generated
@ -5,8 +5,8 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"esbuild": "^0.12.15",
|
"esbuild": "^0.13.5",
|
||||||
"esbuild-svelte": "^0.5.3",
|
"esbuild-svelte": "^0.6.3",
|
||||||
"sass": "^1.68.0",
|
"sass": "^1.68.0",
|
||||||
"svelte-preprocess-sass": "^2.0.1"
|
"svelte-preprocess-sass": "^2.0.1"
|
||||||
}
|
}
|
||||||
@ -67,27 +67,289 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.12.15",
|
"version": "0.13.15",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.15.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
|
||||||
"integrity": "sha512-72V4JNd2+48eOVCXx49xoSWHgC3/cCy96e7mbXKY+WOWghN00cCmlGnwVLRhRHorvv0dgCyuMYBZlM2xDM5OQw==",
|
"integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"esbuild-android-arm64": "0.13.15",
|
||||||
|
"esbuild-darwin-64": "0.13.15",
|
||||||
|
"esbuild-darwin-arm64": "0.13.15",
|
||||||
|
"esbuild-freebsd-64": "0.13.15",
|
||||||
|
"esbuild-freebsd-arm64": "0.13.15",
|
||||||
|
"esbuild-linux-32": "0.13.15",
|
||||||
|
"esbuild-linux-64": "0.13.15",
|
||||||
|
"esbuild-linux-arm": "0.13.15",
|
||||||
|
"esbuild-linux-arm64": "0.13.15",
|
||||||
|
"esbuild-linux-mips64le": "0.13.15",
|
||||||
|
"esbuild-linux-ppc64le": "0.13.15",
|
||||||
|
"esbuild-netbsd-64": "0.13.15",
|
||||||
|
"esbuild-openbsd-64": "0.13.15",
|
||||||
|
"esbuild-sunos-64": "0.13.15",
|
||||||
|
"esbuild-windows-32": "0.13.15",
|
||||||
|
"esbuild-windows-64": "0.13.15",
|
||||||
|
"esbuild-windows-arm64": "0.13.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esbuild-svelte": {
|
"node_modules/esbuild-android-arm64": {
|
||||||
"version": "0.5.3",
|
"version": "0.13.15",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz",
|
||||||
"integrity": "sha512-KByKD/yt8QaqKjLu32MG3MXBExJYlDM0QwzW3pzKLJR4eev0923DrUKRHPBBjB+OVirUtZnEJE/qitjdW/WyAw==",
|
"integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-darwin-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-darwin-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-freebsd-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-freebsd-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-linux-32": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-linux-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-linux-arm": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-linux-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-linux-mips64le": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-linux-ppc64le": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-netbsd-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-openbsd-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-sunos-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-svelte": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-WzDnkVeTwoyMPHHAqEkfy8aRkgK4YmpFcOOq9Cs6gdufPvH39K051mgmLSoqORqbjep7br4KXpDd0NUSSYFtKg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"svelte": "^3.38.3"
|
"svelte": "^3.46.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"esbuild": ">=0.9.6"
|
"esbuild": ">=0.9.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/esbuild-windows-32": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-windows-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-windows-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@ -234,10 +496,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte": {
|
"node_modules/svelte": {
|
||||||
"version": "3.38.3",
|
"version": "3.59.2",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.3.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz",
|
||||||
"integrity": "sha512-N7bBZJH0iF24wsalFZF+fVYMUOigaAUQMIcEKHO3jstK/iL8VmP9xE+P0/a76+FkNcWt+TDv2Gx1taUoUscrvw==",
|
"integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
@ -316,20 +579,158 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"esbuild": {
|
"esbuild": {
|
||||||
"version": "0.12.15",
|
"version": "0.13.15",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.15.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
|
||||||
"integrity": "sha512-72V4JNd2+48eOVCXx49xoSWHgC3/cCy96e7mbXKY+WOWghN00cCmlGnwVLRhRHorvv0dgCyuMYBZlM2xDM5OQw==",
|
"integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==",
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"esbuild-svelte": {
|
|
||||||
"version": "0.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.5.3.tgz",
|
|
||||||
"integrity": "sha512-KByKD/yt8QaqKjLu32MG3MXBExJYlDM0QwzW3pzKLJR4eev0923DrUKRHPBBjB+OVirUtZnEJE/qitjdW/WyAw==",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"svelte": "^3.38.3"
|
"esbuild-android-arm64": "0.13.15",
|
||||||
|
"esbuild-darwin-64": "0.13.15",
|
||||||
|
"esbuild-darwin-arm64": "0.13.15",
|
||||||
|
"esbuild-freebsd-64": "0.13.15",
|
||||||
|
"esbuild-freebsd-arm64": "0.13.15",
|
||||||
|
"esbuild-linux-32": "0.13.15",
|
||||||
|
"esbuild-linux-64": "0.13.15",
|
||||||
|
"esbuild-linux-arm": "0.13.15",
|
||||||
|
"esbuild-linux-arm64": "0.13.15",
|
||||||
|
"esbuild-linux-mips64le": "0.13.15",
|
||||||
|
"esbuild-linux-ppc64le": "0.13.15",
|
||||||
|
"esbuild-netbsd-64": "0.13.15",
|
||||||
|
"esbuild-openbsd-64": "0.13.15",
|
||||||
|
"esbuild-sunos-64": "0.13.15",
|
||||||
|
"esbuild-windows-32": "0.13.15",
|
||||||
|
"esbuild-windows-64": "0.13.15",
|
||||||
|
"esbuild-windows-arm64": "0.13.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"esbuild-android-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-darwin-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-darwin-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-freebsd-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-freebsd-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-linux-32": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-linux-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-linux-arm": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-linux-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-linux-mips64le": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-linux-ppc64le": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-netbsd-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-openbsd-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-sunos-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-svelte": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-WzDnkVeTwoyMPHHAqEkfy8aRkgK4YmpFcOOq9Cs6gdufPvH39K051mgmLSoqORqbjep7br4KXpDd0NUSSYFtKg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"svelte": "^3.46.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esbuild-windows-32": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-windows-64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild-windows-arm64": {
|
||||||
|
"version": "0.13.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz",
|
||||||
|
"integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@ -430,9 +831,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"svelte": {
|
"svelte": {
|
||||||
"version": "3.38.3",
|
"version": "3.59.2",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.3.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz",
|
||||||
"integrity": "sha512-N7bBZJH0iF24wsalFZF+fVYMUOigaAUQMIcEKHO3jstK/iL8VmP9xE+P0/a76+FkNcWt+TDv2Gx1taUoUscrvw==",
|
"integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"svelte-preprocess-filter": {
|
"svelte-preprocess-filter": {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"esbuild": "^0.12.15",
|
"esbuild": "^0.13.5",
|
||||||
"esbuild-svelte": "^0.5.3",
|
"esbuild-svelte": "^0.6.3",
|
||||||
"sass": "^1.68.0",
|
"sass": "^1.68.0",
|
||||||
"svelte-preprocess-sass": "^2.0.1"
|
"svelte-preprocess-sass": "^2.0.1"
|
||||||
}
|
}
|
||||||
|
40
clipfront2/src/About.svelte
Normal file
40
clipfront2/src/About.svelte
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<style lang="sass">
|
||||||
|
@use 'common' as *
|
||||||
|
|
||||||
|
.about
|
||||||
|
max-width: 40em
|
||||||
|
padding-left: 1em
|
||||||
|
padding-right: 1em
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="about">
|
||||||
|
<p>
|
||||||
|
Welcome to {util.hardConfig.name} by <a href="https://osmarks.net/">osmarks.net Computational Memetics</a>. {util.hardConfig.name} searches images using semantic image/text embedding models. In general, search by thinking of what caption your desired image might have been given by random people on the internet. The model currently in use can read text fairly well and understands moderately abstract properties of images, but is limited to English and case-insensitive.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Advanced Mode sliders are generated from PCA on the index. The human-readable labels are generated manually by <a href="https://datasets.osmarks.net/components.html">looking at things</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The code is open-source and available on <a href="https://github.com/osmarks/meme-search-engine/">GitHub.</a>
|
||||||
|
</p>
|
||||||
|
{#if util.hardConfig.telemetryEndpoint}
|
||||||
|
<h2>Privacy</h2>
|
||||||
|
<p>
|
||||||
|
We do not collect personal information. We do collect usage information (associated with a random ID) to improve the ranking algorithms. You can disable this:
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" checked={$telemetryStore} on:change={saveTelemetryEnabled} id="telemetry" />
|
||||||
|
<label for="telemetry">Allow usage statistics</label>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as util from "./util"
|
||||||
|
|
||||||
|
const telemetryStore = util.telemetryEnabled
|
||||||
|
|
||||||
|
const saveTelemetryEnabled = ev => {
|
||||||
|
telemetryStore.set(ev.target.checked)
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,4 +1,7 @@
|
|||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
@use 'common' as *
|
||||||
|
@use 'sass:color'
|
||||||
|
|
||||||
\:global(*)
|
\:global(*)
|
||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
|
|
||||||
@ -6,22 +9,22 @@
|
|||||||
scrollbar-color: black lightgray
|
scrollbar-color: black lightgray
|
||||||
|
|
||||||
\:global(body)
|
\:global(body)
|
||||||
font-family: "Fira Sans", "Noto Sans", "Segoe UI", Verdana, sans-serif
|
font-family: "Iosevka", "Shure Tech Mono", "IBM Plex Mono", monospace // TODO import iosevka
|
||||||
font-weight: 300
|
font-weight: 300
|
||||||
overflow-anchor: none
|
overflow-anchor: none
|
||||||
//margin: 0
|
margin: 0
|
||||||
//min-height: 100vh
|
|
||||||
|
|
||||||
\:global(strong)
|
\:global(strong)
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
|
|
||||||
@mixin header
|
@mixin header
|
||||||
border-bottom: 1px solid gray
|
border-bottom: 1px solid white
|
||||||
margin: 0
|
margin: 0
|
||||||
margin-bottom: 0.5em
|
margin-bottom: 0.5em
|
||||||
font-weight: 500
|
font-weight: 500
|
||||||
//a
|
|
||||||
//color: inherit
|
h1
|
||||||
|
text-transform: uppercase
|
||||||
|
|
||||||
\:global(h1)
|
\:global(h1)
|
||||||
@include header
|
@include header
|
||||||
@ -40,14 +43,15 @@
|
|||||||
padding: 0
|
padding: 0
|
||||||
padding-left: 1em
|
padding-left: 1em
|
||||||
|
|
||||||
input, button, select
|
\:global(input), :\global(button), :\global(select)
|
||||||
border-radius: 0
|
border-radius: 0
|
||||||
border: 1px solid gray
|
border: 1px solid gray
|
||||||
padding: 0.5em
|
padding: 0.5em
|
||||||
|
font-family: inherit
|
||||||
|
|
||||||
.controls
|
.controls
|
||||||
input[type=search]
|
input[type=search]
|
||||||
width: 80%
|
width: 70%
|
||||||
.ctrlbar
|
.ctrlbar
|
||||||
> *
|
> *
|
||||||
margin: 0 -1px
|
margin: 0 -1px
|
||||||
@ -63,31 +67,116 @@
|
|||||||
.sliders-ctrl
|
.sliders-ctrl
|
||||||
width: 5em
|
width: 5em
|
||||||
|
|
||||||
.result
|
.enable-advanced-mode
|
||||||
border: 1px solid gray
|
position: fixed
|
||||||
*
|
top: 0.2em
|
||||||
display: block
|
right: 0.2em
|
||||||
.result img, .result video
|
font-size: 1.3em
|
||||||
|
|
||||||
|
nav
|
||||||
|
background: $palette-secondary
|
||||||
|
display: flex
|
||||||
|
justify-content: space-between
|
||||||
|
padding: 1em
|
||||||
|
|
||||||
|
.friendly
|
||||||
|
h1
|
||||||
|
border: none
|
||||||
|
padding-top: 1em
|
||||||
|
|
||||||
|
input[type=search]
|
||||||
width: 100%
|
width: 100%
|
||||||
|
margin: 0
|
||||||
|
padding: 0.5em
|
||||||
|
font-size: 1.5em
|
||||||
|
border-radius: 6px
|
||||||
|
|
||||||
|
.center
|
||||||
|
margin-left: auto
|
||||||
|
margin-right: auto
|
||||||
|
max-width: 40em
|
||||||
|
|
||||||
|
.description
|
||||||
|
opacity: 0.8
|
||||||
|
margin-bottom: 1em
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
button
|
||||||
|
margin-left: 0.5em
|
||||||
|
margin-right: 0.5em
|
||||||
|
padding: 0.5em
|
||||||
|
background: #9a0eea
|
||||||
|
border-radius: 10px
|
||||||
|
font-size: 1.5em
|
||||||
|
color: white
|
||||||
|
|
||||||
|
.header
|
||||||
|
padding-bottom: 2em
|
||||||
|
|
||||||
|
.header
|
||||||
|
padding-left: 1em
|
||||||
|
padding-right: 1em
|
||||||
|
padding-top: 0.5em
|
||||||
|
padding-bottom: 0.5em
|
||||||
|
background: $palette-primary
|
||||||
|
color: white
|
||||||
|
|
||||||
|
p
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.results
|
||||||
|
padding-left: 1em
|
||||||
|
padding-right: 1em
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark)
|
||||||
|
\:global(body)
|
||||||
|
background-color: black
|
||||||
|
color: white
|
||||||
|
|
||||||
|
\:global(input), :\global(button), :\global(select)
|
||||||
|
border-radius: 0
|
||||||
|
border: 1px solid gray
|
||||||
|
padding: 0.5em
|
||||||
|
font-family: inherit
|
||||||
|
background: #222
|
||||||
|
color: white
|
||||||
|
|
||||||
|
.logo
|
||||||
|
height: 1em
|
||||||
|
vertical-align: middle
|
||||||
|
margin-bottom: 6px
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<h1>Meme Search Engine</h1>
|
<nav>
|
||||||
{#if config.n_total}
|
<div class="left">
|
||||||
<p>{config.n_total} items indexed.</p>
|
<NavItem page="search">Search</NavItem>
|
||||||
{/if}
|
</div>
|
||||||
<details>
|
<div class="right">
|
||||||
<summary>Usage tips</summary>
|
<NavItem page="advanced">Advanced</NavItem>
|
||||||
<ul>
|
<NavItem page="about">About</NavItem>
|
||||||
<li>This uses CLIP-like image/text embedding models. In general, search by thinking of what caption your desired image might be given by random people on the internet.</li>
|
<NavItem page="refine">Refine</NavItem>
|
||||||
<li>The model can read text, but not all of it.</li>
|
</div>
|
||||||
<li>In certain circumstances, it may be useful to postfix your query with "meme".</li>
|
</nav>
|
||||||
<li>Capitalization is ignored.</li>
|
|
||||||
<li>Only English is supported. Other languages might work slightly.</li>
|
{#if page === "search" || page === "advanced"}
|
||||||
<li>Sliders are generated from PCA on the index. The human-readable labels are approximate.</li>
|
<div class={"container" + (friendlyMode ? " friendly" : " advanced")}>
|
||||||
<li>Want your own deployment? Use the open-source code on <a href="https://github.com/osmarks/meme-search-engine/">GitHub.</a>.</li>
|
<div class="header">
|
||||||
</ul>
|
{#if friendlyMode}
|
||||||
</details>
|
<div>
|
||||||
<div class="controls">
|
<div class="center">
|
||||||
|
<h1 class="logo-container"><img class="logo" src="./logo.png"> {util.hardConfig.name}</h1>
|
||||||
|
<div class="description">{util.hardConfig.description}</div>
|
||||||
|
<input type="search" placeholder="🔎 Search Memes" on:keydown={handleKey} autofocus bind:value={friendlyModeQuery} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<h1>{util.hardConfig.name}</h1>
|
||||||
|
<p>
|
||||||
|
{#if config.n_total}
|
||||||
|
{config.n_total} items indexed.
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
<div class="controls">
|
||||||
<ul>
|
<ul>
|
||||||
{#each queryTerms as term}
|
{#each queryTerms as term}
|
||||||
<li>
|
<li>
|
||||||
@ -122,58 +211,98 @@
|
|||||||
</select>
|
</select>
|
||||||
<button on:click={runSearch} style="margin-left: auto">Search</button>
|
<button on:click={runSearch} style="margin-left: auto">Search</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if error}
|
|
||||||
<div>{error}</div>
|
|
||||||
{/if}
|
|
||||||
{#if resultPromise}
|
|
||||||
<Loading />
|
|
||||||
{/if}
|
|
||||||
{#if results}
|
|
||||||
{#if displayedResults.length === 0}
|
|
||||||
No results. Wait for index rebuild.
|
|
||||||
{/if}
|
|
||||||
<Masonry bind:refreshLayout={refreshLayout} colWidth="minmax(Min(20em, 100%), 1fr)" items={displayedResults}>
|
|
||||||
{#each displayedResults as result}
|
|
||||||
{#key `${queryCounter}${result.file}`}
|
|
||||||
<div class="result" style={aspectRatio(result)}>
|
|
||||||
<a href={util.getURL(result)}>
|
|
||||||
{#if util.hasFormat(results, result, "VIDEO")}
|
|
||||||
<video controls poster={util.hasFormat(results, result, "jpegh") ? util.thumbnailURL(results, result, "jpegh") : null} preload="metadata" on:loadstart={updateCounter} on:loadedmetadata={redrawGrid} on:loadeddata={redrawGrid}>
|
|
||||||
<source src={util.getURL(result)} />
|
|
||||||
</video>
|
|
||||||
{:else}
|
|
||||||
<picture>
|
|
||||||
{#if util.hasFormat(results, result, "avifl")}
|
|
||||||
<source srcset={util.thumbnailURL(results, result, "avifl") + (util.hasFormat(results, result, "avifh") ? ", " + util.thumbnailURL(results, result, "avifh") + " 2x" : "")} type="image/avif" />
|
|
||||||
{/if}
|
|
||||||
{#if util.hasFormat(results, result, "jpegl")}
|
|
||||||
<source srcset={util.thumbnailURL(results, result, "jpegl") + (util.hasFormat(results, result, "jpegh") ? ", " + util.thumbnailURL(results, result, "jpegh") + " 2x" : "")} type="image/jpeg" />
|
|
||||||
{/if}
|
|
||||||
<img src={util.getURL(result)} on:load={updateCounter} on:error={updateCounter} alt={result[1]}>
|
|
||||||
</picture>
|
|
||||||
{/if}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/key}
|
{/if}
|
||||||
{/each}
|
</div>
|
||||||
</Masonry>
|
|
||||||
|
<div class="results"><SearchResults {resultPromise} {results} {error} {friendlyMode} {queryCounter} /></div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<svelte:window on:resize={redrawGrid} on:scroll={handleScroll}></svelte:window>
|
{#if page === "about"}
|
||||||
|
<About />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if page === "refine"}
|
||||||
|
<QueryRefiner {config} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as util from "./util"
|
import * as util from "./util"
|
||||||
import Loading from "./Loading.svelte"
|
import SearchResults from "./SearchResults.svelte"
|
||||||
import Masonry from "./Masonry.svelte"
|
import QueryRefiner from "./QueryRefiner.svelte"
|
||||||
|
import NavItem from "./NavItem.svelte"
|
||||||
|
import About from "./About.svelte"
|
||||||
|
|
||||||
const chunkSize = 40
|
document.title = util.hardConfig.name
|
||||||
|
|
||||||
|
let page = "search"
|
||||||
|
let friendlyModeQuery = ""
|
||||||
let queryTerms = []
|
let queryTerms = []
|
||||||
let queryCounter = 0
|
let queryCounter = 0
|
||||||
|
|
||||||
let config = {}
|
let config = {}
|
||||||
|
|
||||||
|
const newTextQuery = (content=null) => {
|
||||||
|
queryTerms.push({ type: "text", weight: 1, sign: "+", text: typeof content === "string" ? content : "" })
|
||||||
|
queryTerms = queryTerms
|
||||||
|
}
|
||||||
|
|
||||||
|
let resultPromise
|
||||||
|
let results
|
||||||
|
let error
|
||||||
|
|
||||||
|
const runSearch = async () => {
|
||||||
|
if (!resultPromise) {
|
||||||
|
let args = {
|
||||||
|
"terms": friendlyMode ? [{ text: friendlyModeQuery, weight: 1, sign: "+" }] : queryTerms.filter(x => x.text !== "").map(x => ({ image: x.imageData, text: x.text, embedding: x.embedding, predefined_embedding: x.predefinedEmbedding, weight: x.weight * { "+": 1, "-": -1 }[x.sign] })),
|
||||||
|
"include_video": true
|
||||||
|
}
|
||||||
|
|
||||||
|
util.sendTelemetry("search", {
|
||||||
|
terms: args.terms.map(x => {
|
||||||
|
if (x.image) {
|
||||||
|
return { type: "image" }
|
||||||
|
} else if (x.text) {
|
||||||
|
return { type: "text", text: x.text }
|
||||||
|
} else if (x.embedding) {
|
||||||
|
return { type: "embedding" }
|
||||||
|
} else if (x.predefined_embedding) {
|
||||||
|
return { type: "predefined_embedding", embedding: x.predefined_embedding }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
queryCounter += 1
|
||||||
|
resultPromise = util.doQuery(args).then(res => {
|
||||||
|
error = null
|
||||||
|
results = res
|
||||||
|
resultPromise = null
|
||||||
|
}).catch(e => { error = e; resultPromise = null; console.log("error", e) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseQueryString = queryStringParams => {
|
||||||
|
if (queryStringParams.get("q") && queryTerms.length === 0) {
|
||||||
|
newTextQuery(queryStringParams.get("q"))
|
||||||
|
friendlyModeQuery = queryStringParams.get("q")
|
||||||
|
runSearch()
|
||||||
|
}
|
||||||
|
if (queryStringParams.get("e") && queryTerms.length === 0) {
|
||||||
|
const binaryData = atob(queryStringParams.get("e").replace(/\-/g, "+").replace(/_/g, "/"))
|
||||||
|
const uint16s = new Uint16Array(new Uint8Array(binaryData.split('').map(c => c.charCodeAt(0))).buffer)
|
||||||
|
queryTerms.push({ type: "embedding", weight: 1, sign: "+", embedding: Array.from(uint16s).map(decodeFloat16) })
|
||||||
|
friendlyMode = false
|
||||||
|
runSearch()
|
||||||
|
}
|
||||||
|
if (queryStringParams.get("page")) {
|
||||||
|
page = queryStringParams.get("page")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
util.router.subscribe(parseQueryString)
|
||||||
|
|
||||||
|
$: friendlyMode = page === "search"
|
||||||
|
|
||||||
util.serverConfig.subscribe(x => {
|
util.serverConfig.subscribe(x => {
|
||||||
config = x
|
config = x
|
||||||
})
|
})
|
||||||
@ -198,8 +327,6 @@
|
|||||||
return `${snd}/${fst}`
|
return `${snd}/${fst}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const aspectRatio = result => result[4] ? `aspect-ratio: ${result[4][0]}/${result[4][1]}` : null
|
|
||||||
|
|
||||||
const decodeFloat16 = uint16 => {
|
const decodeFloat16 = uint16 => {
|
||||||
const sign = (uint16 & 0x8000) ? -1 : 1
|
const sign = (uint16 & 0x8000) ? -1 : 1
|
||||||
const exponent = (uint16 & 0x7C00) >> 10
|
const exponent = (uint16 & 0x7C00) >> 10
|
||||||
@ -215,75 +342,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const focusEl = el => el.focus()
|
const focusEl = el => el.focus()
|
||||||
const newTextQuery = (content=null) => {
|
|
||||||
queryTerms.push({ type: "text", weight: 1, sign: "+", text: typeof content === "string" ? content : "" })
|
|
||||||
queryTerms = queryTerms
|
|
||||||
}
|
|
||||||
const removeTerm = term => {
|
const removeTerm = term => {
|
||||||
queryTerms = queryTerms.filter(x => x !== term)
|
queryTerms = queryTerms.filter(x => x !== term)
|
||||||
}
|
}
|
||||||
|
|
||||||
let refreshLayout
|
|
||||||
let heightThreshold
|
|
||||||
let error
|
|
||||||
let pendingImageLoads
|
|
||||||
const recomputeScroll = () => {
|
|
||||||
const maxOffsets = new Map()
|
|
||||||
for (const el of document.querySelectorAll(".result")) {
|
|
||||||
if (el.getAttribute("data-h")) { // layouted
|
|
||||||
const rect = el.getBoundingClientRect()
|
|
||||||
maxOffsets.set(rect.left, Math.max(maxOffsets.get(rect.left) || 0, rect.top))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
heightThreshold = Math.min(...maxOffsets.values())
|
|
||||||
console.log(heightThreshold, pendingImageLoads)
|
|
||||||
}
|
|
||||||
const redrawGrid = async () => {
|
|
||||||
if (refreshLayout) {
|
|
||||||
refreshLayout()
|
|
||||||
await recomputeScroll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let resultPromise
|
|
||||||
let results
|
|
||||||
let displayedResults = []
|
|
||||||
const runSearch = async () => {
|
|
||||||
if (!resultPromise) {
|
|
||||||
let args = {
|
|
||||||
"terms": queryTerms.filter(x => x.text !== "").map(x => ({ image: x.imageData, text: x.text, embedding: x.embedding, predefined_embedding: x.predefinedEmbedding, weight: x.weight * { "+": 1, "-": -1 }[x.sign] })),
|
|
||||||
"include_video": true
|
|
||||||
}
|
|
||||||
queryCounter += 1
|
|
||||||
resultPromise = util.doQuery(args).then(res => {
|
|
||||||
error = null
|
|
||||||
results = res
|
|
||||||
resultPromise = null
|
|
||||||
displayedResults = []
|
|
||||||
pendingImageLoads = 0
|
|
||||||
for (let i = 0; i < chunkSize; i++) {
|
|
||||||
if (i >= results.matches.length) break
|
|
||||||
displayedResults.push(results.matches[i])
|
|
||||||
pendingImageLoads += 1
|
|
||||||
}
|
|
||||||
redrawGrid()
|
|
||||||
}).catch(e => { error = e; resultPromise = null })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleScroll = () => {
|
|
||||||
if (window.scrollY + window.innerHeight >= heightThreshold && pendingImageLoads === 0) {
|
|
||||||
recomputeScroll()
|
|
||||||
if (window.scrollY + window.innerHeight < heightThreshold) return;
|
|
||||||
let init = displayedResults.length
|
|
||||||
for (let i = 0; i < chunkSize; i++) {
|
|
||||||
if (init + i >= results.matches.length) break
|
|
||||||
displayedResults.push(results.matches[init + i])
|
|
||||||
pendingImageLoads += 1
|
|
||||||
}
|
|
||||||
displayedResults = displayedResults
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKey = ev => {
|
const handleKey = ev => {
|
||||||
if (ev.key === "Enter") {
|
if (ev.key === "Enter") {
|
||||||
runSearch()
|
runSearch()
|
||||||
@ -295,7 +357,6 @@
|
|||||||
const pickFile = () => {
|
const pickFile = () => {
|
||||||
input.oninput = ev => {
|
input.oninput = ev => {
|
||||||
currentFile = ev.target.files[0]
|
currentFile = ev.target.files[0]
|
||||||
console.log(currentFile)
|
|
||||||
if (currentFile) {
|
if (currentFile) {
|
||||||
let reader = new FileReader()
|
let reader = new FileReader()
|
||||||
reader.readAsDataURL(currentFile)
|
reader.readAsDataURL(currentFile)
|
||||||
@ -309,22 +370,4 @@
|
|||||||
}
|
}
|
||||||
input.click()
|
input.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCounter = () => {
|
|
||||||
console.log("redraw")
|
|
||||||
pendingImageLoads -= 1
|
|
||||||
redrawGrid()
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryStringParams = new URLSearchParams(window.location.search)
|
|
||||||
if (queryStringParams.get("q")) {
|
|
||||||
newTextQuery(queryStringParams.get("q"))
|
|
||||||
runSearch()
|
|
||||||
}
|
|
||||||
if (queryStringParams.get("e")) {
|
|
||||||
const binaryData = atob(queryStringParams.get("e").replace(/\-/g, "+").replace(/_/g, "/"))
|
|
||||||
const uint16s = new Uint16Array(new Uint8Array(binaryData.split('').map(c => c.charCodeAt(0))).buffer)
|
|
||||||
queryTerms.push({ type: "embedding", weight: 1, sign: "+", embedding: Array.from(uint16s).map(decodeFloat16) })
|
|
||||||
runSearch()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
.spinner
|
.spinner
|
||||||
color: black
|
color: inherit
|
||||||
|
text-align: center
|
||||||
|
padding-top: 0.5em
|
||||||
|
|
||||||
.spinner:before
|
.spinner:before
|
||||||
animation: textSpinner 0.8s linear infinite
|
animation: textSpinner 0.8s linear infinite
|
||||||
@ -33,4 +35,4 @@
|
|||||||
export let operation = "Loading"
|
export let operation = "Loading"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span class="spinner">{operation}</span>
|
<div class="spinner">{operation}</div>
|
||||||
|
@ -86,7 +86,7 @@ const calcGrid = async (_masonryArr) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$: if(masonryElement) {
|
$: if(masonryElement && gridGap) {
|
||||||
calcGrid([masonryElement])
|
calcGrid([masonryElement])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,8 +107,6 @@ $: if(items) { // update if items are changed
|
|||||||
grid-template-rows: masonry;
|
grid-template-rows: masonry;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
grid-gap: var(--grid-gap);
|
grid-gap: var(--grid-gap);
|
||||||
padding: var(--grid-gap);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
:global(.__grid--masonry > *) {
|
:global(.__grid--masonry > *) {
|
||||||
align-self: start
|
align-self: start
|
||||||
|
25
clipfront2/src/NavItem.svelte
Normal file
25
clipfront2/src/NavItem.svelte
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<style lang="sass">
|
||||||
|
@use 'common' as *
|
||||||
|
|
||||||
|
a
|
||||||
|
text-decoration: none
|
||||||
|
font-size: 1.5em
|
||||||
|
display: inline-block
|
||||||
|
padding-right: 0.25em
|
||||||
|
padding-left: 0.25em
|
||||||
|
color: white
|
||||||
|
|
||||||
|
.active
|
||||||
|
font-style: italic
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<a href={util.router.urlForPage(page)} class={$currentPage === page ? "active" : ""} on:click={util.router.handleClick}><slot /></a>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as util from "./util"
|
||||||
|
import { derived } from "svelte/store"
|
||||||
|
|
||||||
|
let currentPage = derived(util.router, x => x.get("page"))
|
||||||
|
|
||||||
|
export let page
|
||||||
|
</script>
|
78
clipfront2/src/QueryRefiner.svelte
Normal file
78
clipfront2/src/QueryRefiner.svelte
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<style lang="sass">
|
||||||
|
.candidate-images
|
||||||
|
height: 15vh
|
||||||
|
display: flex
|
||||||
|
|
||||||
|
.candidate
|
||||||
|
margin-top: 1em
|
||||||
|
.candidate-images
|
||||||
|
margin-top: 1em
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{#each candidates as candidate}
|
||||||
|
<div class="candidate">
|
||||||
|
<button on:click={select(candidate)}>Select {candidate.i + 1}</button>
|
||||||
|
<div class="candidate-images">
|
||||||
|
{#if candidate.results}
|
||||||
|
{#each candidate.results.matches as result}
|
||||||
|
<ResultImage {result} results={candidate.results} updateCounter={null} redrawGrid={null} constrainBy="height" />
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<svelte:window on:keydown={handleKey} />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as util from "./util"
|
||||||
|
import ResultImage from "./ResultImage.svelte"
|
||||||
|
|
||||||
|
export let config
|
||||||
|
|
||||||
|
const d_emb = 1152
|
||||||
|
|
||||||
|
const vecSum = (xs, ys) => xs.map((x, i) => x + ys[i])
|
||||||
|
const vecZero = d => new Array(d).fill(0)
|
||||||
|
const vecScale = (xs, s) => xs.map(x => x * s)
|
||||||
|
|
||||||
|
const boxMuller = () => {
|
||||||
|
let x = Math.random()
|
||||||
|
let y = Math.random()
|
||||||
|
return Math.sqrt(-2.0 * Math.log(x)) * Math.cos(2.0 * Math.PI * y)
|
||||||
|
}
|
||||||
|
|
||||||
|
const randn = (d, sigma) => Array.from({ length: d }, () => boxMuller() * sigma)
|
||||||
|
|
||||||
|
const K = 2
|
||||||
|
let candidates = []
|
||||||
|
|
||||||
|
const select = async candidate => {
|
||||||
|
candidates = []
|
||||||
|
const direction = randn(d_emb, 1 / d_emb)
|
||||||
|
for (let i = -K; i <= K; i++) {
|
||||||
|
const newV = vecSum(vecScale(direction, i / K), candidate.vector)
|
||||||
|
candidates.push({ vector: newV, results: null, i: i + K })
|
||||||
|
}
|
||||||
|
await Promise.all(candidates.map(async x => {
|
||||||
|
const queryResult = await util.doQuery({ terms: [{ embedding: x.vector, weight: 1, sign: "+" }], include_video: false, k: 100 })
|
||||||
|
x.results = queryResult
|
||||||
|
x.results.matches = x.results.matches.slice(0, 10)
|
||||||
|
}))
|
||||||
|
candidates = candidates
|
||||||
|
console.log(candidates)
|
||||||
|
}
|
||||||
|
|
||||||
|
select({ vector: randn(d_emb, 1 / d_emb) })
|
||||||
|
|
||||||
|
const handleKey = ev => {
|
||||||
|
const num = parseInt(ev.key)
|
||||||
|
if (num && num >= 1 && num <= (2 * K + 1)) {
|
||||||
|
select(candidates[num - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
53
clipfront2/src/ResultImage.svelte
Normal file
53
clipfront2/src/ResultImage.svelte
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<style lang="sass">
|
||||||
|
.ch
|
||||||
|
height: 100%
|
||||||
|
img, video, picture, a
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.cw
|
||||||
|
width: 100%
|
||||||
|
img, video, picture, a
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
*
|
||||||
|
display: block
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div style={aspectRatio(result)} class={constrainBy === "width" ? " cw" : "ch"}>
|
||||||
|
<a href={util.getURL(result)} on:click={() => interact("click")} on:mousedown={() => interact("mousedown")}>
|
||||||
|
{#if util.hasFormat(results, result, "VIDEO")}
|
||||||
|
<video controls poster={util.hasFormat(results, result, "jpegh") ? util.thumbnailURL(results, result, "jpegh") : null} preload="metadata" on:loadstart={updateCounter} on:loadedmetadata={redrawGrid} on:loadeddata={redrawGrid}>
|
||||||
|
<source src={util.getURL(result)} />
|
||||||
|
</video>
|
||||||
|
{:else}
|
||||||
|
<picture>
|
||||||
|
{#if util.hasFormat(results, result, "avifl")}
|
||||||
|
<source srcset={util.thumbnailURL(results, result, "avifl") + (util.hasFormat(results, result, "avifh") ? ", " + util.thumbnailURL(results, result, "avifh") + " 2x" : "")} type="image/avif" />
|
||||||
|
{/if}
|
||||||
|
{#if util.hasFormat(results, result, "jpegl")}
|
||||||
|
<source srcset={util.thumbnailURL(results, result, "jpegl") + (util.hasFormat(results, result, "jpegh") ? ", " + util.thumbnailURL(results, result, "jpegh") + " 2x" : "")} type="image/jpeg" />
|
||||||
|
{/if}
|
||||||
|
<img src={util.getURL(result)} on:load={updateCounter} on:error={updateCounter} alt={result[1]}>
|
||||||
|
</picture>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as util from "./util"
|
||||||
|
|
||||||
|
export let result
|
||||||
|
export let results
|
||||||
|
export let updateCounter
|
||||||
|
export let redrawGrid
|
||||||
|
export let constrainBy = "width"
|
||||||
|
|
||||||
|
const interact = type => {
|
||||||
|
util.sendTelemetry("interact", {
|
||||||
|
type,
|
||||||
|
result: result[1]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const aspectRatio = result => result[4] ? `aspect-ratio: ${result[4][0]}/${result[4][1]}` : null
|
||||||
|
</script>
|
124
clipfront2/src/SearchResults.svelte
Normal file
124
clipfront2/src/SearchResults.svelte
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<style lang="sass">
|
||||||
|
@use 'common' as *
|
||||||
|
|
||||||
|
.result
|
||||||
|
border: 1px solid $palette-primary
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
|
.result img, .result video
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.advanced
|
||||||
|
margin-top: 1em
|
||||||
|
|
||||||
|
.friendly
|
||||||
|
padding-top: 1em
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{#if error}
|
||||||
|
<div class="error">{error}</div>
|
||||||
|
{/if}
|
||||||
|
{#if resultPromise}
|
||||||
|
<Loading />
|
||||||
|
{/if}
|
||||||
|
{#if results}
|
||||||
|
<div class={friendlyMode ? "friendly" : "advanced"}>
|
||||||
|
<Masonry bind:refreshLayout={refreshLayout} colWidth={`minmax(Min(${friendlyMode ? "30em" : "20em"}, 100%), 1fr)`} items={displayedResults} gridGap={friendlyMode ? "1em" : "0.5em"}>
|
||||||
|
{#each displayedResults as result}
|
||||||
|
{#key `${queryCounter}${result.file}`}
|
||||||
|
<div class="result">
|
||||||
|
<ResultImage {result} {results} {updateCounter} {redrawGrid} constrainBy="width" />
|
||||||
|
</div>
|
||||||
|
{/key}
|
||||||
|
{/each}
|
||||||
|
</Masonry>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<svelte:window on:resize={redrawGrid} on:scroll={handleScroll}></svelte:window>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { tick } from "svelte"
|
||||||
|
|
||||||
|
import Loading from "./Loading.svelte"
|
||||||
|
import Masonry from "./Masonry.svelte"
|
||||||
|
import ResultImage from "./ResultImage.svelte"
|
||||||
|
import * as util from "./util.js"
|
||||||
|
|
||||||
|
let refreshLayout
|
||||||
|
|
||||||
|
export let results
|
||||||
|
export let resultPromise
|
||||||
|
export let error
|
||||||
|
export let friendlyMode
|
||||||
|
export let queryCounter
|
||||||
|
|
||||||
|
const chunkSize = 40
|
||||||
|
|
||||||
|
let displayedResults = []
|
||||||
|
|
||||||
|
let heightThreshold
|
||||||
|
let pendingImageLoads
|
||||||
|
const recomputeScroll = () => {
|
||||||
|
const maxOffsets = new Map()
|
||||||
|
for (const el of document.querySelectorAll(".result")) {
|
||||||
|
if (el.getAttribute("data-h")) { // layouted
|
||||||
|
const rect = el.getBoundingClientRect()
|
||||||
|
maxOffsets.set(rect.left, Math.max(maxOffsets.get(rect.left) || 0, rect.top))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
heightThreshold = Math.min(...maxOffsets.values())
|
||||||
|
}
|
||||||
|
export const redrawGrid = async () => {
|
||||||
|
if (refreshLayout) {
|
||||||
|
refreshLayout()
|
||||||
|
await recomputeScroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateCounter = () => {
|
||||||
|
pendingImageLoads -= 1
|
||||||
|
redrawGrid()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
if (window.scrollY + window.innerHeight >= heightThreshold && pendingImageLoads === 0) {
|
||||||
|
recomputeScroll()
|
||||||
|
if (window.scrollY + window.innerHeight < heightThreshold) return;
|
||||||
|
let init = displayedResults.length
|
||||||
|
for (let i = 0; i < chunkSize; i++) {
|
||||||
|
if (init + i >= results.matches.length) break
|
||||||
|
displayedResults.push(results.matches[init + i])
|
||||||
|
pendingImageLoads += 1
|
||||||
|
}
|
||||||
|
if (init !== displayedResults.length) {
|
||||||
|
util.sendTelemetry("scroll", {
|
||||||
|
results: results.matches.length,
|
||||||
|
displayed: displayedResults.length
|
||||||
|
})
|
||||||
|
displayedResults = displayedResults
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastResults
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (results && results !== lastResults) {
|
||||||
|
displayedResults = []
|
||||||
|
pendingImageLoads = 0
|
||||||
|
for (let i = 0; i < chunkSize; i++) {
|
||||||
|
if (i >= results.matches.length) break
|
||||||
|
displayedResults.push(results.matches[i])
|
||||||
|
pendingImageLoads += 1
|
||||||
|
}
|
||||||
|
redrawGrid()
|
||||||
|
lastResults = results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
let _ = friendlyMode
|
||||||
|
tick().then(() => redrawGrid())
|
||||||
|
}
|
||||||
|
</script>
|
@ -7,7 +7,7 @@ esbuild
|
|||||||
.build({
|
.build({
|
||||||
entryPoints: [path.join(__dirname, "app.js")],
|
entryPoints: [path.join(__dirname, "app.js")],
|
||||||
bundle: true,
|
bundle: true,
|
||||||
minify: true,
|
minify: false,
|
||||||
outfile: path.join(__dirname, "../static/app.js"),
|
outfile: path.join(__dirname, "../static/app.js"),
|
||||||
plugins: [sveltePlugin({
|
plugins: [sveltePlugin({
|
||||||
preprocess: {
|
preprocess: {
|
||||||
@ -17,7 +17,8 @@ esbuild
|
|||||||
loader: {
|
loader: {
|
||||||
".woff": "file",
|
".woff": "file",
|
||||||
".woff2": "file",
|
".woff2": "file",
|
||||||
".ttf": "file"
|
".ttf": "file",
|
||||||
|
".png": "file"
|
||||||
},
|
},
|
||||||
logLevel: "info",
|
logLevel: "info",
|
||||||
watch: process.argv.join(" ").includes("watch")
|
watch: process.argv.join(" ").includes("watch")
|
||||||
|
4
clipfront2/src/common.sass
Normal file
4
clipfront2/src/common.sass
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@use 'sass:color'
|
||||||
|
|
||||||
|
$palette-primary: #3f9b0b
|
||||||
|
$palette-secondary: #033500
|
@ -1,15 +1,69 @@
|
|||||||
import * as config from "../../frontend_config.json"
|
import * as config from "../../frontend_config.json"
|
||||||
import { writable } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
|
|
||||||
export const getURL = x => config.image_path + x[1]
|
export const getURL = x => config.image_path + x[1]
|
||||||
|
|
||||||
export const doQuery = args => fetch(config.backend_url, {
|
export const hardConfig = config
|
||||||
|
|
||||||
|
export const router = writable(new URLSearchParams(window.location.search))
|
||||||
|
|
||||||
|
window.addEventListener("popstate", ev => {
|
||||||
|
router.set(new URLSearchParams(window.location.search))
|
||||||
|
})
|
||||||
|
|
||||||
|
router.handleClick = ev => {
|
||||||
|
history.pushState({}, "", ev.target.getAttribute("href"))
|
||||||
|
ev.preventDefault()
|
||||||
|
router.set(new URLSearchParams(window.location.search))
|
||||||
|
}
|
||||||
|
|
||||||
|
router.urlForPage = page => {
|
||||||
|
let queryStringParams = new URLSearchParams(window.location.search)
|
||||||
|
queryStringParams.set("page", page)
|
||||||
|
return window.location.origin + "?" + queryStringParams.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const telemetryEnabled = writable(true)
|
||||||
|
if (localStorage.telemetryEnabled === "false") {
|
||||||
|
telemetryEnabled.set(false)
|
||||||
|
}
|
||||||
|
telemetryEnabled.subscribe(x => {
|
||||||
|
localStorage.telemetryEnabled = x ? "true" : "false"
|
||||||
|
})
|
||||||
|
|
||||||
|
const randomString = () => Math.random().toString(36).substring(2, 15)
|
||||||
|
|
||||||
|
localStorage.correlationId = localStorage.correlationId ?? randomString()
|
||||||
|
let correlationId = localStorage.correlationId
|
||||||
|
let instanceId = randomString()
|
||||||
|
|
||||||
|
export const sendTelemetry = async (event, data) => {
|
||||||
|
if (!get(telemetryEnabled)) return
|
||||||
|
if (!config.telemetry_endpoint) return
|
||||||
|
navigator.sendBeacon(config.telemetry_endpoint, JSON.stringify({
|
||||||
|
correlationId,
|
||||||
|
instanceId,
|
||||||
|
event,
|
||||||
|
data,
|
||||||
|
page: get(router).get("page")
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const doQuery = async args => {
|
||||||
|
const res = await fetch(config.backend_url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify(args)
|
body: JSON.stringify(args)
|
||||||
}).then(x => x.json())
|
})
|
||||||
|
try {
|
||||||
|
return await res.clone().json()
|
||||||
|
} catch(e) {
|
||||||
|
throw new Error(res.status + " " + await res.text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const hasFormat = (results, result, format) => {
|
export const hasFormat = (results, result, format) => {
|
||||||
return (results.formats.indexOf(format) != -1) && ((result[3] & (1 << results.formats.indexOf(format))) != 0)
|
return (results.formats.indexOf(format) != -1) && ((result[3] & (1 << results.formats.indexOf(format))) != 0)
|
||||||
|
@ -3,11 +3,12 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
|
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
|
||||||
<meta name="description" content="osmarks.net meme library semantic search via CLIP; enhanced query UI edition">
|
<meta name="description" content="Organizing the world's memes.">
|
||||||
|
|
||||||
<title>Meme Search Engine</title>
|
<title>Meme Search Engine</title>
|
||||||
</style>
|
</style>
|
||||||
<link rel="stylesheet" href="app.css">
|
<link rel="stylesheet" href="app.css">
|
||||||
|
<link rel="icon" type="image/png" href="logo.png">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="app.js"></script>
|
<script src="app.js"></script>
|
||||||
|
BIN
clipfront2/static/logo.png
Normal file
BIN
clipfront2/static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"backend_url": "http://localhost:5601/",
|
"backend_url": "http://localhost:5601/",
|
||||||
"image_path": "",
|
"image_path": "",
|
||||||
"thumb_path": null
|
"thumb_path": null,
|
||||||
|
"description": "Organizing the world's memes.",
|
||||||
|
"name": "Nooscope",
|
||||||
|
"telemetry_endpoint": "/telemetry"
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,17 @@ import time
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
async def fetch_list_seg(sess, list_url, query):
|
async def fetch_list_seg(sess, list_url, query):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
async with sess.get(list_url + ".json", params=query) as res:
|
async with sess.get(list_url + ".json", params=query) as res:
|
||||||
if rate_limit := res.headers.get("x-ratelimit-remaining"):
|
if rate_limit := res.headers.get("x-ratelimit-remaining"):
|
||||||
rl = float(rate_limit)
|
rl = float(rate_limit)
|
||||||
if rl <= 5.0:
|
if rl <= 5.0:
|
||||||
await asyncio.sleep(float(res.headers["x-ratelimit-reset"]))
|
await asyncio.sleep(float(res.headers["x-ratelimit-reset"]))
|
||||||
return await res.json()
|
return await res.json()
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
print("timeout")
|
||||||
|
|
||||||
async def fetch_past(sess, list_url, n):
|
async def fetch_past(sess, list_url, n):
|
||||||
after = None
|
after = None
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"clip_server": "http://100.64.0.10:1708",
|
"clip_server": "http://100.64.0.10:1708",
|
||||||
"db_path": "data.sqlite3",
|
"db_path": "data.sqlite3",
|
||||||
"port": 1707,
|
"port": 1707,
|
||||||
"files": "/data/public/memes-or-something/",
|
"files": "./mse-test-db-small",
|
||||||
"enable_ocr": false,
|
"enable_ocr": false,
|
||||||
"thumbs_path": "./thumbtemp",
|
"thumbs_path": "./thumbtemp",
|
||||||
"enable_thumbs": false
|
"enable_thumbs": false
|
||||||
|
@ -43,7 +43,7 @@ with torch.inference_mode():
|
|||||||
reconstructions = model(batch).float()
|
reconstructions = model(batch).float()
|
||||||
|
|
||||||
feature_frequencies = model.reset_counters()
|
feature_frequencies = model.reset_counters()
|
||||||
features = model.up_proj.weight.cpu().numpy()
|
features = model.down_proj.weight.cpu().numpy()
|
||||||
|
|
||||||
meme_search_backend = "http://localhost:1707/"
|
meme_search_backend = "http://localhost:1707/"
|
||||||
memes_url = "https://i.osmarks.net/memes-or-something/"
|
memes_url = "https://i.osmarks.net/memes-or-something/"
|
||||||
@ -54,8 +54,8 @@ def emb_url(embedding):
|
|||||||
|
|
||||||
async def get_exemplars():
|
async def get_exemplars():
|
||||||
async with aiohttp.ClientSession():
|
async with aiohttp.ClientSession():
|
||||||
for base in tqdm(range(0, len(features), retrieve_batch_size)):
|
for base in tqdm(range(0, features.shape[1], retrieve_batch_size)):
|
||||||
chunk = features[base:base + retrieve_batch_size]
|
chunk = features[:, base:base + retrieve_batch_size].T
|
||||||
with open(f"feature_dumps/features{base}.html", "w") as f:
|
with open(f"feature_dumps/features{base}.html", "w") as f:
|
||||||
f.write("""<!DOCTYPE html>
|
f.write("""<!DOCTYPE html>
|
||||||
<title>Embeddings SAE Features</title>
|
<title>Embeddings SAE Features</title>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user