Compare commits

..

19 Commits

Author SHA1 Message Date
3366dbc500 Remove non-existent variable 2025-06-29 19:18:36 +03:00
d1d08d1ce4 Update mautrix-go 2025-06-29 19:12:40 +03:00
bb3e02c4d9 Update mautrix-go for optional federation auth 2025-05-04 01:16:40 +03:00
4b96d23621 Add sticker-download-thumbnails for adding thumbnails to an existing pack 2025-03-25 20:06:48 +02:00
3ce380645d Update urls and asyncio calls 2025-03-25 20:06:19 +02:00
a8effa2efa Save and server thumbnails locally (#88)
Fixes #83
Fixes #86
Fixes #87
2025-03-25 19:46:43 +02:00
89d3aece04 Add parameter 2024-12-03 00:16:51 +02:00
266f98579a Update giphyproxy dependencies 2024-12-03 00:14:27 +02:00
0591df0f7e Add missing character 2024-12-03 00:12:43 +02:00
8cea4a46d0 Make giphyproxy destination configurable 2024-12-03 00:03:01 +02:00
333567f481 Convert gif width/height to numbers 2024-06-19 14:29:21 +03:00
125d057e44 Remove animated sticker pack check 2024-06-19 13:55:00 +03:00
e1038e7d1e Force proxying legacy federated downloads in giphy proxy 2024-06-19 12:30:14 +03:00
850668a9f6 Update giphyproxy /version response 2024-06-19 12:21:04 +03:00
804014f3b4 Add cats to giphy proxy 2024-06-19 12:02:52 +03:00
5da539ad84 Add MSC3916-compatible giphy media repo proxy 2024-06-19 11:51:29 +03:00
6332613e13 Don't try to use non-existent variables 2024-06-17 22:48:15 +03:00
dbc3a9fbb8 Don't prompt for giphy api key by default 2024-06-05 13:02:34 +03:00
47f17fde45 Add support for sending gifs via Giphy
Fixes #22
Closes #75

Co-authored-by: Nischay <hegdenischay@gmail.com>
2024-05-18 16:18:55 +03:00
28 changed files with 970 additions and 401 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
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

13
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,13 @@
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

16
giphyproxy/Dockerfile Normal file
View File

@ -0,0 +1,16 @@
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"]

View File

@ -0,0 +1,21 @@
# 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

28
giphyproxy/go.mod Normal file
View File

@ -0,0 +1,28 @@
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
)

54
giphyproxy/go.sum Normal file
View File

@ -0,0 +1,54 @@
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=

76
giphyproxy/main.go Normal file
View File

@ -0,0 +1,76 @@
// 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
}

View File

@ -50,5 +50,6 @@ setuptools.setup(
entry_points={"console_scripts": [ entry_points={"console_scripts": [
"sticker-import=sticker.stickerimport:cmd", "sticker-import=sticker.stickerimport:cmd",
"sticker-pack=sticker.pack:cmd", "sticker-pack=sticker.pack:cmd",
"sticker-download-thumbnails=sticker.download_thumbnails:cmd",
]}, ]},
) )

View File

@ -0,0 +1,58 @@
# 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()

View File

@ -59,7 +59,7 @@ async def load_config(path: str) -> None:
print("Matrix config file not found. Please enter your homeserver and access token.") print("Matrix config file not found. Please enter your homeserver and access token.")
homeserver_url = input("Homeserver URL: ") homeserver_url = input("Homeserver URL: ")
access_token = input("Access token: ") access_token = input("Access token: ")
whoami_url = URL(homeserver_url) / "_matrix" / "client" / "r0" / "account" / "whoami" whoami_url = URL(homeserver_url) / "_matrix" / "client" / "v3" / "account" / "whoami"
if whoami_url.scheme not in ("https", "http"): if whoami_url.scheme not in ("https", "http"):
whoami_url = whoami_url.with_scheme("https") whoami_url = whoami_url.with_scheme("https")
user_id = await whoami(whoami_url, access_token) user_id = await whoami(whoami_url, access_token)
@ -67,11 +67,11 @@ async def load_config(path: str) -> None:
json.dump({ json.dump({
"homeserver": homeserver_url, "homeserver": homeserver_url,
"user_id": user_id, "user_id": user_id,
"access_token": access_token "access_token": access_token,
}, config_file) }, config_file)
print(f"Wrote config to {path}") print(f"Wrote config to {path}")
upload_url = URL(homeserver_url) / "_matrix" / "media" / "r0" / "upload" upload_url = URL(homeserver_url) / "_matrix" / "media" / "v3" / "upload"
async def whoami(url: URL, access_token: str) -> str: async def whoami(url: URL, access_token: str) -> str:

View File

@ -17,6 +17,8 @@ from functools import partial
from io import BytesIO from io import BytesIO
import os.path import os.path
import json import json
from pathlib import Path
from typing import Dict, List
from PIL import Image from PIL import Image
@ -24,19 +26,19 @@ from . import matrix
open_utf8 = partial(open, encoding='UTF-8') open_utf8 = partial(open, encoding='UTF-8')
def convert_image(data: bytes) -> (bytes, int, int): def convert_image(data: bytes, max_w=256, max_h=256) -> (bytes, int, int):
image: Image.Image = Image.open(BytesIO(data)).convert("RGBA") image: Image.Image = Image.open(BytesIO(data)).convert("RGBA")
new_file = BytesIO() new_file = BytesIO()
image.save(new_file, "png") image.save(new_file, "png")
w, h = image.size w, h = image.size
if w > 256 or h > 256: if w > max_w or h > max_h:
# Set the width and height to lower values so clients wouldn't show them as huge images # Set the width and height to lower values so clients wouldn't show them as huge images
if w > h: if w > h:
h = int(h / (w / 256)) h = int(h / (w / max_w))
w = 256 w = max_w
else: else:
w = int(w / (h / 256)) w = int(w / (h / max_h))
h = 256 h = max_h
return new_file.getvalue(), w, h return new_file.getvalue(), w, h
@ -78,3 +80,15 @@ def make_sticker(mxc: str, width: int, height: int, size: int,
}, },
"msgtype": "m.sticker", "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)

