mirror of
https://github.com/maunium/stickerpicker.git
synced 2025-07-17 06:23:28 +02:00
Compare commits
2 Commits
master
...
e2fffe70b3
Author | SHA1 | Date | |
---|---|---|---|
e2fffe70b3 | |||
5c87326040 |
@ -1,15 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{yaml,yml,sql,py,sass}]
|
||||
indent_style = space
|
||||
|
||||
[*.sass]
|
||||
indent_size = 2
|
@ -1,13 +0,0 @@
|
||||
variables:
|
||||
GOTOOLCHAIN: local
|
||||
|
||||
build giphy proxy docker:
|
||||
image: docker:stable
|
||||
stage: build
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
script:
|
||||
- docker build -t $CI_REGISTRY_IMAGE/giphyproxy:latest giphyproxy
|
||||
- docker push $CI_REGISTRY_IMAGE/giphyproxy:latest
|
||||
only:
|
||||
- master
|
@ -1,16 +0,0 @@
|
||||
FROM golang:1-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache ca-certificates
|
||||
WORKDIR /build/giphyproxy
|
||||
COPY . /build/giphyproxy
|
||||
ENV CGO_ENABLED=0
|
||||
RUN go build -o /usr/bin/giphyproxy
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=builder /usr/bin/giphyproxy /usr/bin/giphyproxy
|
||||
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
CMD ["/usr/bin/giphyproxy"]
|
@ -1,21 +0,0 @@
|
||||
# The server name to use for the custom mxc:// URIs.
|
||||
# This server name will effectively be a real Matrix server, it just won't implement anything other than media.
|
||||
# You must either set up .well-known delegation from this domain to this program, or proxy the domain directly to this program.
|
||||
server_name: giphy.example.com
|
||||
# Optionally a custom .well-known response. This defaults to `server_name:443` if empty.
|
||||
well_known_response:
|
||||
# Matrix server signing key to make the federation tester pass, same format as synapse's .signing.key file.
|
||||
# You can generate one using `giphyproxy -generate-key`.
|
||||
server_key: CHANGE ME
|
||||
# Should federation authentication be enforced?
|
||||
# If true, requests to the /_matrix/federation/v1/media/download/... endpoint
|
||||
# will check the Authorization: X-Matrix header before redirecting.
|
||||
federation_auth: false
|
||||
|
||||
# Hostname where the proxy should listen on
|
||||
hostname: 0.0.0.0
|
||||
# Port where the proxy should listen on
|
||||
port: 8008
|
||||
|
||||
# Redirect destination. This can be changed to serve a different format.
|
||||
destination: https://i.giphy.com/%s.webp
|
@ -1,28 +0,0 @@
|
||||
module go.mau.fi/stickerpicker/giphyproxy
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.4
|
||||
|
||||
require (
|
||||
go.mau.fi/util v0.8.8
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
maunium.net/go/mautrix v0.24.2-0.20250629161127-7a7d7f70ef92
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/rs/zerolog v1.34.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
)
|
@ -1,54 +0,0 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c=
|
||||
go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4=
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
maunium.net/go/mautrix v0.24.2-0.20250629161127-7a7d7f70ef92 h1:j/QuGIVm5/KPlxMgbViphre7shSwnI7LIjNeiLDSnL4=
|
||||
maunium.net/go/mautrix v0.24.2-0.20250629161127-7a7d7f70ef92/go.mod h1:Xy6o+pXmbqmgWsUWh15EQ1eozjC+k/VT/7kloByv9PI=
|
@ -1,76 +0,0 @@
|
||||
// maunium-stickerpicker - A fast and simple Matrix sticker picker widget.
|
||||
// Copyright (C) 2024 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"go.mau.fi/util/exerrors"
|
||||
"gopkg.in/yaml.v3"
|
||||
"maunium.net/go/mautrix/federation"
|
||||
"maunium.net/go/mautrix/mediaproxy"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
mediaproxy.BasicConfig `yaml:",inline"`
|
||||
mediaproxy.ServerConfig `yaml:",inline"`
|
||||
Destination string `yaml:"destination"`
|
||||
}
|
||||
|
||||
var configPath = flag.String("config", "config.yaml", "config file path")
|
||||
var generateServerKey = flag.Bool("generate-key", false, "generate a new server key and exit")
|
||||
|
||||
var giphyIDRegex = regexp.MustCompile(`^[a-zA-Z0-9-_]+$`)
|
||||
var destination = "https://i.giphy.com/%s.webp"
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *generateServerKey {
|
||||
fmt.Println(federation.GenerateSigningKey().SynapseString())
|
||||
} else {
|
||||
cfgFile := exerrors.Must(os.ReadFile(*configPath))
|
||||
var cfg Config
|
||||
exerrors.PanicIfNotNil(yaml.Unmarshal(cfgFile, &cfg))
|
||||
mp := exerrors.Must(mediaproxy.NewFromConfig(cfg.BasicConfig, getMedia))
|
||||
mp.KeyServer.Version.Name = "mautrix-go + maunium-stickerpicker giphy proxy"
|
||||
if cfg.Destination != "" {
|
||||
destination = cfg.Destination
|
||||
}
|
||||
exerrors.PanicIfNotNil(mp.Listen(cfg.ServerConfig))
|
||||
}
|
||||
}
|
||||
|
||||
func getMedia(_ context.Context, id string, _ map[string]string) (response mediaproxy.GetMediaResponse, err error) {
|
||||
// This is not related to giphy, but random cats are always fun
|
||||
if id == "cat" {
|
||||
return &mediaproxy.GetMediaResponseURL{
|
||||
URL: "https://cataas.com/cat",
|
||||
ExpiresAt: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
if !giphyIDRegex.MatchString(id) {
|
||||
return nil, mediaproxy.ErrInvalidMediaIDSyntax
|
||||
}
|
||||
return &mediaproxy.GetMediaResponseURL{
|
||||
URL: fmt.Sprintf(destination, id),
|
||||
}, nil
|
||||
}
|
1
setup.py
1
setup.py
@ -50,6 +50,5 @@ setuptools.setup(
|
||||
entry_points={"console_scripts": [
|
||||
"sticker-import=sticker.stickerimport:cmd",
|
||||
"sticker-pack=sticker.pack:cmd",
|
||||
"sticker-download-thumbnails=sticker.download_thumbnails:cmd",
|
||||
]},
|
||||
)
|
||||
|
@ -1,58 +0,0 @@
|
||||
# maunium-stickerpicker - A fast and simple Matrix sticker picker widget.
|
||||
# Copyright (C) 2025 Tulir Asokan
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from yarl import URL
|
||||
|
||||
from .lib import matrix, util
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--config",
|
||||
help="Path to JSON file with Matrix homeserver and access_token",
|
||||
type=str, default="config.json", metavar="file")
|
||||
parser.add_argument("path", help="Path to the sticker pack JSON file", type=str)
|
||||
|
||||
|
||||
async def main(args: argparse.Namespace) -> None:
|
||||
await matrix.load_config(args.config)
|
||||
with util.open_utf8(args.path) as pack_file:
|
||||
pack = json.load(pack_file)
|
||||
print(f"Loaded existing pack meta from {args.path}")
|
||||
|
||||
stickers_data: Dict[str, bytes] = {}
|
||||
async with ClientSession() as sess:
|
||||
for sticker in pack["stickers"]:
|
||||
dl_url = URL(matrix.homeserver_url) / "_matrix/client/v1/media/download" / sticker["url"].removeprefix("mxc://")
|
||||
print("Downloading", sticker["url"])
|
||||
async with sess.get(dl_url, headers={"Authorization": f"Bearer {matrix.access_token}"}) as resp:
|
||||
resp.raise_for_status()
|
||||
stickers_data[sticker["url"]] = await resp.read()
|
||||
|
||||
print("All stickers downloaded, generating thumbnails...")
|
||||
util.add_thumbnails(pack["stickers"], stickers_data, Path(args.path).parent)
|
||||
print("Done!")
|
||||
|
||||
def cmd():
|
||||
asyncio.run(main(parser.parse_args()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cmd()
|
@ -59,7 +59,7 @@ async def load_config(path: str) -> None:
|
||||
print("Matrix config file not found. Please enter your homeserver and access token.")
|
||||
homeserver_url = input("Homeserver URL: ")
|
||||
access_token = input("Access token: ")
|
||||
whoami_url = URL(homeserver_url) / "_matrix" / "client" / "v3" / "account" / "whoami"
|
||||
whoami_url = URL(homeserver_url) / "_matrix" / "client" / "r0" / "account" / "whoami"
|
||||
if whoami_url.scheme not in ("https", "http"):
|
||||
whoami_url = whoami_url.with_scheme("https")
|
||||
user_id = await whoami(whoami_url, access_token)
|
||||
@ -67,11 +67,11 @@ async def load_config(path: str) -> None:
|
||||
json.dump({
|
||||
"homeserver": homeserver_url,
|
||||
"user_id": user_id,
|
||||
"access_token": access_token,
|
||||
"access_token": access_token
|
||||
}, config_file)
|
||||
print(f"Wrote config to {path}")
|
||||
|
||||
upload_url = URL(homeserver_url) / "_matrix" / "media" / "v3" / "upload"
|
||||
upload_url = URL(homeserver_url) / "_matrix" / "media" / "r0" / "upload"
|
||||
|
||||
|
||||
async def whoami(url: URL, access_token: str) -> str:
|
||||
|
@ -17,8 +17,6 @@ from functools import partial
|
||||
from io import BytesIO
|
||||
import os.path
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
from PIL import Image
|
||||
|
||||
@ -26,19 +24,19 @@ from . import matrix
|
||||
|
||||
open_utf8 = partial(open, encoding='UTF-8')
|
||||
|
||||
def convert_image(data: bytes, max_w=256, max_h=256) -> (bytes, int, int):
|
||||
def convert_image(data: bytes) -> (bytes, int, int):
|
||||
image: Image.Image = Image.open(BytesIO(data)).convert("RGBA")
|
||||
new_file = BytesIO()
|
||||
image.save(new_file, "png")
|
||||
w, h = image.size
|
||||
if w > max_w or h > max_h:
|
||||
if w > 256 or h > 256:
|
||||
# Set the width and height to lower values so clients wouldn't show them as huge images
|
||||
if w > h:
|
||||
h = int(h / (w / max_w))
|
||||
w = max_w
|
||||
h = int(h / (w / 256))
|
||||
w = 256
|
||||
else:
|
||||
w = int(w / (h / max_h))
|
||||
h = max_h
|
||||
w = int(w / (h / 256))
|
||||
h = 256
|
||||
return new_file.getvalue(), w, h
|
||||
|
||||
|
||||
@ -80,15 +78,3 @@ def make_sticker(mxc: str, width: int, height: int, size: int,
|
||||
},
|
||||
"msgtype": "m.sticker",
|
||||
}
|
||||
|
||||
|
||||
def add_thumbnails(stickers: List[matrix.StickerInfo], stickers_data: Dict[str, bytes], output_dir: str) -> None:
|
||||
thumbnails = Path(output_dir, "thumbnails")
|
||||
thumbnails.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for sticker in stickers:
|
||||
image_data, _, _ = convert_image(stickers_data[sticker["url"]], 128, 128)
|
||||
|
||||
name = sticker["url"].split("/")[-1]
|
||||
thumbnail_path = thumbnails / name
|
||||
thumbnail_path.write_bytes(image_data)
|
@ -13,7 +13,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
from hashlib import sha256
|
||||
import mimetypes
|
||||
@ -108,11 +107,9 @@ async def main(args: argparse.Namespace) -> None:
|
||||
old_stickers = {sticker["id"]: sticker for sticker in pack["stickers"]}
|
||||
pack["stickers"] = []
|
||||
|
||||
stickers_data: Dict[str, bytes] = {}
|
||||
for file in sorted(os.listdir(args.path)):
|
||||
sticker = await upload_sticker(file, args.path, old_stickers=old_stickers)
|
||||
if sticker:
|
||||
stickers_data[sticker["url"]] = Path(args.path, file).read_bytes()
|
||||
pack["stickers"].append(sticker)
|
||||
|
||||
with util.open_utf8(meta_path, "w") as pack_file:
|
||||
@ -125,8 +122,6 @@ async def main(args: argparse.Namespace) -> None:
|
||||
with util.open_utf8(picker_pack_path, "w") as pack_file:
|
||||
json.dump(pack, pack_file)
|
||||
print(f"Copied pack to {picker_pack_path}")
|
||||
|
||||
util.add_thumbnails(pack["stickers"], stickers_data, args.add_to_index)
|
||||
util.add_to_index(picker_file_name, args.add_to_index)
|
||||
|
||||
|
||||
@ -143,7 +138,7 @@ parser.add_argument("path", help="Path to the sticker pack directory", type=str)
|
||||
|
||||
|
||||
def cmd():
|
||||
asyncio.run(main(parser.parse_args()))
|
||||
asyncio.get_event_loop().run_until_complete(main(parser.parse_args()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -13,7 +13,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from typing import Dict, Tuple
|
||||
from typing import Dict
|
||||
import argparse
|
||||
import asyncio
|
||||
import os.path
|
||||
@ -29,7 +29,7 @@ from telethon.tl.types.messages import StickerSet as StickerSetFull
|
||||
from .lib import matrix, util
|
||||
|
||||
|
||||
async def reupload_document(client: TelegramClient, document: Document) -> Tuple[matrix.StickerInfo, bytes]:
|
||||
async def reupload_document(client: TelegramClient, document: Document) -> matrix.StickerInfo:
|
||||
print(f"Reuploading {document.id}", end="", flush=True)
|
||||
data = await client.download_media(document, file=bytes)
|
||||
print(".", end="", flush=True)
|
||||
@ -37,7 +37,7 @@ async def reupload_document(client: TelegramClient, document: Document) -> Tuple
|
||||
print(".", end="", flush=True)
|
||||
mxc = await matrix.upload(data, "image/png", f"{document.id}.png")
|
||||
print(".", flush=True)
|
||||
return util.make_sticker(mxc, width, height, len(data)), data
|
||||
return util.make_sticker(mxc, width, height, len(data))
|
||||
|
||||
|
||||
def add_meta(document: Document, info: matrix.StickerInfo, pack: StickerSetFull) -> None:
|
||||
@ -56,6 +56,10 @@ def add_meta(document: Document, info: matrix.StickerInfo, pack: StickerSetFull)
|
||||
|
||||
|
||||
async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir: str) -> None:
|
||||
if pack.set.animated:
|
||||
print("Animated stickerpacks are currently not supported")
|
||||
return
|
||||
|
||||
pack_path = os.path.join(output_dir, f"{pack.set.short_name}.json")
|
||||
try:
|
||||
os.mkdir(os.path.dirname(pack_path))
|
||||
@ -75,17 +79,15 @@ async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
stickers_data: Dict[str, bytes] = {}
|
||||
reuploaded_documents: Dict[int, matrix.StickerInfo] = {}
|
||||
for document in pack.documents:
|
||||
try:
|
||||
reuploaded_documents[document.id] = already_uploaded[document.id]
|
||||
print(f"Skipped reuploading {document.id}")
|
||||
except KeyError:
|
||||
reuploaded_documents[document.id], data = await reupload_document(client, document)
|
||||
reuploaded_documents[document.id] = await reupload_document(client, document)
|
||||
# Always ensure the body and telegram metadata is correct
|
||||
add_meta(document, reuploaded_documents[document.id], pack)
|
||||
stickers_data[reuploaded_documents[document.id]["url"]] = data
|
||||
|
||||
for sticker in pack.packs:
|
||||
if not sticker.emoticon:
|
||||
@ -109,7 +111,6 @@ async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir
|
||||
}, pack_file, ensure_ascii=False)
|
||||
print(f"Saved {pack.set.title} as {pack.set.short_name}.json")
|
||||
|
||||
util.add_thumbnails(list(reuploaded_documents.values()), stickers_data, output_dir)
|
||||
util.add_to_index(os.path.basename(pack_path), output_dir)
|
||||
|
||||
|
||||
@ -161,7 +162,7 @@ async def main(args: argparse.Namespace) -> None:
|
||||
|
||||
|
||||
def cmd() -> None:
|
||||
asyncio.run(main(parser.parse_args()))
|
||||
asyncio.get_event_loop().run_until_complete(main(parser.parse_args()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -1,23 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no">
|
||||
<title>Maunium sticker picker</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no">
|
||||
<title>Maunium sticker picker</title>
|
||||
|
||||
<link rel="modulepreload" href="src/widget-api.js"/>
|
||||
<link rel="modulepreload" href="src/frequently-used.js"/>
|
||||
<link rel="modulepreload" href="src/spinner.js"/>
|
||||
<link rel="modulepreload" href="src/giphy.js"/>
|
||||
<link rel="modulepreload" href="lib/htm/preact.js"/>
|
||||
<link rel="preload" href="packs/index.json" as="fetch" type="application/json" crossorigin/>
|
||||
<link rel="modulepreload" href="src/widget-api.js"/>
|
||||
<link rel="modulepreload" href="src/frequently-used.js"/>
|
||||
<link rel="modulepreload" href="src/spinner.js"/>
|
||||
<link rel="modulepreload" href="lib/htm/preact.js"/>
|
||||
<link rel="preload" href="packs/index.json" as="fetch" type="application/json" crossorigin/>
|
||||
|
||||
<link rel="stylesheet" href="style/index.css"/>
|
||||
<link rel="stylesheet" href="style/spinner.css"/>
|
||||
<script src="src/index.js" type="module"></script>
|
||||
<script nomodule>document.body.innerText = "This sticker picker requires modern JavaScript"</script>
|
||||
<link rel="stylesheet" href="style/index.css"/>
|
||||
<link rel="stylesheet" href="style/spinner.css"/>
|
||||
<script src="src/index.js" type="module"></script>
|
||||
<script nomodule>document.body.innerText = "This sticker picker requires modern JavaScript"</script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>This sticker picker requires JavaScript</noscript>
|
||||
<noscript>This sticker picker requires JavaScript</noscript>
|
||||
</body>
|
||||
</html>
|
||||
|
File diff suppressed because one or more lines are too long
@ -11,9 +11,9 @@
|
||||
"sass": "sass --no-source-map --style=compressed style/"
|
||||
},
|
||||
"dependencies": {
|
||||
"esinstall": "^1.1.7",
|
||||
"htm": "^3.1.0",
|
||||
"preact": "^10.5.14",
|
||||
"esinstall": "^1.1.7",
|
||||
"sass": "^1.42.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="534"
|
||||
width="427.20001"
|
||||
viewBox="0 0 27.990145 35"
|
||||
version="1.1"
|
||||
id="svg24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs28" />
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
id="g22"
|
||||
transform="translate(-0.02883895)">
|
||||
<path
|
||||
d="M 4,4 H 24 V 31 H 4 Z"
|
||||
fill="#000000"
|
||||
id="path2"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
id="g16">
|
||||
<path
|
||||
d="M 0,3 H 4 V 32 H 0 Z"
|
||||
fill="#04ff8e"
|
||||
id="path4" />
|
||||
<path
|
||||
d="m 24,11 h 4 v 21 h -4 z"
|
||||
fill="#8e2eff"
|
||||
id="path6" />
|
||||
<path
|
||||
d="m 0,31 h 28 v 4 H 0 Z"
|
||||
fill="#00c5ff"
|
||||
id="path8" />
|
||||
<path
|
||||
d="M 0,0 H 16 V 4 H 0 Z"
|
||||
fill="#fff152"
|
||||
id="path10" />
|
||||
<path
|
||||
d="M 24,8 V 4 H 20 V 0 H 16 V 12 H 28 V 8"
|
||||
fill="#ff5b5b"
|
||||
id="path12" />
|
||||
<path
|
||||
d="m 24,16 v -4 h 4"
|
||||
fill="#551c99"
|
||||
id="path14" />
|
||||
</g>
|
||||
<path
|
||||
d="M 16,0 V 4 H 12"
|
||||
fill="#999131"
|
||||
id="path18" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,54 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="534"
|
||||
width="427.20001"
|
||||
viewBox="0 0 27.990145 35"
|
||||
version="1.1"
|
||||
id="svg24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs28" />
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
id="g22"
|
||||
transform="translate(-0.02883895)">
|
||||
<path
|
||||
d="M 4,4 H 24 V 31 H 4 Z"
|
||||
fill="#000000"
|
||||
id="path2" />
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
id="g16">
|
||||
<path
|
||||
d="M 0,3 H 4 V 32 H 0 Z"
|
||||
fill="#04ff8e"
|
||||
id="path4" />
|
||||
<path
|
||||
d="m 24,11 h 4 v 21 h -4 z"
|
||||
fill="#8e2eff"
|
||||
id="path6" />
|
||||
<path
|
||||
d="m 0,31 h 28 v 4 H 0 Z"
|
||||
fill="#00c5ff"
|
||||
id="path8" />
|
||||
<path
|
||||
d="M 0,0 H 16 V 4 H 0 Z"
|
||||
fill="#fff152"
|
||||
id="path10" />
|
||||
<path
|
||||
d="M 24,8 V 4 H 20 V 0 H 16 V 12 H 28 V 8"
|
||||
fill="#ff5b5b"
|
||||
id="path12" />
|
||||
<path
|
||||
d="m 24,16 v -4 h 4"
|
||||
fill="#551c99"
|
||||
id="path14" />
|
||||
</g>
|
||||
<path
|
||||
d="M 16,0 V 4 H 12"
|
||||
fill="#999131"
|
||||
id="path18" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.6 KiB |
1
web/res/reset.svg
Normal file
1
web/res/reset.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/></svg>
|
After Width: | Height: | Size: 313 B |
107
web/src/giphy.js
107
web/src/giphy.js
@ -1,107 +0,0 @@
|
||||
import {Component, html} from "../lib/htm/preact.js";
|
||||
import * as widgetAPI from "./widget-api.js";
|
||||
import {SearchBox} from "./search-box.js";
|
||||
|
||||
const GIPHY_SEARCH_DEBOUNCE = 1000
|
||||
let GIPHY_API_KEY = "HQku8974Uq5MZn3MZns46kXn2R4GDm75"
|
||||
let GIPHY_MXC_PREFIX = "mxc://giphy.mau.dev/"
|
||||
|
||||
export function giphyIsEnabled() {
|
||||
return GIPHY_API_KEY !== ""
|
||||
}
|
||||
|
||||
export function setGiphyAPIKey(apiKey, mxcPrefix) {
|
||||
GIPHY_API_KEY = apiKey
|
||||
if (mxcPrefix) {
|
||||
GIPHY_MXC_PREFIX = mxcPrefix
|
||||
}
|
||||
}
|
||||
|
||||
export class GiphySearchTab extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
searchTerm: "",
|
||||
gifs: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
}
|
||||
this.handleGifClick = this.handleGifClick.bind(this)
|
||||
this.searchKeyUp = this.searchKeyUp.bind(this)
|
||||
this.updateGifSearchQuery = this.updateGifSearchQuery.bind(this)
|
||||
this.searchTimeout = null
|
||||
}
|
||||
|
||||
async makeGifSearchRequest() {
|
||||
try {
|
||||
const resp = await fetch(`https://api.giphy.com/v1/gifs/search?q=${this.state.searchTerm}&api_key=${GIPHY_API_KEY}`)
|
||||
// TODO handle error responses properly?
|
||||
const data = await resp.json()
|
||||
if (data.data.length === 0) {
|
||||
this.setState({gifs: [], error: "No results"})
|
||||
} else {
|
||||
this.setState({gifs: data.data, error: null})
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({error})
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.searchTimeout)
|
||||
}
|
||||
|
||||
searchKeyUp(event) {
|
||||
if (event.key === "Enter") {
|
||||
clearTimeout(this.searchTimeout)
|
||||
this.makeGifSearchRequest()
|
||||
}
|
||||
}
|
||||
|
||||
updateGifSearchQuery(event) {
|
||||
this.setState({searchTerm: event.target.value})
|
||||
clearTimeout(this.searchTimeout)
|
||||
this.searchTimeout = setTimeout(() => this.makeGifSearchRequest(), GIPHY_SEARCH_DEBOUNCE)
|
||||
}
|
||||
|
||||
handleGifClick(gif) {
|
||||
widgetAPI.sendSticker({
|
||||
"body": gif.title,
|
||||
"info": {
|
||||
"h": +gif.images.original.height,
|
||||
"w": +gif.images.original.width,
|
||||
"size": +gif.images.original.size,
|
||||
"mimetype": "image/webp",
|
||||
},
|
||||
"msgtype": "m.image",
|
||||
"url": GIPHY_MXC_PREFIX + gif.id,
|
||||
|
||||
"id": gif.id,
|
||||
"filename": gif.id + ".webp",
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
// TODO display loading state?
|
||||
return html`
|
||||
<${SearchBox} onInput=${this.updateGifSearchQuery} onKeyUp=${this.searchKeyUp} value=${this.state.searchTerm} placeholder="Find GIFs"/>
|
||||
<div class="pack-list">
|
||||
<section class="stickerpack" id="pack-giphy">
|
||||
<div class="error">
|
||||
${this.state.error}
|
||||
</div>
|
||||
<div class="sticker-list">
|
||||
${this.state.gifs.map((gif) => html`
|
||||
<div class="sticker" onClick=${() => this.handleGifClick(gif)} data-gif-id=${gif.id}>
|
||||
<img src=${gif.images.fixed_height.url} alt=${gif.title} class="visible" data=/>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
<div class="footer powered-by-giphy">
|
||||
<img src="./res/powered-by-giphy.png" alt="Powered by GIPHY"/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
212
web/src/index.js
212
web/src/index.js
@ -13,10 +13,10 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import {html, render, Component} from "../lib/htm/preact.js"
|
||||
import {Spinner} from "./spinner.js"
|
||||
import {SearchBox} from "./search-box.js"
|
||||
import {giphyIsEnabled, GiphySearchTab, setGiphyAPIKey} from "./giphy.js"
|
||||
import { html, render, Component } from "../lib/htm/preact.js"
|
||||
import { Spinner } from "./spinner.js"
|
||||
import { shouldAutofocusSearchBar, shouldDisplayAutofocusSearchBar, SearchBox } from "./search-box.js"
|
||||
import { checkMobileSafari } from "./user-agent-detect.js"
|
||||
import * as widgetAPI from "./widget-api.js"
|
||||
import * as frequent from "./frequently-used.js"
|
||||
|
||||
@ -29,21 +29,31 @@ const params = new URLSearchParams(document.location.search)
|
||||
if (params.has('config')) {
|
||||
INDEX = params.get("config")
|
||||
}
|
||||
// This is updated from packs/index.json
|
||||
let HOMESERVER_URL = "https://matrix-client.matrix.org"
|
||||
|
||||
const makeThumbnailURL = mxc => `${PACKS_BASE_URL}/thumbnails/${mxc.split("/").slice(-1)[0]}`
|
||||
const makeThumbnailURL = mxc => `${HOMESERVER_URL}/_matrix/media/r0/thumbnail/${mxc.substr(6)}?height=128&width=128&method=scale`
|
||||
|
||||
// We need to detect iOS webkit because it has a bug related to scrolling non-fixed divs
|
||||
// This is also used to fix scrolling to sections on Element iOS
|
||||
const isMobileSafari = navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/)
|
||||
const isMobileSafari = checkMobileSafari()
|
||||
|
||||
// We need to detect iOS webkit / Android because autofocusing a field does not open
|
||||
// the device keyboard by design, making the option obsolete
|
||||
const shouldAutofocusOption = shouldAutofocusSearchBar()
|
||||
const displayAutofocusOption = shouldDisplayAutofocusSearchBar()
|
||||
|
||||
const supportedThemes = ["light", "dark", "black"]
|
||||
|
||||
const defaultState = {
|
||||
packs: [],
|
||||
filtering: {
|
||||
searchTerm: "",
|
||||
packs: [],
|
||||
},
|
||||
loading: true,
|
||||
error: null,
|
||||
}
|
||||
|
||||
const defaultSearchState = {
|
||||
searchTerm: null,
|
||||
filteredPacks: null
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
@ -51,10 +61,8 @@ class App extends Component {
|
||||
super(props)
|
||||
this.defaultTheme = params.get("theme")
|
||||
this.state = {
|
||||
viewingGifs: false,
|
||||
packs: defaultState.packs,
|
||||
loading: true,
|
||||
error: null,
|
||||
...defaultState,
|
||||
...defaultSearchState,
|
||||
stickersPerRow: parseInt(localStorage.mauStickersPerRow || "4"),
|
||||
theme: localStorage.mauStickerThemeOverride || this.defaultTheme,
|
||||
frequentlyUsed: {
|
||||
@ -63,7 +71,6 @@ class App extends Component {
|
||||
stickerIDs: frequent.get(),
|
||||
stickers: [],
|
||||
},
|
||||
filtering: defaultState.filtering,
|
||||
}
|
||||
if (!supportedThemes.includes(this.state.theme)) {
|
||||
this.state.theme = "light"
|
||||
@ -77,6 +84,7 @@ class App extends Component {
|
||||
this.packListRef = null
|
||||
this.navRef = null
|
||||
this.searchStickers = this.searchStickers.bind(this)
|
||||
this.resetSearch = this.resetSearch.bind(this)
|
||||
this.sendSticker = this.sendSticker.bind(this)
|
||||
this.navScroll = this.navScroll.bind(this)
|
||||
this.reloadPacks = this.reloadPacks.bind(this)
|
||||
@ -101,28 +109,33 @@ class App extends Component {
|
||||
localStorage.mauFrequentlyUsedStickerCache = JSON.stringify(stickers.map(sticker => [sticker.id, sticker]))
|
||||
}
|
||||
|
||||
searchStickers(e) {
|
||||
// Search
|
||||
|
||||
resetSearch() {
|
||||
this.setState({ ...defaultSearchState })
|
||||
}
|
||||
|
||||
searchStickers(searchTerm) {
|
||||
const sanitizeString = s => s.toLowerCase().trim()
|
||||
const searchTerm = sanitizeString(e.target.value)
|
||||
const sanitizedSearch = sanitizeString(searchTerm)
|
||||
|
||||
const allPacks = [this.state.frequentlyUsed, ...this.state.packs]
|
||||
const packsWithFilteredStickers = allPacks.map(pack => ({
|
||||
...pack,
|
||||
stickers: pack.stickers.filter(sticker =>
|
||||
sanitizeString(sticker.body).includes(searchTerm) ||
|
||||
sanitizeString(sticker.id).includes(searchTerm)
|
||||
sanitizeString(sticker.body).includes(sanitizedSearch) ||
|
||||
sanitizeString(sticker.id).includes(sanitizedSearch)
|
||||
),
|
||||
}))
|
||||
const filteredPacks = packsWithFilteredStickers.filter(({ stickers }) => !!stickers.length)
|
||||
|
||||
this.setState({
|
||||
filtering: {
|
||||
...this.state.filtering,
|
||||
searchTerm,
|
||||
packs: packsWithFilteredStickers.filter(({stickers}) => !!stickers.length),
|
||||
},
|
||||
})
|
||||
this.setState({ searchTerm, filteredPacks })
|
||||
}
|
||||
|
||||
// End search
|
||||
|
||||
// Settings
|
||||
|
||||
setStickersPerRow(val) {
|
||||
localStorage.mauStickersPerRow = val
|
||||
document.documentElement.style.setProperty("--stickers-per-row", localStorage.mauStickersPerRow)
|
||||
@ -135,26 +148,30 @@ class App extends Component {
|
||||
setTheme(theme) {
|
||||
if (theme === "default") {
|
||||
delete localStorage.mauStickerThemeOverride
|
||||
this.setState({theme: this.defaultTheme})
|
||||
this.setState({ theme: this.defaultTheme })
|
||||
} else {
|
||||
localStorage.mauStickerThemeOverride = theme
|
||||
this.setState({theme: theme})
|
||||
this.setState({ theme: theme })
|
||||
}
|
||||
}
|
||||
|
||||
setAutofocusSearchBar(checked) {
|
||||
localStorage.mauAutofocusSearchBar = checked
|
||||
}
|
||||
|
||||
// End settings
|
||||
|
||||
reloadPacks() {
|
||||
this.imageObserver.disconnect()
|
||||
this.sectionObserver.disconnect()
|
||||
this.setState({
|
||||
packs: defaultState.packs,
|
||||
filtering: defaultState.filtering,
|
||||
})
|
||||
this.setState({ packs: defaultState.packs })
|
||||
this.resetSearch()
|
||||
this._loadPacks(true)
|
||||
}
|
||||
|
||||
_loadPacks(disableCache = false) {
|
||||
const cache = disableCache ? "no-cache" : undefined
|
||||
fetch(INDEX, {cache}).then(async indexRes => {
|
||||
fetch(INDEX, { cache }).then(async indexRes => {
|
||||
if (indexRes.status >= 400) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
@ -163,16 +180,14 @@ class App extends Component {
|
||||
return
|
||||
}
|
||||
const indexData = await indexRes.json()
|
||||
if (indexData.giphy_api_key !== undefined) {
|
||||
setGiphyAPIKey(indexData.giphy_api_key, indexData.giphy_mxc_prefix)
|
||||
}
|
||||
HOMESERVER_URL = indexData.homeserver_url || HOMESERVER_URL
|
||||
// TODO only load pack metadata when scrolled into view?
|
||||
for (const packFile of indexData.packs) {
|
||||
let packRes
|
||||
if (packFile.startsWith("https://") || packFile.startsWith("http://")) {
|
||||
packRes = await fetch(packFile, {cache})
|
||||
packRes = await fetch(packFile, { cache })
|
||||
} else {
|
||||
packRes = await fetch(`${PACKS_BASE_URL}/${packFile}`, {cache})
|
||||
packRes = await fetch(`${PACKS_BASE_URL}/${packFile}`, { cache })
|
||||
}
|
||||
const packData = await packRes.json()
|
||||
for (const sticker of packData.stickers) {
|
||||
@ -184,7 +199,7 @@ class App extends Component {
|
||||
})
|
||||
}
|
||||
this.updateFrequentlyUsed()
|
||||
}, error => this.setState({loading: false, error}))
|
||||
}, error => this.setState({ loading: false, error }))
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -216,10 +231,10 @@ class App extends Component {
|
||||
let maxXElem = null
|
||||
for (const entry of intersections) {
|
||||
const packID = entry.target.getAttribute("data-pack-id")
|
||||
if (!packID) {
|
||||
const navElement = document.getElementById(`nav-${packID}`)
|
||||
if (!navElement) {
|
||||
continue
|
||||
}
|
||||
const navElement = document.getElementById(`nav-${packID}`)
|
||||
if (entry.isIntersecting) {
|
||||
navElement.classList.add("visible")
|
||||
const bb = navElement.getBoundingClientRect()
|
||||
@ -235,9 +250,9 @@ class App extends Component {
|
||||
}
|
||||
}
|
||||
if (minXElem !== null) {
|
||||
minXElem.scrollIntoView({inline: "start"})
|
||||
minXElem.scrollIntoView({ inline: "start" })
|
||||
} else if (maxXElem !== null) {
|
||||
maxXElem.scrollIntoView({inline: "end"})
|
||||
maxXElem.scrollIntoView({ inline: "end" })
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,6 +278,7 @@ class App extends Component {
|
||||
const sticker = this.stickersByID.get(id)
|
||||
frequent.add(id)
|
||||
this.updateFrequentlyUsed()
|
||||
this.resetSearch()
|
||||
widgetAPI.sendSticker(sticker)
|
||||
}
|
||||
|
||||
@ -272,67 +288,43 @@ class App extends Component {
|
||||
|
||||
render() {
|
||||
const theme = `theme-${this.state.theme}`
|
||||
const filterActive = !!this.state.filtering.searchTerm
|
||||
const packs = filterActive
|
||||
? this.state.filtering.packs
|
||||
: [this.state.frequentlyUsed, ...this.state.packs]
|
||||
|
||||
const filterActive = !!this.state.filteredPacks
|
||||
const packs = filterActive ? this.state.filteredPacks : [this.state.frequentlyUsed, ...this.state.packs]
|
||||
const noPacksForSearch = filterActive && packs.length === 0
|
||||
|
||||
if (this.state.loading) {
|
||||
return html`
|
||||
<main class="spinner ${theme}">
|
||||
<${Spinner} size=${80} green/>
|
||||
</main>
|
||||
`
|
||||
return html`<main class="spinner ${theme}"><${Spinner} size=${80} green /></main>`
|
||||
} else if (this.state.error) {
|
||||
return html`
|
||||
<main class="error ${theme}">
|
||||
<h1>Failed to load packs</h1>
|
||||
<p>${this.state.error}</p>
|
||||
</main>
|
||||
`
|
||||
return html`<main class="error ${theme}">
|
||||
<h1>Failed to load packs</h1>
|
||||
<p>${this.state.error}</p>
|
||||
</main>`
|
||||
} else if (this.state.packs.length === 0) {
|
||||
return html`
|
||||
<main class="empty ${theme}"><h1>No packs found 😿</h1></main>
|
||||
`
|
||||
return html`<main class="empty ${theme}"><h1>No packs found 😿</h1></main>`
|
||||
}
|
||||
|
||||
const onClickOverride = this.state.viewingGifs
|
||||
? (evt, packID) => {
|
||||
evt.preventDefault()
|
||||
this.setState({viewingGifs: false}, () => {
|
||||
scrollToSection(null, packID)
|
||||
})
|
||||
} : null
|
||||
const switchToGiphy = () => this.setState({viewingGifs: true, filtering: defaultState.filtering})
|
||||
|
||||
return html`
|
||||
<main class="has-content ${theme}">
|
||||
<nav onWheel=${this.navScroll} ref=${elem => this.navRef = elem}>
|
||||
${giphyIsEnabled() && html`
|
||||
<${NavBarItem} pack=${{id: "giphy", title: "GIPHY"}} iconOverride="giphy" onClickOverride=${switchToGiphy} extraClass=${this.state.viewingGifs ? "visible" : ""}/>
|
||||
`}
|
||||
<${NavBarItem} pack=${this.state.frequentlyUsed} iconOverride="recent" onClickOverride=${onClickOverride}/>
|
||||
${this.state.packs.map(pack => html`<${NavBarItem} id=${pack.id} pack=${pack} onClickOverride=${onClickOverride}/>`)}
|
||||
<${NavBarItem} pack=${{id: "settings", title: "Settings"}} iconOverride="settings" onClickOverride=${onClickOverride}/>
|
||||
</nav>
|
||||
|
||||
${this.state.viewingGifs ? html`
|
||||
<${GiphySearchTab}/>
|
||||
` : html`
|
||||
<${SearchBox} onInput=${this.searchStickers} value=${this.state.filtering.searchTerm ?? ""}/>
|
||||
<div class="pack-list ${isMobileSafari ? "ios-safari-hack" : ""}" ref=${(elem) => (this.packListRef = elem)}>
|
||||
${filterActive && packs.length === 0
|
||||
? html`<div class="search-empty"><h1>No stickers match your search</h1></div>`
|
||||
: null}
|
||||
${packs.map((pack) => html`<${Pack} id=${pack.id} pack=${pack} send=${this.sendSticker}/>`)}
|
||||
<${Settings} app=${this}/>
|
||||
</div>
|
||||
`}
|
||||
</main>`
|
||||
return html`<main class="has-content ${theme}">
|
||||
<nav onWheel=${this.navScroll} ref=${elem => this.navRef = elem}>
|
||||
<${NavBarItem} pack=${this.state.frequentlyUsed} iconOverride="recent" />
|
||||
${this.state.packs.map(pack => html`<${NavBarItem} id=${pack.id} pack=${pack}/>`)}
|
||||
<${NavBarItem} pack=${{ id: "settings", title: "Settings" }} iconOverride="settings" />
|
||||
</nav>
|
||||
<${SearchBox}
|
||||
value=${this.state.searchTerm}
|
||||
onSearch=${this.searchStickers}
|
||||
onReset=${this.resetSearch}
|
||||
/>
|
||||
<div class="pack-list ${isMobileSafari ? "ios-safari-hack" : ""}" ref=${elem => this.packListRef = elem}>
|
||||
${noPacksForSearch ? html`<div class="search-empty"><h1>No stickers match your search</h1></div>` : null}
|
||||
${packs.map(pack => html`<${Pack} id=${pack.id} pack=${pack} send=${this.sendSticker} />`)}
|
||||
<${Settings} app=${this}/>
|
||||
</div>
|
||||
</main>`
|
||||
}
|
||||
}
|
||||
|
||||
const Settings = ({app}) => html`
|
||||
const Settings = ({ app }) => html`
|
||||
<section class="stickerpack settings" id="pack-settings" data-pack-id="settings">
|
||||
<h1>Settings</h1>
|
||||
<div class="settings-list">
|
||||
@ -341,7 +333,7 @@ const Settings = ({app}) => html`
|
||||
<label for="stickers-per-row">Stickers per row: ${app.state.stickersPerRow}</label>
|
||||
<input type="range" min=2 max=10 id="stickers-per-row" id="stickers-per-row"
|
||||
value=${app.state.stickersPerRow}
|
||||
onInput=${evt => app.setStickersPerRow(evt.target.value)}/>
|
||||
onInput=${evt => app.setStickersPerRow(evt.target.value)} />
|
||||
</div>
|
||||
<div>
|
||||
<label for="theme">Theme: </label>
|
||||
@ -352,6 +344,14 @@ const Settings = ({app}) => html`
|
||||
<option value="black">Black</option>
|
||||
</select>
|
||||
</div>
|
||||
${displayAutofocusOption ? html`<div>
|
||||
Autofocus search bar:
|
||||
<input
|
||||
type="checkbox"
|
||||
checked=${shouldAutofocusOption}
|
||||
onChange=${evt => app.setAutofocusSearchBar(evt.target.checked)}
|
||||
/>
|
||||
</div>` : null}
|
||||
</div>
|
||||
</section>
|
||||
`
|
||||
@ -360,15 +360,13 @@ const Settings = ({app}) => html`
|
||||
// open the link in the browser instead of just scrolling there, so we need to scroll manually:
|
||||
const scrollToSection = (evt, id) => {
|
||||
const pack = document.getElementById(`pack-${id}`)
|
||||
if (pack) {
|
||||
pack.scrollIntoView({block: "start", behavior: "instant"})
|
||||
}
|
||||
evt?.preventDefault()
|
||||
pack.scrollIntoView({ block: "start", behavior: "instant" })
|
||||
evt.preventDefault()
|
||||
}
|
||||
|
||||
const NavBarItem = ({pack, iconOverride = null, onClickOverride = null, extraClass = null}) => html`
|
||||
<a href="#pack-${pack.id}" id="nav-${pack.id}" data-pack-id=${pack.id} title=${pack.title} class="${extraClass}"
|
||||
onClick=${onClickOverride ? (evt => onClickOverride(evt, pack.id)) : (isMobileSafari ? (evt => scrollToSection(evt, pack.id)) : undefined)}>
|
||||
const NavBarItem = ({ pack, iconOverride = null }) => html`
|
||||
<a href="#pack-${pack.id}" id="nav-${pack.id}" data-pack-id=${pack.id} title=${pack.title}
|
||||
onClick=${isMobileSafari ? (evt => scrollToSection(evt, pack.id)) : undefined}>
|
||||
<div class="sticker">
|
||||
${iconOverride ? html`
|
||||
<span class="icon icon-${iconOverride}"/>
|
||||
@ -380,7 +378,7 @@ const NavBarItem = ({pack, iconOverride = null, onClickOverride = null, extraCla
|
||||
</a>
|
||||
`
|
||||
|
||||
const Pack = ({pack, send}) => html`
|
||||
const Pack = ({ pack, send }) => html`
|
||||
<section class="stickerpack" id="pack-${pack.id}" data-pack-id=${pack.id}>
|
||||
<h1>${pack.title}</h1>
|
||||
<div class="sticker-list">
|
||||
@ -391,10 +389,10 @@ const Pack = ({pack, send}) => html`
|
||||
</section>
|
||||
`
|
||||
|
||||
const Sticker = ({content, send}) => html`
|
||||
const Sticker = ({ content, send }) => html`
|
||||
<div class="sticker" onClick=${send} data-sticker-id=${content.id}>
|
||||
<img data-src=${makeThumbnailURL(content.url)} alt=${content.body} title=${content.body}/>
|
||||
<img data-src=${makeThumbnailURL(content.url)} alt=${content.body} title=${content.body} />
|
||||
</div>
|
||||
`
|
||||
|
||||
render(html`<${App}/>`, document.body)
|
||||
render(html`<${App} />`, document.body)
|
||||
|
@ -13,14 +13,77 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import {html} from "../lib/htm/preact.js"
|
||||
import { html, Component } from "../lib/htm/preact.js"
|
||||
import { checkMobileSafari, checkAndroid } from "./user-agent-detect.js"
|
||||
|
||||
export const SearchBox = ({onInput, onKeyUp, value, placeholder = 'Find stickers'}) => {
|
||||
const component = html`
|
||||
<div class="search-box">
|
||||
<input type="text" placeholder=${placeholder} value=${value} onInput=${onInput} onKeyUp=${onKeyUp}/>
|
||||
<span class="icon icon-search"/>
|
||||
</div>
|
||||
`
|
||||
return component
|
||||
export function shouldDisplayAutofocusSearchBar() {
|
||||
return !checkMobileSafari() && !checkAndroid()
|
||||
}
|
||||
|
||||
export function shouldAutofocusSearchBar() {
|
||||
return localStorage.mauAutofocusSearchBar === 'true' && shouldDisplayAutofocusSearchBar()
|
||||
}
|
||||
|
||||
export function focusSearchBar() {
|
||||
const inputInWebView = document.querySelector('.search-box input')
|
||||
if (inputInWebView && shouldAutofocusSearchBar()) {
|
||||
inputInWebView.focus()
|
||||
}
|
||||
}
|
||||
|
||||
export class SearchBox extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.autofocus = shouldAutofocusSearchBar()
|
||||
this.value = props.value
|
||||
this.onSearch = props.onSearch
|
||||
this.onReset = props.onReset
|
||||
|
||||
this.search = this.search.bind(this)
|
||||
this.clearSearch = this.clearSearch.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
focusSearchBar()
|
||||
}
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
this.value = props.value
|
||||
}
|
||||
|
||||
search(e) {
|
||||
if (e.key === "Escape") {
|
||||
this.clearSearch()
|
||||
return
|
||||
}
|
||||
this.onSearch(e.target.value)
|
||||
}
|
||||
|
||||
clearSearch() {
|
||||
this.onReset()
|
||||
}
|
||||
|
||||
render() {
|
||||
const isEmpty = !this.value
|
||||
|
||||
const className = `icon-display ${isEmpty ? null : 'reset-click-zone'}`
|
||||
const title = isEmpty ? null : 'Click to reset'
|
||||
const onClick = isEmpty ? null : this.clearSearch
|
||||
const iconToDisplay = `icon-${isEmpty ? 'search' : 'reset'}`
|
||||
|
||||
return html`
|
||||
<div class="search-box">
|
||||
<input
|
||||
placeholder="Find stickers …"
|
||||
value=${this.value}
|
||||
onKeyUp=${this.search}
|
||||
autoFocus=${this.autofocus}
|
||||
/>
|
||||
<div class=${className} title=${title} onClick=${onClick}>
|
||||
<span class="icon ${iconToDisplay}" />
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
18
web/src/user-agent-detect.js
Normal file
18
web/src/user-agent-detect.js
Normal file
@ -0,0 +1,18 @@
|
||||
export const getUserAgent = () => navigator.userAgent || navigator.vendor || window.opera
|
||||
|
||||
export const checkiOSDevice = () => {
|
||||
const agent = getUserAgent()
|
||||
return agent.match(/(iPod|iPhone|iPad)/)
|
||||
}
|
||||
|
||||
export const checkMobileSafari = () => {
|
||||
const agent = getUserAgent()
|
||||
return agent.match(/(iPod|iPhone|iPad)/) && agent.match(/AppleWebKit/)
|
||||
}
|
||||
|
||||
export const checkAndroid = () => {
|
||||
const agent = getUserAgent()
|
||||
return agent.match(/android/i)
|
||||
}
|
||||
|
||||
export const checkMobileDevice = () => checkiOSDevice() || checkAndroid()
|
@ -13,6 +13,8 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import { focusSearchBar } from "./search-box.js"
|
||||
|
||||
let widgetId = null
|
||||
|
||||
window.onmessage = event => {
|
||||
@ -33,10 +35,12 @@ window.onmessage = event => {
|
||||
widgetId = request.widgetId
|
||||
}
|
||||
|
||||
let response
|
||||
let response = {}
|
||||
|
||||
if (request.action === "visibility") {
|
||||
response = {}
|
||||
if (request.action === "visibility") { // visibility of the widget changed
|
||||
if (request.visible) {
|
||||
focusSearchBar() // we have to re-focus the search bar when appropriate
|
||||
}
|
||||
} else if (request.action === "capabilities") {
|
||||
response = { capabilities: ["m.sticker"] }
|
||||
} else {
|
||||
@ -60,9 +64,8 @@ export function sendSticker(content) {
|
||||
const widgetData = {
|
||||
...data,
|
||||
description: content.body,
|
||||
file: content.filename ?? `${content.id}.png`,
|
||||
file: `${content.id}.png`,
|
||||
}
|
||||
delete widgetData.content.filename
|
||||
// Element iOS explodes if there are extra fields present
|
||||
delete widgetData.content["net.maunium.telegram.sticker"]
|
||||
|
||||
|
@ -1 +1 @@
|
||||
*{font-family:sans-serif}body{margin:0}h1{font-size:1rem}:root{--stickers-per-row: 4;--sticker-size: calc(100vw / var(--stickers-per-row))}main{color:var(--text-color)}main.spinner{margin-top:5rem}main.error,main.empty{margin:2rem}main.empty{text-align:center}main.has-content{position:fixed;top:0;left:0;right:0;bottom:0;display:grid;grid-template-rows:calc(12vw + 2px) min-content auto}main.theme-light{--highlight-color: #eee;--search-box-color: var(--highlight-color);--text-color: black;background-color:#fff}main.theme-dark{--highlight-color: #444;--search-box-color: #383e4b;--text-color: white;background-color:#22262e}main.theme-dark .icon.icon-giphy{background-image:url(../res/giphy-dark.svg)}main.theme-black{--highlight-color: #222;--search-box-color: var(--highlight-color);--text-color: white;background-color:#000}main.theme-black .icon.icon-giphy{background-image:url(../res/giphy-dark.svg)}div.powered-by-giphy{padding:1rem}div.powered-by-giphy>img{width:100%}.icon{width:100%;height:100%;background-color:var(--text-color);mask-size:contain;-webkit-mask-size:contain;mask-image:var(--icon-image);-webkit-mask-image:var(--icon-image)}.icon.icon-settings{--icon-image: url(../res/settings.svg)}.icon.icon-recent{--icon-image: url(../res/recent.svg)}.icon.icon.icon-search{--icon-image: url(../res/search.svg)}.icon.icon.icon-giphy{background:center/contain no-repeat url(../res/giphy-light.svg);mask:unset}nav{display:flex;overflow-x:auto}nav>a{border-bottom:2px solid rgba(0,0,0,0)}nav>a.visible{border-bottom-color:green}nav>a>div.sticker{width:12vw;height:12vw}div.pack-list,nav{scrollbar-width:none}div.pack-list::-webkit-scrollbar,nav::-webkit-scrollbar{display:none}div.pack-list{overflow-y:auto}div.pack-list.ios-safari-hack{position:fixed;top:calc(calc(12vw + 2px) + calc(2 * 0.7rem + 2 * 0.5rem + 1rem));bottom:0;left:0;right:0;-webkit-overflow-scrolling:touch}div.search-empty{margin:1.2rem;text-align:center}section.stickerpack{margin-top:.75rem}section.stickerpack>div.sticker-list{display:flex;flex-wrap:wrap}section.stickerpack>h1{margin:0 0 0 .75rem}section.stickerpack#pack-giphy{display:flex;justify-content:space-between;flex-direction:column;min-height:100%}div.sticker{display:flex;padding:4px;cursor:pointer;position:relative;width:var(--sticker-size);height:var(--sticker-size);box-sizing:border-box}div.sticker:hover{background-color:var(--highlight-color)}div.sticker>img{display:none;width:100%;object-fit:contain}div.sticker>img.visible{display:initial}div.sticker>.icon{width:70%;height:70%;margin:15%}div.search-box{position:relative;display:flex}div.search-box>input[type=text]{flex-grow:1;background-color:var(--search-box-color);outline:none;border:none;border-radius:.25rem;height:1rem;padding:.7rem;padding-right:calc(1rem + 0.7rem);margin:.5rem;font-size:1rem;color:var(--text-color)}div.search-box>span.icon{display:flex;position:absolute;top:calc(50% - 1rem/2);right:1rem;width:1rem;height:1rem;box-sizing:border-box}div.settings-list{display:flex;flex-direction:column}div.settings-list>*{margin:.5rem}div.settings-list button{padding:.5rem;border-radius:.25rem}div.settings-list input{width:100%}
|
||||
*{font-family:sans-serif}body{margin:0}h1{font-size:1rem}:root{--stickers-per-row: 4;--sticker-size: calc(100vw / var(--stickers-per-row))}main{color:var(--text-color)}main.spinner{margin-top:5rem}main.error,main.empty{margin:2rem}main.empty{text-align:center}main.has-content{position:fixed;top:0;left:0;right:0;bottom:0;display:grid;grid-template-rows:calc(12vw + 2px) min-content auto}main.theme-light{--highlight-color: #eee;--search-box-color: var(--highlight-color);--text-color: black;background-color:#fff}main.theme-dark{--highlight-color: #444;--search-box-color: #383e4b;--text-color: white;background-color:#22262e}main.theme-black{--highlight-color: #222;--search-box-color: var(--highlight-color);--text-color: white;background-color:#000}.icon{width:100%;height:100%;background-color:var(--text-color);mask-size:contain;-webkit-mask-size:contain;mask-image:var(--icon-image);-webkit-mask-image:var(--icon-image)}.icon.icon-settings{--icon-image: url(../res/settings.svg)}.icon.icon-recent{--icon-image: url(../res/recent.svg)}.icon.icon-search{--icon-image: url(../res/search.svg)}.icon.icon-reset{--icon-image: url(../res/reset.svg)}nav{display:flex;overflow-x:auto}nav>a{border-bottom:2px solid transparent}nav>a.visible{border-bottom-color:green}nav>a>div.sticker{width:12vw;height:12vw}div.pack-list,nav{scrollbar-width:none}div.pack-list::-webkit-scrollbar,nav::-webkit-scrollbar{display:none}div.pack-list{overflow-y:auto}div.pack-list.ios-safari-hack{position:fixed;top:calc(calc(12vw + 2px) + calc(2 * 0.7rem + 2 * 0.5rem + 1rem));bottom:0;left:0;right:0;-webkit-overflow-scrolling:touch}div.search-empty{margin:1.2rem;text-align:center}section.stickerpack{margin-top:.75rem}section.stickerpack>div.sticker-list{display:flex;flex-wrap:wrap}section.stickerpack>h1{margin:0 0 0 .75rem}div.sticker{display:flex;padding:4px;cursor:pointer;position:relative;width:var(--sticker-size);height:var(--sticker-size);box-sizing:border-box}div.sticker:hover{background-color:var(--highlight-color)}div.sticker>img{display:none;width:100%;object-fit:contain}div.sticker>img.visible{display:initial}div.sticker>.icon{width:70%;height:70%;margin:15%}div.search-box{display:flex;margin:.5rem;padding:0;border-radius:.4rem;background-color:var(--search-box-color);opacity:.5;transition:all .1s;border:1px solid transparent}div.search-box:focus-within{opacity:1;border:1px solid var(--text-color)}div.search-box:not(:focus-within):hover{opacity:.7}div.search-box input,div.search-box .icon-display{color:var(--text-color);outline:none;border:none;height:1rem;margin:0;padding:.7rem;background-color:transparent;-webkit-tap-highlight-color:transparent}div.search-box .icon-display{width:1rem}div.search-box .icon-display.reset-click-zone{cursor:pointer}div.search-box .icon-display .icon{display:block;width:1rem;height:1rem}div.search-box .icon-display .icon-search{opacity:.5}div.search-box input{flex-grow:1;font-size:1rem}div.settings-list{display:flex;flex-direction:column}div.settings-list>*{margin:.5rem}div.settings-list button{padding:.5rem;border-radius:.25rem}div.settings-list input:not([type=checkbox]){width:100%}
|
||||
|
@ -71,23 +71,12 @@ main.theme-dark
|
||||
--text-color: white
|
||||
background-color: #22262e
|
||||
|
||||
.icon.icon-giphy
|
||||
background-image: url(../res/giphy-dark.svg)
|
||||
|
||||
main.theme-black
|
||||
--highlight-color: #222
|
||||
--search-box-color: var(--highlight-color)
|
||||
--text-color: white
|
||||
background-color: black
|
||||
|
||||
.icon.icon-giphy
|
||||
background-image: url(../res/giphy-dark.svg)
|
||||
|
||||
div.powered-by-giphy
|
||||
padding: 1rem
|
||||
> img
|
||||
width: 100%
|
||||
|
||||
.icon
|
||||
width: 100%
|
||||
height: 100%
|
||||
@ -103,12 +92,11 @@ div.powered-by-giphy
|
||||
&.icon-recent
|
||||
--icon-image: url(../res/recent.svg)
|
||||
|
||||
&.icon.icon-search
|
||||
&.icon-search
|
||||
--icon-image: url(../res/search.svg)
|
||||
|
||||
&.icon.icon-giphy
|
||||
background: center / contain no-repeat url(../res/giphy-light.svg)
|
||||
mask: unset
|
||||
&.icon-reset
|
||||
--icon-image: url(../res/reset.svg)
|
||||
|
||||
nav
|
||||
display: flex
|
||||
@ -155,12 +143,6 @@ section.stickerpack
|
||||
> h1
|
||||
margin: 0 0 0 .75rem
|
||||
|
||||
section.stickerpack#pack-giphy
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
flex-direction: column
|
||||
min-height: 100%
|
||||
|
||||
div.sticker
|
||||
display: flex
|
||||
padding: 4px
|
||||
@ -187,30 +169,49 @@ div.sticker
|
||||
margin: 15%
|
||||
|
||||
div.search-box
|
||||
position: relative
|
||||
display: flex
|
||||
margin: $search-box-input-margin
|
||||
padding: 0
|
||||
border-radius: .4rem
|
||||
background-color: var(--search-box-color)
|
||||
opacity: .5
|
||||
transition: all .1s
|
||||
border: 1px solid transparent
|
||||
|
||||
>input[type="text"]
|
||||
flex-grow: 1
|
||||
background-color: var(--search-box-color)
|
||||
&:focus-within
|
||||
opacity: 1
|
||||
border: 1px solid var(--text-color)
|
||||
|
||||
&:not(:focus-within):hover
|
||||
opacity: .7
|
||||
|
||||
input,.icon-display
|
||||
color: var(--text-color)
|
||||
outline: none
|
||||
border: none
|
||||
border-radius: .25rem
|
||||
height: $search-box-input-height
|
||||
margin: 0
|
||||
padding: $search-box-input-padding
|
||||
padding-right: calc(#{$search-box-icon-size} + #{$search-box-input-padding})
|
||||
margin: $search-box-input-margin
|
||||
font-size: 1rem
|
||||
color: var(--text-color)
|
||||
background-color: transparent
|
||||
-webkit-tap-highlight-color: transparent
|
||||
|
||||
>span.icon
|
||||
display: flex
|
||||
position: absolute
|
||||
top: calc(50% - #{$search-box-icon-size} / 2)
|
||||
right: $search-box-icon-size
|
||||
width: $search-box-icon-size
|
||||
height: $search-box-icon-size
|
||||
box-sizing: border-box
|
||||
.icon-display
|
||||
width: $search-box-input-height
|
||||
|
||||
&.reset-click-zone
|
||||
cursor: pointer
|
||||
|
||||
.icon
|
||||
display: block
|
||||
width: $search-box-icon-size
|
||||
height: $search-box-icon-size
|
||||
|
||||
.icon-search
|
||||
opacity: .5
|
||||
|
||||
input
|
||||
flex-grow: 1
|
||||
font-size: 1rem
|
||||
|
||||
div.settings-list
|
||||
display: flex
|
||||
@ -223,5 +224,5 @@ div.settings-list
|
||||
padding: .5rem
|
||||
border-radius: .25rem
|
||||
|
||||
input
|
||||
input:not([type="checkbox"])
|
||||
width: 100%
|
||||
|
372
web/yarn.lock
372
web/yarn.lock
@ -16,13 +16,13 @@
|
||||
resolve "^1.17.0"
|
||||
|
||||
"@rollup/plugin-inject@^4.0.0", "@rollup/plugin-inject@^4.0.2":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.4.tgz#fbeee66e9a700782c4f65c8b0edbafe58678fbc2"
|
||||
integrity sha512-4pbcU4J/nS+zuHk+c+OL3WtmEQhqxlZ9uqfjQMQDOHOPld7PsCd8k5LWs8h5wjwJN7MgnAn768F2sDxEP4eNFQ==
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.2.tgz#55b21bb244a07675f7fdde577db929c82fc17395"
|
||||
integrity sha512-TSLMA8waJ7Dmgmoc8JfPnwUwVZgLjjIAM6MqeIFqPO2ODK36JqE0Cf2F54UTgCUuW8da93Mvoj75a6KAVWgylw==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
estree-walker "^2.0.1"
|
||||
magic-string "^0.25.7"
|
||||
"@rollup/pluginutils" "^3.0.4"
|
||||
estree-walker "^1.0.1"
|
||||
magic-string "^0.25.5"
|
||||
|
||||
"@rollup/plugin-json@^4.0.0":
|
||||
version "4.1.0"
|
||||
@ -51,7 +51,7 @@
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
magic-string "^0.25.7"
|
||||
|
||||
"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
|
||||
"@rollup/pluginutils@^3.0.4", "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
|
||||
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
|
||||
@ -61,9 +61,9 @@
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@types/estree@*":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
|
||||
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
||||
version "0.0.50"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
|
||||
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
|
||||
|
||||
"@types/estree@0.0.39":
|
||||
version "0.0.39"
|
||||
@ -71,11 +71,9 @@
|
||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||
|
||||
"@types/node@*":
|
||||
version "20.12.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050"
|
||||
integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
version "16.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.2.tgz#5764ca9aa94470adb4e1185fe2e9f19458992b2e"
|
||||
integrity sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==
|
||||
|
||||
"@types/resolve@1.17.1":
|
||||
version "1.17.1"
|
||||
@ -84,31 +82,21 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
acorn-walk@^8.2.0:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa"
|
||||
integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==
|
||||
|
||||
acorn@^8.7.0:
|
||||
version "8.11.3"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
|
||||
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
|
||||
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
|
||||
dependencies:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
assert@^1.4.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.1.tgz#038ab248e4ff078e7bc2485ba6e6388466c78f76"
|
||||
integrity sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
|
||||
integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
|
||||
dependencies:
|
||||
object.assign "^4.1.4"
|
||||
util "^0.10.4"
|
||||
object-assign "^4.1.1"
|
||||
util "0.10.3"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
@ -116,9 +104,9 @@ balanced-match@^1.0.0:
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
|
||||
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
@ -136,30 +124,19 @@ braces@~3.0.2:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
builtin-modules@^3.1.0, builtin-modules@^3.2.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
|
||||
integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
|
||||
integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==
|
||||
|
||||
builtins@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
|
||||
integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==
|
||||
|
||||
call-bind@^1.0.5:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
|
||||
integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
|
||||
dependencies:
|
||||
es-define-property "^1.0.0"
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
get-intrinsic "^1.2.4"
|
||||
set-function-length "^1.2.1"
|
||||
integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og=
|
||||
|
||||
"chokidar@>=3.0.0 <4.0.0":
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
|
||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
|
||||
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
|
||||
dependencies:
|
||||
anymatch "~3.1.2"
|
||||
braces "~3.0.2"
|
||||
@ -172,19 +149,19 @@ call-bind@^1.0.5:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
cjs-module-lexer@^1.2.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c"
|
||||
integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
|
||||
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
|
||||
|
||||
commondir@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
|
||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
@ -196,39 +173,9 @@ cross-spawn@^7.0.3:
|
||||
which "^2.0.1"
|
||||
|
||||
deepmerge@^4.2.2:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
|
||||
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
|
||||
|
||||
define-data-property@^1.0.1, define-data-property@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
|
||||
integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
|
||||
dependencies:
|
||||
es-define-property "^1.0.0"
|
||||
es-errors "^1.3.0"
|
||||
gopd "^1.0.1"
|
||||
|
||||
define-properties@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
|
||||
integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
|
||||
dependencies:
|
||||
define-data-property "^1.0.1"
|
||||
has-property-descriptors "^1.0.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
es-define-property@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
|
||||
integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.4"
|
||||
|
||||
es-errors@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||
|
||||
es-module-lexer@^0.6.0:
|
||||
version "0.6.0"
|
||||
@ -296,7 +243,7 @@ fill-range@^7.0.1:
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
fsevents@~2.1.2:
|
||||
version "2.1.3"
|
||||
@ -304,25 +251,14 @@ fsevents@~2.1.2:
|
||||
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
|
||||
|
||||
fsevents@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
|
||||
function-bind@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
|
||||
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
|
||||
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
has-proto "^1.0.1"
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
get-stream@^6.0.0:
|
||||
version "6.0.1"
|
||||
@ -337,67 +273,38 @@ glob-parent@~5.1.2:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob@^7.1.3, glob@^7.1.6:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.1.1"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
gopd@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
||||
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
|
||||
integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
|
||||
dependencies:
|
||||
es-define-property "^1.0.0"
|
||||
|
||||
has-proto@^1.0.1:
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
|
||||
integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
|
||||
|
||||
has-symbols@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
|
||||
hasown@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
|
||||
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
function-bind "^1.1.1"
|
||||
|
||||
htm@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/htm/-/htm-3.1.1.tgz#49266582be0dc66ed2235d5ea892307cc0c24b78"
|
||||
integrity sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/htm/-/htm-3.1.0.tgz#0c305493b60da9f6ed097a2aaf4c994bd85ea022"
|
||||
integrity sha512-L0s3Sid5r6YwrEvkig14SK3Emmc+kIjlfLhEGn2Vy3bk21JyDEes4MoDsbJk6luaPp8bugErnxPz86ZuAw6e5Q==
|
||||
|
||||
human-signals@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
|
||||
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
|
||||
|
||||
immutable@^4.0.0:
|
||||
version "4.3.6"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.6.tgz#6a05f7858213238e587fb83586ffa3b4b27f0447"
|
||||
integrity sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
@ -407,10 +314,10 @@ inherits@2:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
inherits@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
|
||||
inherits@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
@ -419,17 +326,17 @@ is-binary-path@~2.1.0:
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-core-module@^2.13.0:
|
||||
version "2.13.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
|
||||
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
|
||||
is-core-module@^2.2.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.7.0.tgz#3c0ef7d31b4acfc574f80c58409d568a836848e3"
|
||||
integrity sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==
|
||||
dependencies:
|
||||
hasown "^2.0.0"
|
||||
has "^1.0.3"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
|
||||
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||
|
||||
is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
version "4.0.3"
|
||||
@ -441,7 +348,7 @@ is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
is-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||
integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==
|
||||
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
@ -470,19 +377,19 @@ is-valid-identifier@^2.0.2:
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
||||
|
||||
kleur@^4.1.1:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
|
||||
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
|
||||
version "4.1.4"
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
|
||||
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
|
||||
|
||||
magic-string@^0.25.7:
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
|
||||
integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
|
||||
magic-string@^0.25.5, magic-string@^0.25.7:
|
||||
version "0.25.7"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.8"
|
||||
sourcemap-codec "^1.4.4"
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
@ -494,10 +401,10 @@ mimic-fn@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
minimatch@^3.1.1:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
@ -518,25 +425,15 @@ npm-run-path@^4.0.1:
|
||||
dependencies:
|
||||
path-key "^3.0.0"
|
||||
|
||||
object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
||||
|
||||
object.assign@^4.1.4:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
|
||||
integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
|
||||
dependencies:
|
||||
call-bind "^1.0.5"
|
||||
define-properties "^1.2.1"
|
||||
has-symbols "^1.0.3"
|
||||
object-keys "^1.1.1"
|
||||
object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
@ -550,27 +447,27 @@ onetime@^5.1.2:
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
path-key@^3.0.0, path-key@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
path-parse@^1.0.7:
|
||||
path-parse@^1.0.6:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
||||
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
|
||||
|
||||
preact@^10.5.14:
|
||||
version "10.22.0"
|
||||
resolved "https://registry.yarnpkg.com/preact/-/preact-10.22.0.tgz#a50f38006ae438d255e2631cbdaf7488e6dd4e16"
|
||||
integrity sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==
|
||||
version "10.5.14"
|
||||
resolved "https://registry.yarnpkg.com/preact/-/preact-10.5.14.tgz#0b14a2eefba3c10a57116b90d1a65f5f00cd2701"
|
||||
integrity sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ==
|
||||
|
||||
readdirp@~3.6.0:
|
||||
version "3.6.0"
|
||||
@ -580,13 +477,12 @@ readdirp@~3.6.0:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
resolve@^1.17.0, resolve@^1.20.0:
|
||||
version "1.22.8"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
|
||||
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
||||
dependencies:
|
||||
is-core-module "^2.13.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
is-core-module "^2.2.0"
|
||||
path-parse "^1.0.6"
|
||||
|
||||
rimraf@^3.0.0:
|
||||
version "3.0.2"
|
||||
@ -610,25 +506,11 @@ rollup@~2.37.1:
|
||||
fsevents "~2.1.2"
|
||||
|
||||
sass@^1.42.1:
|
||||
version "1.77.2"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.2.tgz#18d4ed2eefc260cdc8099c5439ec1303fd5863aa"
|
||||
integrity sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==
|
||||
version "1.42.1"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.42.1.tgz#5ab17bebc1cb1881ad2e0c9a932c66ad64e441e2"
|
||||
integrity sha512-/zvGoN8B7dspKc5mC6HlaygyCBRvnyzzgD5khiaCfglWztY99cYoiTUksVx11NlnemrcfH5CEaCpsUKoW0cQqg==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
source-map-js ">=0.6.2 <2.0.0"
|
||||
|
||||
set-function-length@^1.2.1:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
|
||||
integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
|
||||
dependencies:
|
||||
define-data-property "^1.1.4"
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
get-intrinsic "^1.2.4"
|
||||
gopd "^1.0.1"
|
||||
has-property-descriptors "^1.0.2"
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
@ -643,21 +525,16 @@ shebang-regex@^3.0.0:
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
signal-exit@^3.0.3:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
|
||||
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f"
|
||||
integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==
|
||||
|
||||
slash@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||
|
||||
"source-map-js@>=0.6.2 <2.0.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||
|
||||
sourcemap-codec@^1.4.8:
|
||||
sourcemap-codec@^1.4.4:
|
||||
version "1.4.8"
|
||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||
@ -667,11 +544,6 @@ strip-final-newline@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||
|
||||
supports-preserve-symlinks-flag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
@ -679,32 +551,24 @@ to-regex-range@^5.0.1:
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
util@^0.10.4:
|
||||
version "0.10.4"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
|
||||
integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
|
||||
util@0.10.3:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
|
||||
integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
|
||||
dependencies:
|
||||
inherits "2.0.3"
|
||||
inherits "2.0.1"
|
||||
|
||||
validate-npm-package-name@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e"
|
||||
integrity sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==
|
||||
integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34=
|
||||
dependencies:
|
||||
builtins "^1.0.3"
|
||||
|
||||
vm2@^3.9.2:
|
||||
version "3.9.19"
|
||||
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.19.tgz#be1e1d7a106122c6c492b4d51c2e8b93d3ed6a4a"
|
||||
integrity sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==
|
||||
dependencies:
|
||||
acorn "^8.7.0"
|
||||
acorn-walk "^8.2.0"
|
||||
version "3.9.3"
|
||||
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.3.tgz#29917f6cc081cc43a3f580c26c5b553fd3c91f40"
|
||||
integrity sha512-smLS+18RjXYMl9joyJxMNI9l4w7biW8ilSDaVRvFBDwOH8P0BK1ognFQTpg0wyQ6wIKLTblHJvROW692L/E53Q==
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
@ -716,4 +580,4 @@ which@^2.0.1:
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
Reference in New Issue
Block a user