View File

@ -13,6 +13,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from pathlib import Path
from typing import Dict, Optional from typing import Dict, Optional
from hashlib import sha256 from hashlib import sha256
import mimetypes import mimetypes
@ -107,9 +108,11 @@ async def main(args: argparse.Namespace) -> None:
old_stickers = {sticker["id"]: sticker for sticker in pack["stickers"]} old_stickers = {sticker["id"]: sticker for sticker in pack["stickers"]}
pack["stickers"] = [] pack["stickers"] = []
stickers_data: Dict[str, bytes] = {}
for file in sorted(os.listdir(args.path)): for file in sorted(os.listdir(args.path)):
sticker = await upload_sticker(file, args.path, old_stickers=old_stickers) sticker = await upload_sticker(file, args.path, old_stickers=old_stickers)
if sticker: if sticker:
stickers_data[sticker["url"]] = Path(args.path, file).read_bytes()
pack["stickers"].append(sticker) pack["stickers"].append(sticker)
with util.open_utf8(meta_path, "w") as pack_file: with util.open_utf8(meta_path, "w") as pack_file:
@ -122,6 +125,8 @@ async def main(args: argparse.Namespace) -> None:
with util.open_utf8(picker_pack_path, "w") as pack_file: with util.open_utf8(picker_pack_path, "w") as pack_file:
json.dump(pack, pack_file) json.dump(pack, pack_file)
print(f"Copied pack to {picker_pack_path}") 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) util.add_to_index(picker_file_name, args.add_to_index)
@ -138,7 +143,7 @@ parser.add_argument("path", help="Path to the sticker pack directory", type=str)
def cmd(): def cmd():
asyncio.get_event_loop().run_until_complete(main(parser.parse_args())) asyncio.run(main(parser.parse_args()))
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -13,7 +13,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import Dict from typing import Dict, Tuple
import argparse import argparse
import asyncio import asyncio
import os.path import os.path
@ -29,7 +29,7 @@ from telethon.tl.types.messages import StickerSet as StickerSetFull
from .lib import matrix, util from .lib import matrix, util
async def reupload_document(client: TelegramClient, document: Document) -> matrix.StickerInfo: async def reupload_document(client: TelegramClient, document: Document) -> Tuple[matrix.StickerInfo, bytes]:
print(f"Reuploading {document.id}", end="", flush=True) print(f"Reuploading {document.id}", end="", flush=True)
data = await client.download_media(document, file=bytes) data = await client.download_media(document, file=bytes)
print(".", end="", flush=True) print(".", end="", flush=True)
@ -37,7 +37,7 @@ async def reupload_document(client: TelegramClient, document: Document) -> matri
print(".", end="", flush=True) print(".", end="", flush=True)
mxc = await matrix.upload(data, "image/png", f"{document.id}.png") mxc = await matrix.upload(data, "image/png", f"{document.id}.png")
print(".", flush=True) print(".", flush=True)
return util.make_sticker(mxc, width, height, len(data)) return util.make_sticker(mxc, width, height, len(data)), data
def add_meta(document: Document, info: matrix.StickerInfo, pack: StickerSetFull) -> None: def add_meta(document: Document, info: matrix.StickerInfo, pack: StickerSetFull) -> None:
@ -56,10 +56,6 @@ def add_meta(document: Document, info: matrix.StickerInfo, pack: StickerSetFull)
async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir: str) -> None: 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") pack_path = os.path.join(output_dir, f"{pack.set.short_name}.json")
try: try:
os.mkdir(os.path.dirname(pack_path)) os.mkdir(os.path.dirname(pack_path))
@ -79,15 +75,17 @@ async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir
except FileNotFoundError: except FileNotFoundError:
pass pass
stickers_data: Dict[str, bytes] = {}
reuploaded_documents: Dict[int, matrix.StickerInfo] = {} reuploaded_documents: Dict[int, matrix.StickerInfo] = {}
for document in pack.documents: for document in pack.documents:
try: try:
reuploaded_documents[document.id] = already_uploaded[document.id] reuploaded_documents[document.id] = already_uploaded[document.id]
print(f"Skipped reuploading {document.id}") print(f"Skipped reuploading {document.id}")
except KeyError: except KeyError:
reuploaded_documents[document.id] = await reupload_document(client, document) reuploaded_documents[document.id], data = await reupload_document(client, document)
# Always ensure the body and telegram metadata is correct # Always ensure the body and telegram metadata is correct
add_meta(document, reuploaded_documents[document.id], pack) add_meta(document, reuploaded_documents[document.id], pack)
stickers_data[reuploaded_documents[document.id]["url"]] = data
for sticker in pack.packs: for sticker in pack.packs:
if not sticker.emoticon: if not sticker.emoticon:
@ -111,6 +109,7 @@ async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir
}, pack_file, ensure_ascii=False) }, pack_file, ensure_ascii=False)
print(f"Saved {pack.set.title} as {pack.set.short_name}.json") 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) util.add_to_index(os.path.basename(pack_path), output_dir)
@ -162,7 +161,7 @@ async def main(args: argparse.Namespace) -> None:
def cmd() -> None: def cmd() -> None:
asyncio.get_event_loop().run_until_complete(main(parser.parse_args())) asyncio.run(main(parser.parse_args()))
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,22 +1,23 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no">
<title>Maunium sticker picker</title> <title>Maunium sticker picker</title>
<link rel="modulepreload" href="src/widget-api.js"/> <link rel="modulepreload" href="src/widget-api.js"/>
<link rel="modulepreload" href="src/frequently-used.js"/> <link rel="modulepreload" href="src/frequently-used.js"/>
<link rel="modulepreload" href="src/spinner.js"/> <link rel="modulepreload" href="src/spinner.js"/>
<link rel="modulepreload" href="lib/htm/preact.js"/> <link rel="modulepreload" href="src/giphy.js"/>
<link rel="preload" href="packs/index.json" as="fetch" type="application/json" crossorigin/> <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/index.css"/>
<link rel="stylesheet" href="style/spinner.css"/> <link rel="stylesheet" href="style/spinner.css"/>
<script src="src/index.js" type="module"></script> <script src="src/index.js" type="module"></script>
<script nomodule>document.body.innerText = "This sticker picker requires modern JavaScript"</script> <script nomodule>document.body.innerText = "This sticker picker requires modern JavaScript"</script>
</head> </head>
<body> <body>
<noscript>This sticker picker requires JavaScript</noscript> <noscript>This sticker picker requires JavaScript</noscript>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

View File

@ -11,9 +11,9 @@
"sass": "sass --no-source-map --style=compressed style/" "sass": "sass --no-source-map --style=compressed style/"
}, },
"dependencies": { "dependencies": {
"esinstall": "^1.1.7",
"htm": "^3.1.0", "htm": "^3.1.0",
"preact": "^10.5.14", "preact": "^10.5.14",
"esinstall": "^1.1.7",
"sass": "^1.42.1" "sass": "^1.42.1"
} }
} }

55
web/res/giphy-dark.svg Normal file
View File

@ -0,0 +1,55 @@
<?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>

After

Width:  |  Height:  |  Size: 1.2 KiB

54
web/res/giphy-light.svg Normal file
View File

@ -0,0 +1,54 @@
<?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>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 313 B

107
web/src/giphy.js Normal file
View File

@ -0,0 +1,107 @@
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>
`
}
}

View File

@ -13,10 +13,10 @@
// //
// You should have received a copy of the GNU Affero General Public License // 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/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import { html, render, Component } from "../lib/htm/preact.js" import {html, render, Component} from "../lib/htm/preact.js"
import { Spinner } from "./spinner.js" import {Spinner} from "./spinner.js"
import { shouldAutofocusSearchBar, shouldDisplayAutofocusSearchBar, SearchBox } from "./search-box.js" import {SearchBox} from "./search-box.js"
import { checkMobileSafari } from "./user-agent-detect.js" import {giphyIsEnabled, GiphySearchTab, setGiphyAPIKey} from "./giphy.js"
import * as widgetAPI from "./widget-api.js" import * as widgetAPI from "./widget-api.js"
import * as frequent from "./frequently-used.js" import * as frequent from "./frequently-used.js"
@ -29,31 +29,21 @@ const params = new URLSearchParams(document.location.search)
if (params.has('config')) { if (params.has('config')) {
INDEX = params.get("config") INDEX = params.get("config")
} }
// This is updated from packs/index.json
let HOMESERVER_URL = "https://matrix-client.matrix.org"
const makeThumbnailURL = mxc => `${HOMESERVER_URL}/_matrix/media/r0/thumbnail/${mxc.substr(6)}?height=128&width=128&method=scale` const makeThumbnailURL = mxc => `${PACKS_BASE_URL}/thumbnails/${mxc.split("/").slice(-1)[0]}`
// We need to detect iOS webkit because it has a bug related to scrolling non-fixed divs // 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 // This is also used to fix scrolling to sections on Element iOS
const isMobileSafari = checkMobileSafari() const isMobileSafari = navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/)
// 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 supportedThemes = ["light", "dark", "black"]
const defaultState = { const defaultState = {
packs: [], packs: [],
loading: true, filtering: {
error: null, searchTerm: "",
} packs: [],
},
const defaultSearchState = {
searchTerm: null,
filteredPacks: null
} }
class App extends Component { class App extends Component {
@ -61,8 +51,10 @@ class App extends Component {
super(props) super(props)
this.defaultTheme = params.get("theme") this.defaultTheme = params.get("theme")
this.state = { this.state = {
...defaultState, viewingGifs: false,
...defaultSearchState, packs: defaultState.packs,
loading: true,
error: null,
stickersPerRow: parseInt(localStorage.mauStickersPerRow || "4"), stickersPerRow: parseInt(localStorage.mauStickersPerRow || "4"),
theme: localStorage.mauStickerThemeOverride || this.defaultTheme, theme: localStorage.mauStickerThemeOverride || this.defaultTheme,
frequentlyUsed: { frequentlyUsed: {
@ -71,6 +63,7 @@ class App extends Component {
stickerIDs: frequent.get(), stickerIDs: frequent.get(),
stickers: [], stickers: [],
}, },
filtering: defaultState.filtering,
} }
if (!supportedThemes.includes(this.state.theme)) { if (!supportedThemes.includes(this.state.theme)) {
this.state.theme = "light" this.state.theme = "light"
@ -84,7 +77,6 @@ class App extends Component {
this.packListRef = null this.packListRef = null
this.navRef = null this.navRef = null
this.searchStickers = this.searchStickers.bind(this) this.searchStickers = this.searchStickers.bind(this)
this.resetSearch = this.resetSearch.bind(this)
this.sendSticker = this.sendSticker.bind(this) this.sendSticker = this.sendSticker.bind(this)
this.navScroll = this.navScroll.bind(this) this.navScroll = this.navScroll.bind(this)
this.reloadPacks = this.reloadPacks.bind(this) this.reloadPacks = this.reloadPacks.bind(this)
@ -109,33 +101,28 @@ class App extends Component {
localStorage.mauFrequentlyUsedStickerCache = JSON.stringify(stickers.map(sticker => [sticker.id, sticker])) localStorage.mauFrequentlyUsedStickerCache = JSON.stringify(stickers.map(sticker => [sticker.id, sticker]))
} }
// Search searchStickers(e) {
resetSearch() {
this.setState({ ...defaultSearchState })
}
searchStickers(searchTerm) {
const sanitizeString = s => s.toLowerCase().trim() const sanitizeString = s => s.toLowerCase().trim()
const sanitizedSearch = sanitizeString(searchTerm) const searchTerm = sanitizeString(e.target.value)
const allPacks = [this.state.frequentlyUsed, ...this.state.packs] const allPacks = [this.state.frequentlyUsed, ...this.state.packs]
const packsWithFilteredStickers = allPacks.map(pack => ({ const packsWithFilteredStickers = allPacks.map(pack => ({
...pack, ...pack,
stickers: pack.stickers.filter(sticker => stickers: pack.stickers.filter(sticker =>
sanitizeString(sticker.body).includes(sanitizedSearch) || sanitizeString(sticker.body).includes(searchTerm) ||
sanitizeString(sticker.id).includes(sanitizedSearch) sanitizeString(sticker.id).includes(searchTerm)
), ),
})) }))
const filteredPacks = packsWithFilteredStickers.filter(({ stickers }) => !!stickers.length)
this.setState({ searchTerm, filteredPacks }) this.setState({
filtering: {
...this.state.filtering,
searchTerm,
packs: packsWithFilteredStickers.filter(({stickers}) => !!stickers.length),
},
})
} }
// End search
// Settings
setStickersPerRow(val) { setStickersPerRow(val) {
localStorage.mauStickersPerRow = val localStorage.mauStickersPerRow = val
document.documentElement.style.setProperty("--stickers-per-row", localStorage.mauStickersPerRow) document.documentElement.style.setProperty("--stickers-per-row", localStorage.mauStickersPerRow)
@ -148,30 +135,26 @@ class App extends Component {
setTheme(theme) { setTheme(theme) {
if (theme === "default") { if (theme === "default") {
delete localStorage.mauStickerThemeOverride delete localStorage.mauStickerThemeOverride
this.setState({ theme: this.defaultTheme }) this.setState({theme: this.defaultTheme})
} else { } else {
localStorage.mauStickerThemeOverride = theme localStorage.mauStickerThemeOverride = theme
this.setState({ theme: theme }) this.setState({theme: theme})
} }
} }
setAutofocusSearchBar(checked) {
localStorage.mauAutofocusSearchBar = checked
}
// End settings
reloadPacks() { reloadPacks() {
this.imageObserver.disconnect() this.imageObserver.disconnect()
this.sectionObserver.disconnect() this.sectionObserver.disconnect()
this.setState({ packs: defaultState.packs }) this.setState({
this.resetSearch() packs: defaultState.packs,
filtering: defaultState.filtering,
})
this._loadPacks(true) this._loadPacks(true)
} }
_loadPacks(disableCache = false) { _loadPacks(disableCache = false) {
const cache = disableCache ? "no-cache" : undefined const cache = disableCache ? "no-cache" : undefined
fetch(INDEX, { cache }).then(async indexRes => { fetch(INDEX, {cache}).then(async indexRes => {
if (indexRes.status >= 400) { if (indexRes.status >= 400) {
this.setState({ this.setState({
loading: false, loading: false,
@ -180,14 +163,16 @@ class App extends Component {
return return
} }
const indexData = await indexRes.json() const indexData = await indexRes.json()
HOMESERVER_URL = indexData.homeserver_url || HOMESERVER_URL if (indexData.giphy_api_key !== undefined) {
setGiphyAPIKey(indexData.giphy_api_key, indexData.giphy_mxc_prefix)
}
// TODO only load pack metadata when scrolled into view? // TODO only load pack metadata when scrolled into view?
for (const packFile of indexData.packs) { for (const packFile of indexData.packs) {
let packRes let packRes
if (packFile.startsWith("https://") || packFile.startsWith("http://")) { if (packFile.startsWith("https://") || packFile.startsWith("http://")) {
packRes = await fetch(packFile, { cache }) packRes = await fetch(packFile, {cache})
} else { } else {
packRes = await fetch(`${PACKS_BASE_URL}/${packFile}`, { cache }) packRes = await fetch(`${PACKS_BASE_URL}/${packFile}`, {cache})
} }
const packData = await packRes.json() const packData = await packRes.json()
for (const sticker of packData.stickers) { for (const sticker of packData.stickers) {
@ -199,7 +184,7 @@ class App extends Component {
}) })
} }
this.updateFrequentlyUsed() this.updateFrequentlyUsed()
}, error => this.setState({ loading: false, error })) }, error => this.setState({loading: false, error}))
} }
componentDidMount() { componentDidMount() {
@ -231,10 +216,10 @@ class App extends Component {
let maxXElem = null let maxXElem = null
for (const entry of intersections) { for (const entry of intersections) {
const packID = entry.target.getAttribute("data-pack-id") const packID = entry.target.getAttribute("data-pack-id")
const navElement = document.getElementById(`nav-${packID}`) if (!packID) {
if (!navElement) {
continue continue
} }
const navElement = document.getElementById(`nav-${packID}`)
if (entry.isIntersecting) { if (entry.isIntersecting) {
navElement.classList.add("visible") navElement.classList.add("visible")
const bb = navElement.getBoundingClientRect() const bb = navElement.getBoundingClientRect()
@ -250,9 +235,9 @@ class App extends Component {
} }
} }
if (minXElem !== null) { if (minXElem !== null) {
minXElem.scrollIntoView({ inline: "start" }) minXElem.scrollIntoView({inline: "start"})
} else if (maxXElem !== null) { } else if (maxXElem !== null) {
maxXElem.scrollIntoView({ inline: "end" }) maxXElem.scrollIntoView({inline: "end"})
} }
} }
@ -278,7 +263,6 @@ class App extends Component {
const sticker = this.stickersByID.get(id) const sticker = this.stickersByID.get(id)
frequent.add(id) frequent.add(id)
this.updateFrequentlyUsed() this.updateFrequentlyUsed()
this.resetSearch()
widgetAPI.sendSticker(sticker) widgetAPI.sendSticker(sticker)
} }
@ -288,43 +272,67 @@ class App extends Component {
render() { render() {
const theme = `theme-${this.state.theme}` const theme = `theme-${this.state.theme}`
const filterActive = !!this.state.filtering.searchTerm
const filterActive = !!this.state.filteredPacks const packs = filterActive
const packs = filterActive ? this.state.filteredPacks : [this.state.frequentlyUsed, ...this.state.packs] ? this.state.filtering.packs
const noPacksForSearch = filterActive && packs.length === 0 : [this.state.frequentlyUsed, ...this.state.packs]
if (this.state.loading) { 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) { } else if (this.state.error) {
return html`<main class="error ${theme}"> return html`
<h1>Failed to load packs</h1> <main class="error ${theme}">
<p>${this.state.error}</p> <h1>Failed to load packs</h1>
</main>` <p>${this.state.error}</p>
</main>
`
} else if (this.state.packs.length === 0) { } 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>
`
} }
return html`<main class="has-content ${theme}"> const onClickOverride = this.state.viewingGifs
<nav onWheel=${this.navScroll} ref=${elem => this.navRef = elem}> ? (evt, packID) => {
<${NavBarItem} pack=${this.state.frequentlyUsed} iconOverride="recent" /> evt.preventDefault()
${this.state.packs.map(pack => html`<${NavBarItem} id=${pack.id} pack=${pack}/>`)} this.setState({viewingGifs: false}, () => {
<${NavBarItem} pack=${{ id: "settings", title: "Settings" }} iconOverride="settings" /> scrollToSection(null, packID)
</nav> })
<${SearchBox} } : null
value=${this.state.searchTerm} const switchToGiphy = () => this.setState({viewingGifs: true, filtering: defaultState.filtering})
onSearch=${this.searchStickers}
onReset=${this.resetSearch} return html`
/> <main class="has-content ${theme}">
<div class="pack-list ${isMobileSafari ? "ios-safari-hack" : ""}" ref=${elem => this.packListRef = elem}> <nav onWheel=${this.navScroll} ref=${elem => this.navRef = elem}>
${noPacksForSearch ? html`<div class="search-empty"><h1>No stickers match your search</h1></div>` : null} ${giphyIsEnabled() && html`
${packs.map(pack => html`<${Pack} id=${pack.id} pack=${pack} send=${this.sendSticker} />`)} <${NavBarItem} pack=${{id: "giphy", title: "GIPHY"}} iconOverride="giphy" onClickOverride=${switchToGiphy} extraClass=${this.state.viewingGifs ? "visible" : ""}/>
<${Settings} app=${this}/> `}
</div> <${NavBarItem} pack=${this.state.frequentlyUsed} iconOverride="recent" onClickOverride=${onClickOverride}/>
</main>` ${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>`
} }
} }
const Settings = ({ app }) => html` const Settings = ({app}) => html`
<section class="stickerpack settings" id="pack-settings" data-pack-id="settings"> <section class="stickerpack settings" id="pack-settings" data-pack-id="settings">
<h1>Settings</h1> <h1>Settings</h1>
<div class="settings-list"> <div class="settings-list">
@ -333,7 +341,7 @@ const Settings = ({ app }) => html`
<label for="stickers-per-row">Stickers per row: ${app.state.stickersPerRow}</label> <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" <input type="range" min=2 max=10 id="stickers-per-row" id="stickers-per-row"
value=${app.state.stickersPerRow} value=${app.state.stickersPerRow}
onInput=${evt => app.setStickersPerRow(evt.target.value)} /> onInput=${evt => app.setStickersPerRow(evt.target.value)}/>
</div> </div>
<div> <div>
<label for="theme">Theme: </label> <label for="theme">Theme: </label>
@ -344,14 +352,6 @@ const Settings = ({ app }) => html`
<option value="black">Black</option> <option value="black">Black</option>
</select> </select>
</div> </div>
${displayAutofocusOption ? html`<div>
Autofocus search bar:
<input
type="checkbox"
checked=${shouldAutofocusOption}
onChange=${evt => app.setAutofocusSearchBar(evt.target.checked)}
/>
</div>` : null}
</div> </div>
</section> </section>
` `
@ -360,13 +360,15 @@ const Settings = ({ app }) => html`
// open the link in the browser instead of just scrolling there, so we need to scroll manually: // open the link in the browser instead of just scrolling there, so we need to scroll manually:
const scrollToSection = (evt, id) => { const scrollToSection = (evt, id) => {
const pack = document.getElementById(`pack-${id}`) const pack = document.getElementById(`pack-${id}`)
pack.scrollIntoView({ block: "start", behavior: "instant" }) if (pack) {
evt.preventDefault() pack.scrollIntoView({block: "start", behavior: "instant"})
}
evt?.preventDefault()
} }
const NavBarItem = ({ pack, iconOverride = null }) => html` 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} <a href="#pack-${pack.id}" id="nav-${pack.id}" data-pack-id=${pack.id} title=${pack.title} class="${extraClass}"
onClick=${isMobileSafari ? (evt => scrollToSection(evt, pack.id)) : undefined}> onClick=${onClickOverride ? (evt => onClickOverride(evt, pack.id)) : (isMobileSafari ? (evt => scrollToSection(evt, pack.id)) : undefined)}>
<div class="sticker"> <div class="sticker">
${iconOverride ? html` ${iconOverride ? html`
<span class="icon icon-${iconOverride}"/> <span class="icon icon-${iconOverride}"/>
@ -378,7 +380,7 @@ const NavBarItem = ({ pack, iconOverride = null }) => html`
</a> </a>
` `
const Pack = ({ pack, send }) => html` const Pack = ({pack, send}) => html`
<section class="stickerpack" id="pack-${pack.id}" data-pack-id=${pack.id}> <section class="stickerpack" id="pack-${pack.id}" data-pack-id=${pack.id}>
<h1>${pack.title}</h1> <h1>${pack.title}</h1>
<div class="sticker-list"> <div class="sticker-list">
@ -389,10 +391,10 @@ const Pack = ({ pack, send }) => html`
</section> </section>
` `
const Sticker = ({ content, send }) => html` const Sticker = ({content, send}) => html`
<div class="sticker" onClick=${send} data-sticker-id=${content.id}> <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> </div>
` `
render(html`<${App} />`, document.body) render(html`<${App}/>`, document.body)

View File

@ -13,77 +13,14 @@
// //
// You should have received a copy of the GNU Affero General Public License // 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/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import { html, Component } from "../lib/htm/preact.js" import {html} from "../lib/htm/preact.js"
import { checkMobileSafari, checkAndroid } from "./user-agent-detect.js"
export function shouldDisplayAutofocusSearchBar() { export const SearchBox = ({onInput, onKeyUp, value, placeholder = 'Find stickers'}) => {
return !checkMobileSafari() && !checkAndroid() const component = html`
} <div class="search-box">
<input type="text" placeholder=${placeholder} value=${value} onInput=${onInput} onKeyUp=${onKeyUp}/>
export function shouldAutofocusSearchBar() { <span class="icon icon-search"/>
return localStorage.mauAutofocusSearchBar === 'true' && shouldDisplayAutofocusSearchBar() </div>
} `
return component
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>
`
}
} }

View File

@ -1,18 +0,0 @@
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()

View File

@ -13,8 +13,6 @@
// //
// You should have received a copy of the GNU Affero General Public License // 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/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import { focusSearchBar } from "./search-box.js"
let widgetId = null let widgetId = null
window.onmessage = event => { window.onmessage = event => {
@ -35,12 +33,10 @@ window.onmessage = event => {
widgetId = request.widgetId widgetId = request.widgetId
} }
let response = {} let response
if (request.action === "visibility") { // visibility of the widget changed if (request.action === "visibility") {
if (request.visible) { response = {}
focusSearchBar() // we have to re-focus the search bar when appropriate
}
} else if (request.action === "capabilities") { } else if (request.action === "capabilities") {
response = { capabilities: ["m.sticker"] } response = { capabilities: ["m.sticker"] }
} else { } else {
@ -64,8 +60,9 @@ export function sendSticker(content) {
const widgetData = { const widgetData = {
...data, ...data,
description: content.body, description: content.body,
file: `${content.id}.png`, file: content.filename ?? `${content.id}.png`,
} }
delete widgetData.content.filename
// Element iOS explodes if there are extra fields present // Element iOS explodes if there are extra fields present
delete widgetData.content["net.maunium.telegram.sticker"] delete widgetData.content["net.maunium.telegram.sticker"]

View File

@ -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-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%} *{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%}

View File

@ -71,12 +71,23 @@ main.theme-dark
--text-color: white --text-color: white
background-color: #22262e background-color: #22262e
.icon.icon-giphy
background-image: url(../res/giphy-dark.svg)
main.theme-black main.theme-black
--highlight-color: #222 --highlight-color: #222
--search-box-color: var(--highlight-color) --search-box-color: var(--highlight-color)
--text-color: white --text-color: white
background-color: black background-color: black
.icon.icon-giphy
background-image: url(../res/giphy-dark.svg)
div.powered-by-giphy
padding: 1rem
> img
width: 100%
.icon .icon
width: 100% width: 100%
height: 100% height: 100%
@ -92,11 +103,12 @@ main.theme-black
&.icon-recent &.icon-recent
--icon-image: url(../res/recent.svg) --icon-image: url(../res/recent.svg)
&.icon-search &.icon.icon-search
--icon-image: url(../res/search.svg) --icon-image: url(../res/search.svg)
&.icon-reset &.icon.icon-giphy
--icon-image: url(../res/reset.svg) background: center / contain no-repeat url(../res/giphy-light.svg)
mask: unset
nav nav
display: flex display: flex
@ -143,6 +155,12 @@ section.stickerpack
> h1 > h1
margin: 0 0 0 .75rem margin: 0 0 0 .75rem
section.stickerpack#pack-giphy
display: flex
justify-content: space-between
flex-direction: column
min-height: 100%
div.sticker div.sticker
display: flex display: flex
padding: 4px padding: 4px
@ -169,49 +187,30 @@ div.sticker
margin: 15% margin: 15%
div.search-box div.search-box
position: relative
display: flex 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
&:focus-within >input[type="text"]
opacity: 1 flex-grow: 1
border: 1px solid var(--text-color) background-color: var(--search-box-color)
&:not(:focus-within):hover
opacity: .7
input,.icon-display
color: var(--text-color)
outline: none outline: none
border: none border: none
border-radius: .25rem
height: $search-box-input-height height: $search-box-input-height
margin: 0
padding: $search-box-input-padding padding: $search-box-input-padding
background-color: transparent padding-right: calc(#{$search-box-icon-size} + #{$search-box-input-padding})
-webkit-tap-highlight-color: transparent margin: $search-box-input-margin
.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 font-size: 1rem
color: var(--text-color)
>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
div.settings-list div.settings-list
display: flex display: flex
@ -224,5 +223,5 @@ div.settings-list
padding: .5rem padding: .5rem
border-radius: .25rem border-radius: .25rem
input:not([type="checkbox"]) input
width: 100% width: 100%

View File

@ -16,13 +16,13 @@
resolve "^1.17.0" resolve "^1.17.0"
"@rollup/plugin-inject@^4.0.0", "@rollup/plugin-inject@^4.0.2": "@rollup/plugin-inject@^4.0.0", "@rollup/plugin-inject@^4.0.2":
version "4.0.2" version "4.0.4"
resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.2.tgz#55b21bb244a07675f7fdde577db929c82fc17395" resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.4.tgz#fbeee66e9a700782c4f65c8b0edbafe58678fbc2"
integrity sha512-TSLMA8waJ7Dmgmoc8JfPnwUwVZgLjjIAM6MqeIFqPO2ODK36JqE0Cf2F54UTgCUuW8da93Mvoj75a6KAVWgylw== integrity sha512-4pbcU4J/nS+zuHk+c+OL3WtmEQhqxlZ9uqfjQMQDOHOPld7PsCd8k5LWs8h5wjwJN7MgnAn768F2sDxEP4eNFQ==
dependencies: dependencies:
"@rollup/pluginutils" "^3.0.4" "@rollup/pluginutils" "^3.1.0"
estree-walker "^1.0.1" estree-walker "^2.0.1"
magic-string "^0.25.5" magic-string "^0.25.7"
"@rollup/plugin-json@^4.0.0": "@rollup/plugin-json@^4.0.0":
version "4.1.0" version "4.1.0"
@ -51,7 +51,7 @@
"@rollup/pluginutils" "^3.1.0" "@rollup/pluginutils" "^3.1.0"
magic-string "^0.25.7" magic-string "^0.25.7"
"@rollup/pluginutils@^3.0.4", "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
@ -61,9 +61,9 @@
picomatch "^2.2.2" picomatch "^2.2.2"
"@types/estree@*": "@types/estree@*":
version "0.0.50" version "1.0.5"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@types/estree@0.0.39": "@types/estree@0.0.39":
version "0.0.39" version "0.0.39"
@ -71,9 +71,11 @@
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
"@types/node@*": "@types/node@*":
version "16.10.2" version "20.12.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.2.tgz#5764ca9aa94470adb4e1185fe2e9f19458992b2e" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050"
integrity sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ== integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==
dependencies:
undici-types "~5.26.4"
"@types/resolve@1.17.1": "@types/resolve@1.17.1":
version "1.17.1" version "1.17.1"
@ -82,21 +84,31 @@
dependencies: dependencies:
"@types/node" "*" "@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: anymatch@~3.1.2:
version "3.1.2" version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies: dependencies:
normalize-path "^3.0.0" normalize-path "^3.0.0"
picomatch "^2.0.4" picomatch "^2.0.4"
assert@^1.4.1: assert@^1.4.1:
version "1.5.0" version "1.5.1"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.1.tgz#038ab248e4ff078e7bc2485ba6e6388466c78f76"
integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== integrity sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==
dependencies: dependencies:
object-assign "^4.1.1" object.assign "^4.1.4"
util "0.10.3" util "^0.10.4"
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.2" version "1.0.2"
@ -104,9 +116,9 @@ balanced-match@^1.0.0:
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
binary-extensions@^2.0.0: binary-extensions@^2.0.0:
version "2.2.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.11" version "1.1.11"
@ -124,19 +136,30 @@ braces@~3.0.2:
fill-range "^7.0.1" fill-range "^7.0.1"
builtin-modules@^3.1.0, builtin-modules@^3.2.0: builtin-modules@^3.1.0, builtin-modules@^3.2.0:
version "3.2.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
builtins@^1.0.3: builtins@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= 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"
"chokidar@>=3.0.0 <4.0.0": "chokidar@>=3.0.0 <4.0.0":
version "3.5.2" version "3.6.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
dependencies: dependencies:
anymatch "~3.1.2" anymatch "~3.1.2"
braces "~3.0.2" braces "~3.0.2"
@ -149,19 +172,19 @@ builtins@^1.0.3:
fsevents "~2.3.2" fsevents "~2.3.2"
cjs-module-lexer@^1.2.1: cjs-module-lexer@^1.2.1:
version "1.2.2" version "1.3.1"
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==
commondir@^1.0.1: commondir@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
cross-spawn@^7.0.3: cross-spawn@^7.0.3:
version "7.0.3" version "7.0.3"
@ -173,9 +196,39 @@ cross-spawn@^7.0.3:
which "^2.0.1" which "^2.0.1"
deepmerge@^4.2.2: deepmerge@^4.2.2:
version "4.2.2" version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== 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==
es-module-lexer@^0.6.0: es-module-lexer@^0.6.0:
version "0.6.0" version "0.6.0"
@ -243,7 +296,7 @@ fill-range@^7.0.1:
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.1.2: fsevents@~2.1.2:
version "2.1.3" version "2.1.3"
@ -251,14 +304,25 @@ fsevents@~2.1.2:
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
fsevents@~2.3.2: fsevents@~2.3.2:
version "2.3.2" version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.1: function-bind@^1.1.2:
version "1.1.1" version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 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"
get-stream@^6.0.0: get-stream@^6.0.0:
version "6.0.1" version "6.0.1"
@ -273,38 +337,67 @@ glob-parent@~5.1.2:
is-glob "^4.0.1" is-glob "^4.0.1"
glob@^7.1.3, glob@^7.1.6: glob@^7.1.3, glob@^7.1.6:
version "7.2.0" version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
dependencies: dependencies:
fs.realpath "^1.0.0" fs.realpath "^1.0.0"
inflight "^1.0.4" inflight "^1.0.4"
inherits "2" inherits "2"
minimatch "^3.0.4" minimatch "^3.1.1"
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
has@^1.0.3: gopd@^1.0.1:
version "1.0.3" version "1.0.1"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
dependencies: dependencies:
function-bind "^1.1.1" 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:
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==
dependencies:
function-bind "^1.1.2"
htm@^3.1.0: htm@^3.1.0:
version "3.1.0" version "3.1.1"
resolved "https://registry.yarnpkg.com/htm/-/htm-3.1.0.tgz#0c305493b60da9f6ed097a2aaf4c994bd85ea022" resolved "https://registry.yarnpkg.com/htm/-/htm-3.1.1.tgz#49266582be0dc66ed2235d5ea892307cc0c24b78"
integrity sha512-L0s3Sid5r6YwrEvkig14SK3Emmc+kIjlfLhEGn2Vy3bk21JyDEes4MoDsbJk6luaPp8bugErnxPz86ZuAw6e5Q== integrity sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==
human-signals@^2.1.0: human-signals@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== 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: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies: dependencies:
once "^1.3.0" once "^1.3.0"
wrappy "1" wrappy "1"
@ -314,10 +407,10 @@ inherits@2:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.1: inherits@2.0.3:
version "2.0.1" version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
is-binary-path@~2.1.0: is-binary-path@~2.1.0:
version "2.1.0" version "2.1.0"
@ -326,17 +419,17 @@ is-binary-path@~2.1.0:
dependencies: dependencies:
binary-extensions "^2.0.0" binary-extensions "^2.0.0"
is-core-module@^2.2.0: is-core-module@^2.13.0:
version "2.7.0" version "2.13.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.7.0.tgz#3c0ef7d31b4acfc574f80c58409d568a836848e3" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
integrity sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ== integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
dependencies: dependencies:
has "^1.0.3" hasown "^2.0.0"
is-extglob@^2.1.1: is-extglob@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.1, is-glob@~4.0.1: is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.3" version "4.0.3"
@ -348,7 +441,7 @@ is-glob@^4.0.1, is-glob@~4.0.1:
is-module@^1.0.0: is-module@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==
is-number@^7.0.0: is-number@^7.0.0:
version "7.0.0" version "7.0.0"
@ -377,19 +470,19 @@ is-valid-identifier@^2.0.2:
isexe@^2.0.0: isexe@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
kleur@^4.1.1: kleur@^4.1.1:
version "4.1.4" version "4.1.5"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
magic-string@^0.25.5, magic-string@^0.25.7: magic-string@^0.25.7:
version "0.25.7" version "0.25.9"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
dependencies: dependencies:
sourcemap-codec "^1.4.4" sourcemap-codec "^1.4.8"
merge-stream@^2.0.0: merge-stream@^2.0.0:
version "2.0.0" version "2.0.0"
@ -401,10 +494,10 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimatch@^3.0.4: minimatch@^3.1.1:
version "3.0.4" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies: dependencies:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
@ -425,15 +518,25 @@ npm-run-path@^4.0.1:
dependencies: dependencies:
path-key "^3.0.0" path-key "^3.0.0"
object-assign@^4.1.1: object-keys@^1.1.1:
version "4.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 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"
once@^1.3.0: once@^1.3.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies: dependencies:
wrappy "1" wrappy "1"
@ -447,27 +550,27 @@ onetime@^5.1.2:
path-is-absolute@^1.0.0: path-is-absolute@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
path-key@^3.0.0, path-key@^3.1.0: path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.6: path-parse@^1.0.7:
version "1.0.7" version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.0: picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.0:
version "2.3.0" version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
preact@^10.5.14: preact@^10.5.14:
version "10.5.14" version "10.22.0"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.5.14.tgz#0b14a2eefba3c10a57116b90d1a65f5f00cd2701" resolved "https://registry.yarnpkg.com/preact/-/preact-10.22.0.tgz#a50f38006ae438d255e2631cbdaf7488e6dd4e16"
integrity sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ== integrity sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==
readdirp@~3.6.0: readdirp@~3.6.0:
version "3.6.0" version "3.6.0"
@ -477,12 +580,13 @@ readdirp@~3.6.0:
picomatch "^2.2.1" picomatch "^2.2.1"
resolve@^1.17.0, resolve@^1.20.0: resolve@^1.17.0, resolve@^1.20.0:
version "1.20.0" version "1.22.8"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
dependencies: dependencies:
is-core-module "^2.2.0" is-core-module "^2.13.0"
path-parse "^1.0.6" path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
rimraf@^3.0.0: rimraf@^3.0.0:
version "3.0.2" version "3.0.2"
@ -506,11 +610,25 @@ rollup@~2.37.1:
fsevents "~2.1.2" fsevents "~2.1.2"
sass@^1.42.1: sass@^1.42.1:
version "1.42.1" version "1.77.2"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.42.1.tgz#5ab17bebc1cb1881ad2e0c9a932c66ad64e441e2" resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.2.tgz#18d4ed2eefc260cdc8099c5439ec1303fd5863aa"
integrity sha512-/zvGoN8B7dspKc5mC6HlaygyCBRvnyzzgD5khiaCfglWztY99cYoiTUksVx11NlnemrcfH5CEaCpsUKoW0cQqg== integrity sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==
dependencies: dependencies:
chokidar ">=3.0.0 <4.0.0" 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: shebang-command@^2.0.0:
version "2.0.0" version "2.0.0"
@ -525,16 +643,21 @@ shebang-regex@^3.0.0:
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
signal-exit@^3.0.3: signal-exit@^3.0.3:
version "3.0.5" version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
slash@~3.0.0: slash@~3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
sourcemap-codec@^1.4.4: "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:
version "1.4.8" version "1.4.8"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
@ -544,6 +667,11 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== 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: to-regex-range@^5.0.1:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@ -551,24 +679,32 @@ to-regex-range@^5.0.1:
dependencies: dependencies:
is-number "^7.0.0" is-number "^7.0.0"
util@0.10.3: undici-types@~5.26.4:
version "0.10.3" version "5.26.5"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= 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==
dependencies: dependencies:
inherits "2.0.1" inherits "2.0.3"
validate-npm-package-name@^3.0.0: validate-npm-package-name@^3.0.0:
version "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" resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e"
integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= integrity sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==
dependencies: dependencies:
builtins "^1.0.3" builtins "^1.0.3"
vm2@^3.9.2: vm2@^3.9.2:
version "3.9.3" version "3.9.19"
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.3.tgz#29917f6cc081cc43a3f580c26c5b553fd3c91f40" resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.19.tgz#be1e1d7a106122c6c492b4d51c2e8b93d3ed6a4a"
integrity sha512-smLS+18RjXYMl9joyJxMNI9l4w7biW8ilSDaVRvFBDwOH8P0BK1ognFQTpg0wyQ6wIKLTblHJvROW692L/E53Q== integrity sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==
dependencies:
acorn "^8.7.0"
acorn-walk "^8.2.0"
which@^2.0.1: which@^2.0.1:
version "2.0.2" version "2.0.2"
@ -580,4 +716,4 @@ which@^2.0.1:
wrappy@1: wrappy@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==