From f94d84b0440cc1873d2d1b28ac4c7eea817527b8 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 13 Jan 2025 18:13:06 +0200 Subject: [PATCH 01/78] web/timeline: add validation for per-message profiles --- web/src/ui/timeline/content/index.ts | 10 +++++++++- web/src/util/validation.ts | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/web/src/ui/timeline/content/index.ts b/web/src/ui/timeline/content/index.ts index 7bd981b..221c6bd 100644 --- a/web/src/ui/timeline/content/index.ts +++ b/web/src/ui/timeline/content/index.ts @@ -118,5 +118,13 @@ export function getPerMessageProfile(evt: MemDBEvent | null): BeeperPerMessagePr if (evt === null || evt.type !== "m.room.message" && evt.type !== "m.sticker") { return undefined } - return (evt.content as MessageEventContent)["com.beeper.per_message_profile"] + const profile = (evt.content as MessageEventContent)["com.beeper.per_message_profile"] + if (profile?.displayname && typeof profile.displayname !== "string") { + return undefined + } else if (profile?.avatar_url && typeof profile.avatar_url !== "string") { + return undefined + } else if (profile?.id && typeof profile.id !== "string") { + return undefined + } + return profile } diff --git a/web/src/util/validation.ts b/web/src/util/validation.ts index 260f94d..db5933a 100644 --- a/web/src/util/validation.ts +++ b/web/src/util/validation.ts @@ -88,7 +88,7 @@ export function getServerName(userID: UserID): string { } export function getDisplayname(userID: UserID, profile?: UserProfile | null): string { - return profile?.displayname || getLocalpart(userID) + return ensureString(profile?.displayname) || getLocalpart(userID) } export function parseMXC(mxc: unknown): [string, string] | [] { From 5bb28d32161e6ac19b4c574f7218e9cffb44dd6e Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Thu, 23 Jan 2025 15:14:49 -0700 Subject: [PATCH 02/78] web/composer: prevent sending when loading media (#590) --- web/src/ui/composer/MessageComposer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/ui/composer/MessageComposer.tsx b/web/src/ui/composer/MessageComposer.tsx index f9437b3..87749c6 100644 --- a/web/src/ui/composer/MessageComposer.tsx +++ b/web/src/ui/composer/MessageComposer.tsx @@ -166,7 +166,7 @@ const MessageComposer = () => { const canSend = Boolean(state.text || state.media || state.location) const onClickSend = (evt: React.FormEvent) => { evt.preventDefault() - if (!canSend) { + if (!canSend || loadingMedia) { return } doSendMessage(state) From 4649689b72ab0cf9f793edac0dcdd8039ba9da09 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 20 Jan 2025 16:56:27 +0200 Subject: [PATCH 03/78] dependencies: update --- desktop/go.mod | 23 +- desktop/go.sum | 36 +- go.mod | 16 +- go.sum | 25 +- web/package-lock.json | 825 ++++++++++++++++++++++-------------------- web/package.json | 2 +- 6 files changed, 492 insertions(+), 435 deletions(-) diff --git a/desktop/go.mod b/desktop/go.mod index 2e38a85..5dfcb56 100644 --- a/desktop/go.mod +++ b/desktop/go.mod @@ -2,13 +2,13 @@ module go.mau.fi/gomuks/desktop go 1.23.0 -toolchain go1.23.3 +toolchain go1.23.5 -require github.com/wailsapp/wails/v3 v3.0.0-alpha.8.3 +require github.com/wailsapp/wails/v3 v3.0.0-alpha.9 require ( - go.mau.fi/gomuks v0.3.1 - go.mau.fi/util v0.8.4-0.20250106152331-30b8c95e7d7a + go.mau.fi/gomuks v0.4.0 + go.mau.fi/util v0.8.4 ) require ( @@ -43,7 +43,7 @@ require ( github.com/leaanthony/u v1.1.0 // indirect github.com/lmittmann/tint v1.0.4 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.24 // indirect github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect @@ -57,26 +57,27 @@ require ( github.com/skeema/knownhosts v1.2.2 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/wailsapp/go-webview2 v1.0.18 // indirect + github.com/wailsapp/go-webview2 v1.0.19 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/yuin/goldmark v1.7.8 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect golang.org/x/crypto v0.32.0 // indirect - golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect golang.org/x/image v0.23.0 // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.33.0 // indirect + golang.org/x/net v0.34.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.28.0 // indirect + golang.org/x/tools v0.29.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - maunium.net/go/mautrix v0.22.2-0.20250106152426-68eaa9d1df1f // indirect + maunium.net/go/mautrix v0.23.0 // indirect mvdan.cc/xurls/v2 v2.6.0 // indirect ) diff --git a/desktop/go.sum b/desktop/go.sum index c0fdeb3..2611697 100644 --- a/desktop/go.sum +++ b/desktop/go.sum @@ -100,8 +100,9 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 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= @@ -147,23 +148,24 @@ 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 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 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= -github.com/wailsapp/go-webview2 v1.0.18 h1:SSSCoLA+MYikSp1U0WmvELF/4c3x5kH8Vi31TKyZ4yk= -github.com/wailsapp/go-webview2 v1.0.18/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= +github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU= +github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v3 v3.0.0-alpha.8.3 h1:9aCL0IXD60A5iscQ/ps6f3ti3IlaoG6LQe0RZ9JkueU= -github.com/wailsapp/wails/v3 v3.0.0-alpha.8.3/go.mod h1:9Ca1goy5oqxmy8Oetb8Tchkezcx4tK03DK+SqYByu5Y= +github.com/wailsapp/wails/v3 v3.0.0-alpha.9 h1:b8CfRrhPno8Fra0xFp4Ifyj+ogmXBc35rsQWvcrHtsI= +github.com/wailsapp/wails/v3 v3.0.0-alpha.9/go.mod h1:dSv6s722nSWaUyUiapAM1DHc5HKggNGY1a79shO85/g= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -go.mau.fi/util v0.8.4-0.20250106152331-30b8c95e7d7a h1:D9RCHBFjxah9F/YB7amvRJjT2IEOFWcz8jpcEY8dBV0= -go.mau.fi/util v0.8.4-0.20250106152331-30b8c95e7d7a/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY= +go.mau.fi/util v0.8.4 h1:mVKlJcXWfVo8ZW3f4vqtjGpqtZqJvX4ETekxawt2vnQ= +go.mau.fi/util v0.8.4/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -173,8 +175,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -189,8 +191,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -237,13 +239,15 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -252,7 +256,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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.22.2-0.20250106152426-68eaa9d1df1f h1:+nAznNCSCm+P4am9CmguSfNfcbpA7qtTzJQrwCP8aHM= -maunium.net/go/mautrix v0.22.2-0.20250106152426-68eaa9d1df1f/go.mod h1:FmwzK7RSzrd1OfGDgJzFWXl7nYmYm8/P0Y77sy/A1Uw= +maunium.net/go/mautrix v0.23.0 h1:HNlR19eew5lvrNSL2muhExaGhYdaGk5FfEiA82QqUP4= +maunium.net/go/mautrix v0.23.0/go.mod h1:AGnnaz3ylGikUo1I1MJVn9QLsl2No1/ZNnGDyO0QD5s= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= diff --git a/go.mod b/go.mod index c9e7622..404b444 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module go.mau.fi/gomuks go 1.23.0 -toolchain go1.23.4 +toolchain go1.23.5 require ( github.com/alecthomas/chroma/v2 v2.15.0 @@ -17,15 +17,15 @@ require ( github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 github.com/yuin/goldmark v1.7.8 - go.mau.fi/util v0.8.4-0.20250106152331-30b8c95e7d7a + go.mau.fi/util v0.8.4 go.mau.fi/zeroconfig v0.1.3 golang.org/x/crypto v0.32.0 golang.org/x/image v0.23.0 - golang.org/x/net v0.33.0 + golang.org/x/net v0.34.0 golang.org/x/text v0.21.0 gopkg.in/yaml.v3 v3.0.1 maunium.net/go/mauflag v1.0.0 - maunium.net/go/mautrix v0.22.2-0.20250106152426-68eaa9d1df1f + maunium.net/go/mautrix v0.23.0 mvdan.cc/xurls/v2 v2.6.0 ) @@ -33,13 +33,13 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect github.com/rs/xid v1.6.0 // indirect github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect - golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect golang.org/x/sys v0.29.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect ) diff --git a/go.sum b/go.sum index ee83488..3e4d434 100644 --- a/go.sum +++ b/go.sum @@ -31,11 +31,13 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 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 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 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/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 h1:ah1dvbqPMN5+ocrg/ZSgZ6k8bOk+kcZQ7fnyx6UvOm4= @@ -57,24 +59,25 @@ 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 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 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= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -go.mau.fi/util v0.8.4-0.20250106152331-30b8c95e7d7a h1:D9RCHBFjxah9F/YB7amvRJjT2IEOFWcz8jpcEY8dBV0= -go.mau.fi/util v0.8.4-0.20250106152331-30b8c95e7d7a/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY= +go.mau.fi/util v0.8.4 h1:mVKlJcXWfVo8ZW3f4vqtjGpqtZqJvX4ETekxawt2vnQ= +go.mau.fi/util v0.8.4/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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= @@ -91,7 +94,7 @@ 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/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.22.2-0.20250106152426-68eaa9d1df1f h1:+nAznNCSCm+P4am9CmguSfNfcbpA7qtTzJQrwCP8aHM= -maunium.net/go/mautrix v0.22.2-0.20250106152426-68eaa9d1df1f/go.mod h1:FmwzK7RSzrd1OfGDgJzFWXl7nYmYm8/P0Y77sy/A1Uw= +maunium.net/go/mautrix v0.23.0 h1:HNlR19eew5lvrNSL2muhExaGhYdaGk5FfEiA82QqUP4= +maunium.net/go/mautrix v0.23.0/go.mod h1:AGnnaz3ylGikUo1I1MJVn9QLsl2No1/ZNnGDyO0QD5s= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= diff --git a/web/package-lock.json b/web/package-lock.json index 804656b..717b50b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -35,7 +35,7 @@ "globals": "^15.9.0", "typescript": "^5.5.3", "typescript-eslint": "^8.7.0", - "vite": "^5.4.8", + "vite": "^6.0.9", "vite-plugin-svgr": "^4.2.0" } }, @@ -69,9 +69,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", "dev": true, "license": "MIT", "engines": { @@ -123,14 +123,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -140,13 +140,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -233,13 +233,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -264,17 +264,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/types": "^7.26.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -293,9 +293,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -307,9 +307,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", "cpu": [ "ppc64" ], @@ -320,13 +320,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", "cpu": [ "arm" ], @@ -337,13 +337,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", "cpu": [ "arm64" ], @@ -354,13 +354,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", "cpu": [ "x64" ], @@ -371,13 +371,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", "cpu": [ "arm64" ], @@ -388,13 +388,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", "cpu": [ "x64" ], @@ -405,13 +405,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", "cpu": [ "arm64" ], @@ -422,13 +422,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", "cpu": [ "x64" ], @@ -439,13 +439,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", "cpu": [ "arm" ], @@ -456,13 +456,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", "cpu": [ "arm64" ], @@ -473,13 +473,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", "cpu": [ "ia32" ], @@ -490,13 +490,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", "cpu": [ "loong64" ], @@ -507,13 +507,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", "cpu": [ "mips64el" ], @@ -524,13 +524,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", "cpu": [ "ppc64" ], @@ -541,13 +541,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", "cpu": [ "riscv64" ], @@ -558,13 +558,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", "cpu": [ "s390x" ], @@ -575,13 +575,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", "cpu": [ "x64" ], @@ -592,13 +592,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", "cpu": [ "x64" ], @@ -609,13 +626,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", "cpu": [ "x64" ], @@ -626,13 +660,13 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", "cpu": [ "x64" ], @@ -643,13 +677,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", "cpu": [ "arm64" ], @@ -660,13 +694,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", "cpu": [ "ia32" ], @@ -677,13 +711,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", "cpu": [ "x64" ], @@ -694,7 +728,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -755,9 +789,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -805,9 +839,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, "license": "MIT", "engines": { @@ -825,12 +859,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -1031,9 +1066,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.0.tgz", - "integrity": "sha512-qFcFto9figFLz2g25DxJ1WWL9+c91fTxnGuwhToCl8BaqDsDYMl/kOnBXAyAqkkzAWimYMSWNPWEjt+ADAHuoQ==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.31.0.tgz", + "integrity": "sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==", "cpu": [ "arm" ], @@ -1045,9 +1080,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.0.tgz", - "integrity": "sha512-vqrQdusvVl7dthqNjWCL043qelBK+gv9v3ZiqdxgaJvmZyIAAXMjeGVSqZynKq69T7062T5VrVTuikKSAAVP6A==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.31.0.tgz", + "integrity": "sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==", "cpu": [ "arm64" ], @@ -1059,9 +1094,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.0.tgz", - "integrity": "sha512-617pd92LhdA9+wpixnzsyhVft3szYiN16aNUMzVkf2N+yAk8UXY226Bfp36LvxYTUt7MO/ycqGFjQgJ0wlMaWQ==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.31.0.tgz", + "integrity": "sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==", "cpu": [ "arm64" ], @@ -1073,9 +1108,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.0.tgz", - "integrity": "sha512-Y3b4oDoaEhCypg8ajPqigKDcpi5ZZovemQl9Edpem0uNv6UUjXv7iySBpGIUTSs2ovWOzYpfw9EbFJXF/fJHWw==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.31.0.tgz", + "integrity": "sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==", "cpu": [ "x64" ], @@ -1087,9 +1122,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.0.tgz", - "integrity": "sha512-3REQJ4f90sFIBfa0BUokiCdrV/E4uIjhkWe1bMgCkhFXbf4D8YN6C4zwJL881GM818qVYE9BO3dGwjKhpo2ABA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.31.0.tgz", + "integrity": "sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==", "cpu": [ "arm64" ], @@ -1101,9 +1136,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.0.tgz", - "integrity": "sha512-ZtY3Y8icbe3Cc+uQicsXG5L+CRGUfLZjW6j2gn5ikpltt3Whqjfo5mkyZ86UiuHF9Q3ZsaQeW7YswlHnN+lAcg==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.31.0.tgz", + "integrity": "sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==", "cpu": [ "x64" ], @@ -1115,9 +1150,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.0.tgz", - "integrity": "sha512-bsPGGzfiHXMhQGuFGpmo2PyTwcrh2otL6ycSZAFTESviUoBOuxF7iBbAL5IJXc/69peXl5rAtbewBFeASZ9O0g==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.31.0.tgz", + "integrity": "sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==", "cpu": [ "arm" ], @@ -1129,9 +1164,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.0.tgz", - "integrity": "sha512-kvyIECEhs2DrrdfQf++maCWJIQ974EI4txlz1nNSBaCdtf7i5Xf1AQCEJWOC5rEBisdaMFFnOWNLYt7KpFqy5A==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.31.0.tgz", + "integrity": "sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==", "cpu": [ "arm" ], @@ -1143,9 +1178,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.0.tgz", - "integrity": "sha512-CFE7zDNrokaotXu+shwIrmWrFxllg79vciH4E/zeK7NitVuWEaXRzS0mFfFvyhZfn8WfVOG/1E9u8/DFEgK7WQ==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.31.0.tgz", + "integrity": "sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==", "cpu": [ "arm64" ], @@ -1157,9 +1192,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.0.tgz", - "integrity": "sha512-MctNTBlvMcIBP0t8lV/NXiUwFg9oK5F79CxLU+a3xgrdJjfBLVIEHSAjQ9+ipofN2GKaMLnFFXLltg1HEEPaGQ==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.31.0.tgz", + "integrity": "sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==", "cpu": [ "arm64" ], @@ -1171,9 +1206,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.0.tgz", - "integrity": "sha512-fBpoYwLEPivL3q368+gwn4qnYnr7GVwM6NnMo8rJ4wb0p/Y5lg88vQRRP077gf+tc25akuqd+1Sxbn9meODhwA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.31.0.tgz", + "integrity": "sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==", "cpu": [ "loong64" ], @@ -1185,9 +1220,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.0.tgz", - "integrity": "sha512-1hiHPV6dUaqIMXrIjN+vgJqtfkLpqHS1Xsg0oUfUVD98xGp1wX89PIXgDF2DWra1nxAd8dfE0Dk59MyeKaBVAw==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.31.0.tgz", + "integrity": "sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==", "cpu": [ "ppc64" ], @@ -1199,9 +1234,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.0.tgz", - "integrity": "sha512-U0xcC80SMpEbvvLw92emHrNjlS3OXjAM0aVzlWfar6PR0ODWCTQtKeeB+tlAPGfZQXicv1SpWwRz9Hyzq3Jx3g==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.31.0.tgz", + "integrity": "sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==", "cpu": [ "riscv64" ], @@ -1213,9 +1248,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.0.tgz", - "integrity": "sha512-VU/P/IODrNPasgZDLIFJmMiLGez+BN11DQWfTVlViJVabyF3JaeaJkP6teI8760f18BMGCQOW9gOmuzFaI1pUw==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.31.0.tgz", + "integrity": "sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==", "cpu": [ "s390x" ], @@ -1227,9 +1262,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.0.tgz", - "integrity": "sha512-laQVRvdbKmjXuFA3ZiZj7+U24FcmoPlXEi2OyLfbpY2MW1oxLt9Au8q9eHd0x6Pw/Kw4oe9gwVXWwIf2PVqblg==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.31.0.tgz", + "integrity": "sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==", "cpu": [ "x64" ], @@ -1241,9 +1276,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.0.tgz", - "integrity": "sha512-3wzKzduS7jzxqcOvy/ocU/gMR3/QrHEFLge5CD7Si9fyHuoXcidyYZ6jyx8OPYmCcGm3uKTUl+9jUSAY74Ln5A==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.31.0.tgz", + "integrity": "sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==", "cpu": [ "x64" ], @@ -1255,9 +1290,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.0.tgz", - "integrity": "sha512-jROwnI1+wPyuv696rAFHp5+6RFhXGGwgmgSfzE8e4xfit6oLRg7GyMArVUoM3ChS045OwWr9aTnU+2c1UdBMyw==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.31.0.tgz", + "integrity": "sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==", "cpu": [ "arm64" ], @@ -1269,9 +1304,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.0.tgz", - "integrity": "sha512-duzweyup5WELhcXx5H1jokpr13i3BV9b48FMiikYAwk/MT1LrMYYk2TzenBd0jj4ivQIt58JWSxc19y4SvLP4g==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.31.0.tgz", + "integrity": "sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==", "cpu": [ "ia32" ], @@ -1283,9 +1318,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.0.tgz", - "integrity": "sha512-DYvxS0M07PvgvavMIybCOBYheyrqlui6ZQBHJs6GqduVzHSZ06TPPvlfvnYstjODHQ8UUXFwt5YE+h0jFI8kwg==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.31.0.tgz", + "integrity": "sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==", "cpu": [ "x64" ], @@ -1529,9 +1564,9 @@ } }, "node_modules/@swc/core": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.4.tgz", - "integrity": "sha512-ut3zfiTLORMxhr6y/GBxkHmzcGuVpwJYX4qyXWuBKkpw/0g0S5iO1/wW7RnLnZbAi8wS/n0atRZoaZlXWBkeJg==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.8.tgz", + "integrity": "sha512-I3G+n9qbHNu6KNraaAG1+Z1S1x5S7MGRA6OEppT8Pt3Z9uD5a/kYAGU33eXy7zY+BoKuKA2X1H0r4vSimAgU8w==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -1547,16 +1582,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.10.4", - "@swc/core-darwin-x64": "1.10.4", - "@swc/core-linux-arm-gnueabihf": "1.10.4", - "@swc/core-linux-arm64-gnu": "1.10.4", - "@swc/core-linux-arm64-musl": "1.10.4", - "@swc/core-linux-x64-gnu": "1.10.4", - "@swc/core-linux-x64-musl": "1.10.4", - "@swc/core-win32-arm64-msvc": "1.10.4", - "@swc/core-win32-ia32-msvc": "1.10.4", - "@swc/core-win32-x64-msvc": "1.10.4" + "@swc/core-darwin-arm64": "1.10.8", + "@swc/core-darwin-x64": "1.10.8", + "@swc/core-linux-arm-gnueabihf": "1.10.8", + "@swc/core-linux-arm64-gnu": "1.10.8", + "@swc/core-linux-arm64-musl": "1.10.8", + "@swc/core-linux-x64-gnu": "1.10.8", + "@swc/core-linux-x64-musl": "1.10.8", + "@swc/core-win32-arm64-msvc": "1.10.8", + "@swc/core-win32-ia32-msvc": "1.10.8", + "@swc/core-win32-x64-msvc": "1.10.8" }, "peerDependencies": { "@swc/helpers": "*" @@ -1568,9 +1603,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.4.tgz", - "integrity": "sha512-sV/eurLhkjn/197y48bxKP19oqcLydSel42Qsy2zepBltqUx+/zZ8+/IS0Bi7kaWVFxerbW1IPB09uq8Zuvm3g==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.8.tgz", + "integrity": "sha512-FtacTu9zS5YuepujQqujveNw8BQ8ESJ+pN1Z7C+WrKCHlCl+5dh0n6gMAlEj+3iRvY6UAYqkzTVeiX/bOMoJKA==", "cpu": [ "arm64" ], @@ -1585,9 +1620,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.4.tgz", - "integrity": "sha512-gjYNU6vrAUO4+FuovEo9ofnVosTFXkF0VDuo1MKPItz6e2pxc2ale4FGzLw0Nf7JB1sX4a8h06CN16/pLJ8Q2w==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.8.tgz", + "integrity": "sha512-nfk+iq7EKQwADaCERzZLSi9ovzjJcqDWaO4e2ztyCNaLFi6fP1m6+ij21aki5KAd8AXoY4fue4Mo2fuYbesX9Q==", "cpu": [ "x64" ], @@ -1602,9 +1637,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.4.tgz", - "integrity": "sha512-zd7fXH5w8s+Sfvn2oO464KDWl+ZX1MJiVmE4Pdk46N3PEaNwE0koTfgx2vQRqRG4vBBobzVvzICC3618WcefOA==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.8.tgz", + "integrity": "sha512-CL2zfbnrEc6nIiWbgshOz0mjn/zY8JcYqO12vGcTxmZOrh0n+mmHN2ejX91pYWQnQDtbhCmFTaEndExFpA7Gww==", "cpu": [ "arm" ], @@ -1619,9 +1654,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.4.tgz", - "integrity": "sha512-+UGfoHDxsMZgFD3tABKLeEZHqLNOkxStu+qCG7atGBhS4Slri6h6zijVvf4yI5X3kbXdvc44XV/hrP/Klnui2A==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.8.tgz", + "integrity": "sha512-quS8F18DDScW3B7qnbWkz95abZ5p0xp/W8N498NAAls/YQj4jQIlf8WlAWoxVVjY/SmSus5kN5tuwhHD8t0NPw==", "cpu": [ "arm64" ], @@ -1636,9 +1671,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.4.tgz", - "integrity": "sha512-cDDj2/uYsOH0pgAnDkovLZvKJpFmBMyXkxEG6Q4yw99HbzO6QzZ5HDGWGWVq/6dLgYKlnnmpjZCPPQIu01mXEg==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.8.tgz", + "integrity": "sha512-wI0Hny8fHbBK/OjJ7eFYP0uDKiCMMMr5OBWGKMRRUvWs2zlGeJQZbwUeCnWuLLXzDfL+feMfh5TieYlqKTTtRw==", "cpu": [ "arm64" ], @@ -1653,9 +1688,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.4.tgz", - "integrity": "sha512-qJXh9D6Kf5xSdGWPINpLGixAbB5JX8JcbEJpRamhlDBoOcQC79dYfOMEIxWPhTS1DGLyFakAx2FX/b2VmQmj0g==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.8.tgz", + "integrity": "sha512-24FCRUFO8gzPP2eu3soHTm3lk+ktcsIhdM2DTOlXGA+2TBYFWgAZX/yZV+eeRrtIZYSr4OcOWsNWnQ5Ma4budA==", "cpu": [ "x64" ], @@ -1670,9 +1705,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.4.tgz", - "integrity": "sha512-A76lIAeyQnHCVt0RL/pG+0er8Qk9+acGJqSZOZm67Ve3B0oqMd871kPtaHBM0BW3OZAhoILgfHW3Op9Q3mx3Cw==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.8.tgz", + "integrity": "sha512-mBo7M/FmUhoWpUG17MLbS98iRA7t6ThxQBWDJZd322whkN1GqrvumYm2wvvjmoMTeDOPwAL3hIIa5H+Q4vb1zA==", "cpu": [ "x64" ], @@ -1687,9 +1722,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.4.tgz", - "integrity": "sha512-e6j5kBu4fIY7fFxFxnZI0MlEovRvp50Lg59Fw+DVbtqHk3C85dckcy5xKP+UoXeuEmFceauQDczUcGs19SRGSQ==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.8.tgz", + "integrity": "sha512-rXJ9y77JZZXoZkgFR0mObKa3TethRBJ6Exs/pwhScl9pz4qsfxhj/bQbEu1g1i/ihmd0l+IKZwGSC7Ibh3HA2Q==", "cpu": [ "arm64" ], @@ -1704,9 +1739,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.4.tgz", - "integrity": "sha512-RSYHfdKgNXV/amY5Tqk1EWVsyQnhlsM//jeqMLw5Fy9rfxP592W9UTumNikNRPdjI8wKKzNMXDb1U29tQjN0dg==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.8.tgz", + "integrity": "sha512-n6ekYFJEBPvTpRIqJiu6EHXVzVnuCtDTpFnn/0KVGJI1yQHriGVEovnb/+qyLh8Rwx2AZM9qgZVgMhVtfcFQJg==", "cpu": [ "ia32" ], @@ -1721,9 +1756,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.4.tgz", - "integrity": "sha512-1ujYpaqfqNPYdwKBlvJnOqcl+Syn3UrQ4XE0Txz6zMYgyh6cdU6a3pxqLqIUSJ12MtXRA9ZUhEz1ekU3LfLWXw==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.8.tgz", + "integrity": "sha512-vplXxtH/lFc/epELnAyvdCvqlDJrM+OKtkphYcbPqq50g/dEZYZ8FYHU5Df9Uo19UooWSo1LaxPk4R7n6i1Axw==", "cpu": [ "x64" ], @@ -1790,9 +1825,9 @@ "license": "MIT" }, "node_modules/@types/leaflet": { - "version": "1.9.15", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.15.tgz", - "integrity": "sha512-7UuggAuAs+mva66gtf2OTB1nEhzU/9JED93TIaOEgvFMvG/dIGQaukHE7izHo1Zd+Ko1L4ETUw7TBc8yUxevpg==", + "version": "1.9.16", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.16.tgz", + "integrity": "sha512-wzZoyySUxkgMZ0ihJ7IaUIblG8Rdc8AbbZKLneyn+QjYsj5q1QU7TEKYqwTr10BGSzY5LI7tJk9Ifo+mEjdFRw==", "dev": true, "license": "MIT", "dependencies": { @@ -1800,9 +1835,9 @@ } }, "node_modules/@types/react": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.3.tgz", - "integrity": "sha512-UavfHguIjnnuq9O67uXfgy/h3SRJbidAYvNjLceB+2RIKVRBzVsh0QO+Pw6BCSQqFS9xwzKfwstXx0m6AbAREA==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz", + "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", "dev": true, "license": "MIT", "dependencies": { @@ -1810,9 +1845,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", - "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", + "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1830,21 +1865,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", - "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/type-utils": "8.19.0", - "@typescript-eslint/utils": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1860,16 +1895,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "engines": { @@ -1885,14 +1920,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1903,16 +1938,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", - "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1927,9 +1962,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", "dev": true, "license": "MIT", "engines": { @@ -1941,20 +1976,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2007,16 +2042,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", - "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0" + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2031,13 +2066,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/types": "8.20.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2306,9 +2341,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -2412,9 +2447,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001695", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", + "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", "dev": true, "funding": [ { @@ -2714,9 +2749,9 @@ } }, "node_modules/domutils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.1.tgz", - "integrity": "sha512-xWXmuRnN9OMP6ptPd2+H0cCbcYBULa5YDTbMm/2lvkWvNA3O4wcW+GvzooqBuNM8yy6pl3VIAeJTUUWUbfI5Fw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2755,9 +2790,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.76", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz", - "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==", + "version": "1.5.83", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz", + "integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==", "dev": true, "license": "ISC" }, @@ -2871,9 +2906,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, "license": "MIT", "dependencies": { @@ -2928,9 +2963,9 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2938,32 +2973,34 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, "node_modules/escalade": { @@ -2990,19 +3027,19 @@ } }, "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", + "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", + "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", @@ -3157,9 +3194,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.16.tgz", - "integrity": "sha512-slterMlxAhov/DZO8NScf6mEeMBBXodFUolijDvrtTxyezyLoTQaa73FyYus/VbTdftd8wBgBxPMRk3poleXNQ==", + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.18.tgz", + "integrity": "sha512-IRGEoFn3OKalm3hjfolEWGqoF/jPqeEYFp+C8B0WMzwGwBMvlRDQd06kghDhF0C61uJ6WfSDhEZE/sAQjduKgw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4227,9 +4264,9 @@ } }, "node_modules/katex": { - "version": "0.16.19", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.19.tgz", - "integrity": "sha512-3IA6DYVhxhBabjSLTNO9S4+OliA3Qvb8pBQXMfC4WxXJgLwZgnfDl0BmB4z6nBMdznBsZ+CGM8DrGZ5hcguDZg==", + "version": "0.16.21", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", + "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" @@ -4700,9 +4737,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -4720,7 +4757,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4916,9 +4953,9 @@ } }, "node_modules/rollup": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.0.tgz", - "integrity": "sha512-sDnr1pcjTgUT69qBksNF1N1anwfbyYG6TBQ22b03bII8EdiUQ7J0TlozVaTMjT/eEJAO49e1ndV7t+UZfL1+vA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.31.0.tgz", + "integrity": "sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==", "dev": true, "license": "MIT", "dependencies": { @@ -4932,25 +4969,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.30.0", - "@rollup/rollup-android-arm64": "4.30.0", - "@rollup/rollup-darwin-arm64": "4.30.0", - "@rollup/rollup-darwin-x64": "4.30.0", - "@rollup/rollup-freebsd-arm64": "4.30.0", - "@rollup/rollup-freebsd-x64": "4.30.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.30.0", - "@rollup/rollup-linux-arm-musleabihf": "4.30.0", - "@rollup/rollup-linux-arm64-gnu": "4.30.0", - "@rollup/rollup-linux-arm64-musl": "4.30.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.30.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.30.0", - "@rollup/rollup-linux-riscv64-gnu": "4.30.0", - "@rollup/rollup-linux-s390x-gnu": "4.30.0", - "@rollup/rollup-linux-x64-gnu": "4.30.0", - "@rollup/rollup-linux-x64-musl": "4.30.0", - "@rollup/rollup-win32-arm64-msvc": "4.30.0", - "@rollup/rollup-win32-ia32-msvc": "4.30.0", - "@rollup/rollup-win32-x64-msvc": "4.30.0", + "@rollup/rollup-android-arm-eabi": "4.31.0", + "@rollup/rollup-android-arm64": "4.31.0", + "@rollup/rollup-darwin-arm64": "4.31.0", + "@rollup/rollup-darwin-x64": "4.31.0", + "@rollup/rollup-freebsd-arm64": "4.31.0", + "@rollup/rollup-freebsd-x64": "4.31.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.31.0", + "@rollup/rollup-linux-arm-musleabihf": "4.31.0", + "@rollup/rollup-linux-arm64-gnu": "4.31.0", + "@rollup/rollup-linux-arm64-musl": "4.31.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.31.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.31.0", + "@rollup/rollup-linux-riscv64-gnu": "4.31.0", + "@rollup/rollup-linux-s390x-gnu": "4.31.0", + "@rollup/rollup-linux-x64-gnu": "4.31.0", + "@rollup/rollup-linux-x64-musl": "4.31.0", + "@rollup/rollup-win32-arm64-msvc": "4.31.0", + "@rollup/rollup-win32-ia32-msvc": "4.31.0", + "@rollup/rollup-win32-x64-msvc": "4.31.0", "fsevents": "~2.3.2" } }, @@ -5347,16 +5384,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/tsconfig-paths": { @@ -5471,9 +5508,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5485,15 +5522,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.0.tgz", - "integrity": "sha512-Ni8sUkVWYK4KAcTtPjQ/UTiRk6jcsuDhPpxULapUDi8A/l8TSBk+t1GtJA1RsCzIJg0q6+J7bf35AwQigENWRQ==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.20.0.tgz", + "integrity": "sha512-Kxz2QRFsgbWj6Xcftlw3Dd154b3cEPFqQC+qMZrMypSijPd4UanKKvoKDrJ4o8AIfZFKAF+7sMaEIR8mTElozA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.19.0", - "@typescript-eslint/parser": "8.19.0", - "@typescript-eslint/utils": "8.19.0" + "@typescript-eslint/eslint-plugin": "8.20.0", + "@typescript-eslint/parser": "8.20.0", + "@typescript-eslint/utils": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5533,9 +5570,9 @@ "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -5554,7 +5591,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -5574,21 +5611,21 @@ } }, "node_modules/vite": { - "version": "5.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", - "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.9.tgz", + "integrity": "sha512-MSgUxHcaXLtnBPktkbUSoQUANApKYuxZ6DrbVENlIorbhL2dZydTLaZ01tjUoE3szeFzlFk9ANOKk0xurh4MKA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.24.2", + "postcss": "^8.4.49", + "rollup": "^4.23.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -5597,19 +5634,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.4.0" + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -5630,6 +5673,12 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, diff --git a/web/package.json b/web/package.json index 0d135f1..76b4398 100644 --- a/web/package.json +++ b/web/package.json @@ -37,7 +37,7 @@ "globals": "^15.9.0", "typescript": "^5.5.3", "typescript-eslint": "^8.7.0", - "vite": "^5.4.8", + "vite": "^6.0.9", "vite-plugin-svgr": "^4.2.0" } } From 9cff332671727cdc05c5e9aaf90a00942bc3f083 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Thu, 23 Jan 2025 23:03:53 +0000 Subject: [PATCH 04/78] web: implement user moderation actions (#588) Co-authored-by: Tulir Asokan --- pkg/hicli/json-commands.go | 22 +++ web/src/api/rpc.ts | 5 + web/src/api/types/hitypes.ts | 2 + web/src/api/types/mxtypes.ts | 4 + web/src/icons/block.svg | 1 + web/src/icons/gavel.svg | 1 + web/src/icons/person-add.svg | 1 + web/src/icons/person-remove.svg | 1 + web/src/ui/rightpanel/RightPanel.css | 19 +++ web/src/ui/rightpanel/UserInfo.tsx | 3 + web/src/ui/rightpanel/UserModeration.tsx | 147 ++++++++++++++++++ .../timeline/menu/ConfirmWithMessageModal.tsx | 10 +- 12 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 web/src/icons/block.svg create mode 100644 web/src/icons/gavel.svg create mode 100644 web/src/icons/person-add.svg create mode 100644 web/src/icons/person-remove.svg create mode 100644 web/src/ui/rightpanel/UserModeration.tsx diff --git a/pkg/hicli/json-commands.go b/pkg/hicli/json-commands.go index dca1ea7..ea7c70f 100644 --- a/pkg/hicli/json-commands.go +++ b/pkg/hicli/json-commands.go @@ -66,6 +66,21 @@ func (h *HiClient) handleJSONCommand(ctx context.Context, req *JSONCommand) (any return unmarshalAndCall(req.Data, func(params *sendStateEventParams) (id.EventID, error) { return h.SetState(ctx, params.RoomID, params.EventType, params.StateKey, params.Content) }) + case "set_membership": + return unmarshalAndCall(req.Data, func(params *setMembershipParams) (any, error) { + switch params.Action { + case "invite": + return h.Client.InviteUser(ctx, params.RoomID, &mautrix.ReqInviteUser{UserID: params.UserID, Reason: params.Reason}) + case "kick": + return h.Client.KickUser(ctx, params.RoomID, &mautrix.ReqKickUser{UserID: params.UserID, Reason: params.Reason}) + case "ban": + return h.Client.BanUser(ctx, params.RoomID, &mautrix.ReqBanUser{UserID: params.UserID, Reason: params.Reason}) + case "unban": + return h.Client.UnbanUser(ctx, params.RoomID, &mautrix.ReqUnbanUser{UserID: params.UserID, Reason: params.Reason}) + default: + return nil, fmt.Errorf("unknown action %q", params.Action) + } + }) case "set_account_data": return unmarshalAndCall(req.Data, func(params *setAccountDataParams) (bool, error) { if params.RoomID != "" { @@ -262,6 +277,13 @@ type sendStateEventParams struct { Content json.RawMessage `json:"content"` } +type setMembershipParams struct { + Action string `json:"action"` + RoomID id.RoomID `json:"room_id"` + UserID id.UserID `json:"user_id"` + Reason string `json:"reason"` +} + type setAccountDataParams struct { RoomID id.RoomID `json:"room_id,omitempty"` Type string `json:"type"` diff --git a/web/src/api/rpc.ts b/web/src/api/rpc.ts index 95fb019..525c373 100644 --- a/web/src/api/rpc.ts +++ b/web/src/api/rpc.ts @@ -24,6 +24,7 @@ import type { JSONValue, LoginFlowsResponse, LoginRequest, + MembershipAction, Mentions, MessageEventContent, PaginationResponse, @@ -167,6 +168,10 @@ export default abstract class RPCClient { return this.request("set_state", { room_id, type, state_key, content }) } + setMembership(room_id: RoomID, user_id: UserID, action: MembershipAction, reason?: string): Promise { + return this.request("set_membership", { room_id, user_id, action, reason }) + } + setAccountData(type: EventType, content: unknown, room_id?: RoomID): Promise { return this.request("set_account_data", { type, content, room_id }) } diff --git a/web/src/api/types/hitypes.ts b/web/src/api/types/hitypes.ts index c7489cd..7defc86 100644 --- a/web/src/api/types/hitypes.ts +++ b/web/src/api/types/hitypes.ts @@ -292,3 +292,5 @@ export interface DBPushRegistration { encryption: { key: string } expiration?: number } + +export type MembershipAction = "invite" | "kick" | "ban" | "unban" diff --git a/web/src/api/types/mxtypes.ts b/web/src/api/types/mxtypes.ts index 51a22d4..94059e5 100644 --- a/web/src/api/types/mxtypes.ts +++ b/web/src/api/types/mxtypes.ts @@ -218,6 +218,10 @@ export interface ReactionEventContent { "com.beeper.reaction.shortcode"?: string } +export interface IgnoredUsersEventContent { + ignored_users: Record +} + export interface EncryptedFile { url: ContentURI k: string diff --git a/web/src/icons/block.svg b/web/src/icons/block.svg new file mode 100644 index 0000000..01b6a9e --- /dev/null +++ b/web/src/icons/block.svg @@ -0,0 +1 @@ + diff --git a/web/src/icons/gavel.svg b/web/src/icons/gavel.svg new file mode 100644 index 0000000..78195c7 --- /dev/null +++ b/web/src/icons/gavel.svg @@ -0,0 +1 @@ + diff --git a/web/src/icons/person-add.svg b/web/src/icons/person-add.svg new file mode 100644 index 0000000..a6dc833 --- /dev/null +++ b/web/src/icons/person-add.svg @@ -0,0 +1 @@ + diff --git a/web/src/icons/person-remove.svg b/web/src/icons/person-remove.svg new file mode 100644 index 0000000..1e6b59b --- /dev/null +++ b/web/src/icons/person-remove.svg @@ -0,0 +1 @@ + diff --git a/web/src/ui/rightpanel/RightPanel.css b/web/src/ui/rightpanel/RightPanel.css index 50a3c44..ef7138a 100644 --- a/web/src/ui/rightpanel/RightPanel.css +++ b/web/src/ui/rightpanel/RightPanel.css @@ -192,6 +192,25 @@ div.right-panel-content.user { } } + div.user-moderation { + display: flex; + flex-direction: column; + + button.moderation-action { + padding: .5rem; + width: 100%; + gap: .5rem; + justify-content: left; + + &.dangerous { + color: var(--error-color); + } + &.positive { + color: var(--primary-color); + } + } + } + div.errors { display: flex; flex-direction: column; diff --git a/web/src/ui/rightpanel/UserInfo.tsx b/web/src/ui/rightpanel/UserInfo.tsx index 0181904..17823d1 100644 --- a/web/src/ui/rightpanel/UserInfo.tsx +++ b/web/src/ui/rightpanel/UserInfo.tsx @@ -26,6 +26,7 @@ import UserExtendedProfile from "./UserExtendedProfile.tsx" import DeviceList from "./UserInfoDeviceList.tsx" import UserInfoError from "./UserInfoError.tsx" import MutualRooms from "./UserInfoMutualRooms.tsx" +import UserModeration from "./UserModeration.tsx" interface UserInfoProps { userID: UserID @@ -77,6 +78,8 @@ const UserInfo = ({ userID }: UserInfoProps) => { }
+ +
{errors?.length ? <>
diff --git a/web/src/ui/rightpanel/UserModeration.tsx b/web/src/ui/rightpanel/UserModeration.tsx new file mode 100644 index 0000000..d5133a6 --- /dev/null +++ b/web/src/ui/rightpanel/UserModeration.tsx @@ -0,0 +1,147 @@ +// gomuks - A Matrix client written in Go. +// Copyright (C) 2025 Nexus Nicholson +// +// 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 . +import { use } from "react" +import Client from "@/api/client.ts" +import { RoomStateStore, useAccountData } from "@/api/statestore" +import { IgnoredUsersEventContent, MemDBEvent, MembershipAction } from "@/api/types" +import { ModalContext } from "../modal" +import ConfirmWithMessageModal from "../timeline/menu/ConfirmWithMessageModal.tsx" +import { getPowerLevels } from "../timeline/menu/util.ts" +import IgnoreIcon from "@/icons/block.svg?react" +import BanIcon from "@/icons/gavel.svg?react" +import InviteIcon from "@/icons/person-add.svg?react" +import KickIcon from "@/icons/person-remove.svg?react" + +interface UserModerationProps { + userID: string; + client: Client; + room: RoomStateStore | undefined; + member: MemDBEvent | null; +} + +const UserIgnoreButton = ({ userID, client }: { userID: string; client: Client }) => { + const ignoredUsers = useAccountData(client.store, "m.ignored_user_list") as IgnoredUsersEventContent | null + + const isIgnored = Boolean(ignoredUsers?.ignored_users?.[userID]) + const ignoreUser = () => { + const newIgnoredUsers = { ...(ignoredUsers || { ignored_users: {}}) } + newIgnoredUsers.ignored_users[userID] = {} + client.rpc.setAccountData("m.ignored_user_list", newIgnoredUsers).catch(err => { + console.error("Failed to ignore user", err) + window.alert(`Failed to ignore ${userID}: ${err}`) + }) + } + const unignoreUser = () => { + const newIgnoredUsers = { ...(ignoredUsers || { ignored_users: {}}) } + delete newIgnoredUsers.ignored_users[userID] + client.rpc.setAccountData("m.ignored_user_list", newIgnoredUsers).catch(err => { + console.error("Failed to unignore user", err) + window.alert(`Failed to unignore ${userID}: ${err}`) + }) + } + + return ( + + ) +} + +const UserModeration = ({ userID, client, member, room }: UserModerationProps) => { + const openModal = use(ModalContext) + const hasPL = (action: "invite" | "kick" | "ban") => { + if (!room) { + throw new Error("hasPL called without room") + } + const [pls, ownPL] = getPowerLevels(room, client) + if(action === "invite") { + return ownPL >= (pls.invite ?? 0) + } + const otherUserPL = pls.users?.[userID] ?? pls.users_default ?? 0 + return ownPL >= (pls[action] ?? 50) && ownPL > otherUserPL + } + + const runAction = (action: MembershipAction) => { + if (!room) { + throw new Error("runAction called without room") + } + const callback = (reason: string) => { + client.rpc.setMembership(room.roomID, userID, action, reason).then( + () => console.debug("Actioned", userID), + err => { + console.error("Failed to action", err) + window.alert(`Failed to ${action} ${userID}: ${err}`) + }, + ) + } + const titleCasedAction = action.charAt(0).toUpperCase() + action.slice(1) + return () => { + openModal({ + dimmed: true, + boxed: true, + innerBoxClass: "confirm-message-modal", + content: Are you sure you want to {action} {userID}?} + placeholder="Reason (optional)" + confirmButton={titleCasedAction} + onConfirm={callback} + />, + }) + } + } + const membership = member?.content.membership || "leave" + + return
+

Moderation

+ {room && (["knock", "leave"].includes(membership) || !member) && hasPL("invite") && ( + + )} + {room && ["knock", "invite", "join"].includes(membership) && hasPL("kick") && ( + + )} + {room && membership !== "ban" && hasPL("ban") && ( + + )} + {room && membership === "ban" && hasPL("ban") && ( + + )} + +
+} + +export default UserModeration diff --git a/web/src/ui/timeline/menu/ConfirmWithMessageModal.tsx b/web/src/ui/timeline/menu/ConfirmWithMessageModal.tsx index 9028a32..05d11e1 100644 --- a/web/src/ui/timeline/menu/ConfirmWithMessageModal.tsx +++ b/web/src/ui/timeline/menu/ConfirmWithMessageModal.tsx @@ -13,16 +13,16 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React, { use, useState } from "react" +import React, { JSX, use, useState } from "react" import { MemDBEvent } from "@/api/types" import { isMobileDevice } from "@/util/ismobile.ts" import { ModalCloseContext } from "../../modal" import TimelineEvent from "../TimelineEvent.tsx" interface ConfirmWithMessageProps { - evt: MemDBEvent + evt?: MemDBEvent title: string - description: string + description: string | JSX.Element placeholder: string confirmButton: string onConfirm: (reason: string) => void @@ -40,9 +40,9 @@ const ConfirmWithMessageModal = ({ } return

{title}

-
+ {evt &&
-
+
}
{description}
From fabf3404af2c2d495075c75e9c2aac08af3d1e8e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 24 Jan 2025 01:04:28 +0200 Subject: [PATCH 05/78] web/rightpanel: ignore timezone if value is unsupported --- web/src/ui/rightpanel/UserExtendedProfile.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/web/src/ui/rightpanel/UserExtendedProfile.tsx b/web/src/ui/rightpanel/UserExtendedProfile.tsx index e745f53..afdea59 100644 --- a/web/src/ui/rightpanel/UserExtendedProfile.tsx +++ b/web/src/ui/rightpanel/UserExtendedProfile.tsx @@ -27,14 +27,19 @@ const currentTimeAdjusted = (tz: string) => { timeZoneName: "short", timeZone: tz, }).format(new Date()) - } catch (e) { - return `${e}` + } catch { + return null } } const ClockElement = ({ tz }: { tz: string }) => { - const [time, setTime] = useState(currentTimeAdjusted(tz)) + const cta = currentTimeAdjusted(tz) + const isValidTZ = cta !== null + const [time, setTime] = useState(cta) useEffect(() => { + if (!isValidTZ) { + return + } let interval: number | undefined const updateTime = () => setTime(currentTimeAdjusted(tz)) const timeout = setTimeout(() => { @@ -42,8 +47,11 @@ const ClockElement = ({ tz }: { tz: string }) => { updateTime() }, (1001 - Date.now() % 1000)) return () => interval ? clearInterval(interval) : clearTimeout(timeout) - }, [tz]) + }, [tz, isValidTZ]) + if (!isValidTZ) { + return null + } return <>
Time:
{time}
From 865b2e4fdfd25589a6f809157a0d0aad017b25b4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 24 Jan 2025 01:15:07 +0200 Subject: [PATCH 06/78] web/rightpanel: don't show ignore button for self --- web/src/ui/rightpanel/UserInfo.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/ui/rightpanel/UserInfo.tsx b/web/src/ui/rightpanel/UserInfo.tsx index 17823d1..87b5ea0 100644 --- a/web/src/ui/rightpanel/UserInfo.tsx +++ b/web/src/ui/rightpanel/UserInfo.tsx @@ -72,14 +72,14 @@ const UserInfo = ({ userID }: UserInfoProps) => { profile={globalProfile} refreshProfile={refreshProfile} client={client} userID={userID} />}
+ +
{userID !== client.userID && <>
+ +
} - -
- -
{errors?.length ? <>
From b7f939f480913f36a3fe20df76a6b6509547030b Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Thu, 23 Jan 2025 23:39:10 +0000 Subject: [PATCH 07/78] web: add share event button (#589) Co-authored-by: Tulir Asokan --- web/src/api/statestore/room.ts | 31 ++++++++- web/src/icons/share.svg | 1 + web/src/ui/timeline/menu/ShareModal.tsx | 65 +++++++++++++++++++ web/src/ui/timeline/menu/index.css | 11 ++++ .../ui/timeline/menu/useSecondaryItems.tsx | 48 +++++++++++++- 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 web/src/icons/share.svg create mode 100644 web/src/ui/timeline/menu/ShareModal.tsx diff --git a/web/src/api/statestore/room.ts b/web/src/api/statestore/room.ts index 5252a3c..f0f5a0f 100644 --- a/web/src/api/statestore/room.ts +++ b/web/src/api/statestore/room.ts @@ -18,7 +18,7 @@ import { CustomEmojiPack, parseCustomEmojiPack } from "@/util/emoji" import { NonNullCachedEventDispatcher } from "@/util/eventdispatcher.ts" import toSearchableString from "@/util/searchablestring.ts" import Subscribable, { MultiSubscribable, NoDataSubscribable } from "@/util/subscribable.ts" -import { getDisplayname } from "@/util/validation.ts" +import { getDisplayname, getServerName } from "@/util/validation.ts" import { ContentURI, DBReceipt, @@ -246,6 +246,35 @@ export class RoomStateStore { return this.#membersCache ?? [] } + getViaServers(): string[] { + const ownServerName = getServerName(this.parent.userID) + const vias = [ownServerName] + const members = this.getMembers() + const memberCount = new Map() + const powerLevels: PowerLevelEventContent = this.getStateEvent("m.room.power_levels", "")?.content ?? {} + const usersDefault = powerLevels.users_default ?? 0 + let powerServer: string | undefined = undefined + for (const member of members) { + const serverName = getServerName(member.userID) + if (serverName !== ownServerName) { + if (!powerServer && (powerLevels?.users?.[member.userID] ?? usersDefault) > usersDefault) { + powerServer = serverName + vias.push(powerServer) + } + memberCount.set(serverName, (memberCount.get(serverName) ?? 0) + 1) + } + } + const servers = Array.from(memberCount.entries()) + servers.sort(([, a], [, b]) => b - a) + for (const [serverName] of servers) { + if (serverName !== ownServerName && serverName !== powerServer) { + vias.push(serverName) + break + } + } + return vias + } + getPinnedEvents(): EventID[] { const pinnedList = this.getStateEvent("m.room.pinned_events", "")?.content?.pinned if (Array.isArray(pinnedList)) { diff --git a/web/src/icons/share.svg b/web/src/icons/share.svg new file mode 100644 index 0000000..f86fdd4 --- /dev/null +++ b/web/src/icons/share.svg @@ -0,0 +1 @@ + diff --git a/web/src/ui/timeline/menu/ShareModal.tsx b/web/src/ui/timeline/menu/ShareModal.tsx new file mode 100644 index 0000000..9e531cc --- /dev/null +++ b/web/src/ui/timeline/menu/ShareModal.tsx @@ -0,0 +1,65 @@ +import React, { use, useState } from "react" +import { MemDBEvent } from "@/api/types" +import { ModalCloseContext } from "@/ui/modal" +import TimelineEvent from "@/ui/timeline/TimelineEvent.tsx" +import Toggle from "@/ui/util/Toggle.tsx" + +interface ConfirmWithMessageProps { + evt: MemDBEvent + title: string + confirmButton: string + onConfirm: (useMatrixTo: boolean, includeEvent: boolean) => void + generateLink: (useMatrixTo: boolean, includeEvent: boolean) => string +} + +const ShareModal = ({ evt, title, confirmButton, onConfirm, generateLink }: ConfirmWithMessageProps) => { + const [useMatrixTo, setUseMatrixTo] = useState(false) + const [includeEvent, setIncludeEvent] = useState(true) + const closeModal = use(ModalCloseContext) + const onConfirmWrapped = (evt: React.FormEvent) => { + evt.preventDefault() + closeModal() + onConfirm(useMatrixTo, includeEvent) + } + + const link = generateLink(useMatrixTo, includeEvent) + return +

{title}

+
+ +
+ + + + + + + + + + + +
Use matrix.to link + setUseMatrixTo(evt.target.checked)} + /> +
Link to this specific event + setIncludeEvent(evt.target.checked)} + /> +
+
+ Preview: {link} +
+
+ + +
+ +} + +export default ShareModal diff --git a/web/src/ui/timeline/menu/index.css b/web/src/ui/timeline/menu/index.css index a78c222..b9f8bcc 100644 --- a/web/src/ui/timeline/menu/index.css +++ b/web/src/ui/timeline/menu/index.css @@ -101,6 +101,7 @@ div.confirm-message-modal > form { > div.timeline-event { margin: 0; + padding: 0; } } @@ -118,4 +119,14 @@ div.confirm-message-modal > form { padding: .5rem 1rem; } } + + > div.output-preview { + > span.no-select { + user-select: none; + } + + > code { + word-break: break-word; + } + } } diff --git a/web/src/ui/timeline/menu/useSecondaryItems.tsx b/web/src/ui/timeline/menu/useSecondaryItems.tsx index 134d4f7..3c077bd 100644 --- a/web/src/ui/timeline/menu/useSecondaryItems.tsx +++ b/web/src/ui/timeline/menu/useSecondaryItems.tsx @@ -21,11 +21,13 @@ import { ModalCloseContext, ModalContext } from "../../modal" import { RoomContext, RoomContextData } from "../../roomview/roomcontext.ts" import JSONView from "../../util/JSONView.tsx" import ConfirmWithMessageModal from "./ConfirmWithMessageModal.tsx" +import ShareModal from "./ShareModal.tsx" import { getPending, getPowerLevels } from "./util.ts" import ViewSourceIcon from "@/icons/code.svg?react" import DeleteIcon from "@/icons/delete.svg?react" import PinIcon from "@/icons/pin.svg?react" import ReportIcon from "@/icons/report.svg?react" +import ShareIcon from "@/icons/share.svg?react" import UnpinIcon from "@/icons/unpin.svg?react" export const useSecondaryItems = ( @@ -40,7 +42,7 @@ export const useSecondaryItems = ( openModal({ dimmed: true, boxed: true, - content: , + content: , }) } const onClickReport = () => { @@ -89,6 +91,49 @@ export const useSecondaryItems = ( .catch(err => window.alert(`Failed to ${pin ? "pin" : "unpin"} message: ${err}`)) } + const onClickShareEvent = () => { + const generateLink = (useMatrixTo: boolean, includeEvent: boolean) => { + const isRoomIDLink = true + let generatedURL = useMatrixTo ? "https://matrix.to/#/" : "matrix:roomid/" + if (useMatrixTo) { + generatedURL += evt.room_id + } else { + generatedURL += `${evt.room_id.slice(1)}` + } + if (includeEvent) { + if (useMatrixTo) { + generatedURL += `/${evt.event_id}` + } else { + generatedURL += `/e/${evt.event_id.slice(1)}` + } + } + if (isRoomIDLink) { + generatedURL += "?" + new URLSearchParams( + roomCtx.store.getViaServers().map(server => ["via", server]), + ).toString() + } + return generatedURL + } + openModal({ + dimmed: true, + boxed: true, + innerBoxClass: "confirm-message-modal", + content: + { + navigator.clipboard.writeText(generateLink(useMatrixTo, includeEvent)).catch( + err => window.alert(`Failed to copy link: ${err}`), + ) + }} + generateLink={generateLink} + /> + , + }) + } + const [isPending, pendingTitle] = getPending(evt) useRoomState(roomCtx.store, "m.room.power_levels", "") // We get pins from getPinnedEvents, but use the hook anyway to subscribe to changes @@ -104,6 +149,7 @@ export const useSecondaryItems = ( return <> + {ownPL >= pinPL && (pins.includes(evt.event_id) ? + +
+
+ + +
+
+ + +
+
+ +} + const SettingsView = ({ room }: SettingsViewProps) => { const roomMeta = useEventAsState(room.meta) const client = use(ClientContext)! @@ -393,6 +436,9 @@ const SettingsView = ({ room }: SettingsViewProps) => { +
+ +
{window.Notification && !window.gomuksAndroid &&
- {renderRightPanelContent(props)} + + {renderRightPanelContent(props)} +
} diff --git a/web/src/ui/roomview/RoomView.css b/web/src/ui/roomview/RoomView.css index ec525b1..fbcaf22 100644 --- a/web/src/ui/roomview/RoomView.css +++ b/web/src/ui/roomview/RoomView.css @@ -18,6 +18,13 @@ div.room-view { justify-content: center; align-items: center; } + + > div.room-view-error { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + } } div#mobile-event-menu-container { diff --git a/web/src/ui/roomview/RoomView.tsx b/web/src/ui/roomview/RoomView.tsx index a646395..d0cd8c6 100644 --- a/web/src/ui/roomview/RoomView.tsx +++ b/web/src/ui/roomview/RoomView.tsx @@ -19,6 +19,7 @@ import MessageComposer from "../composer/MessageComposer.tsx" import TypingNotifications from "../composer/TypingNotifications.tsx" import RightPanel, { RightPanelProps } from "../rightpanel/RightPanel.tsx" import TimelineView from "../timeline/TimelineView.tsx" +import ErrorBoundary from "../util/ErrorBoundary.tsx" import RoomViewHeader from "./RoomViewHeader.tsx" import { RoomContext, RoomContextData } from "./roomcontext.ts" import "./RoomView.css" @@ -49,11 +50,13 @@ const RoomView = ({ room, rightPanelResizeHandle, rightPanel }: RoomViewProps) = } return
-
- - - - + +
+ + + + +
{rightPanelResizeHandle} {rightPanel && } diff --git a/web/src/ui/timeline/content/ContentErrorBoundary.tsx b/web/src/ui/timeline/content/ContentErrorBoundary.tsx index 7265c81..2862c67 100644 --- a/web/src/ui/timeline/content/ContentErrorBoundary.tsx +++ b/web/src/ui/timeline/content/ContentErrorBoundary.tsx @@ -14,27 +14,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import React from "react" +import ErrorBoundary from "@/ui/util/ErrorBoundary.tsx" -export default class ContentErrorBoundary extends React.Component<{ children: React.ReactNode }, { error?: Error }> { - constructor(props: { children: React.ReactNode }) { - super(props) - this.state = { error: undefined } - } - - static getDerivedStateFromError(error: unknown) { - if (error instanceof Error) { - error = new Error(`${error}`) - } - return { error } - } - - render() { - if (this.state.error) { - return
- Failed to render event: {this.state.error.message.replace(/^Error: /, "")} -
- } - - return this.props.children +export default class ContentErrorBoundary extends ErrorBoundary { + renderError(message: string): React.JSX.Element { + return
+ Failed to render event: {message} +
} } diff --git a/web/src/ui/util/ErrorBoundary.tsx b/web/src/ui/util/ErrorBoundary.tsx new file mode 100644 index 0000000..1c98bc2 --- /dev/null +++ b/web/src/ui/util/ErrorBoundary.tsx @@ -0,0 +1,52 @@ +// gomuks - A Matrix client written in Go. +// 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 . +import React from "react" + +export interface ErrorBoundaryProps { + thing?: string + wrapperClassName?: string + children: React.ReactNode +} + +export default class ErrorBoundary extends React.Component { + constructor(props: ErrorBoundaryProps) { + super(props) + this.state = { error: undefined } + } + + static getDerivedStateFromError(error: unknown) { + return { + error: `${error}`.replace(/^Error: /, ""), + } + } + + renderError(message: string) { + const inner = <> + Failed to render {this.props.thing ?? "component"}: {message} + + if (this.props.wrapperClassName) { + return
{inner}
+ } + return inner + } + + render() { + if (this.state.error) { + return this.renderError(this.state.error) + } + return this.props.children + } +} From 289b428644390759a1a5abf621bed7ccdbedda3c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 16 Feb 2025 17:33:22 +0200 Subject: [PATCH 33/78] dependencies: update --- desktop/go.mod | 24 +++++++++++------------ desktop/go.sum | 52 +++++++++++++++++++++++++------------------------- go.mod | 20 +++++++++---------- go.sum | 36 +++++++++++++++++----------------- 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/desktop/go.mod b/desktop/go.mod index f163739..4bd1e82 100644 --- a/desktop/go.mod +++ b/desktop/go.mod @@ -8,7 +8,7 @@ require github.com/wailsapp/wails/v3 v3.0.0-alpha.9 require ( go.mau.fi/gomuks v0.4.0 - go.mau.fi/util v0.8.5-0.20250208141401-fde0c0c733f1 + go.mau.fi/util v0.8.5 ) require ( @@ -47,7 +47,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.24 // indirect - github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect + github.com/petermattis/goid v0.0.0-20250211185408-f2b9d978cd7a // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/rivo/uniseg v0.4.7 // indirect @@ -66,20 +66,20 @@ require ( github.com/yuin/goldmark v1.7.8 // indirect go.mau.fi/webp v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c // indirect - golang.org/x/image v0.23.0 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/exp v0.0.0-20250215185904-eff6e970281f // indirect + golang.org/x/image v0.24.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - maunium.net/go/mautrix v0.23.1-0.20250208141851-29319ccfd515 // indirect + maunium.net/go/mautrix v0.23.1 // indirect mvdan.cc/xurls/v2 v2.6.0 // indirect ) diff --git a/desktop/go.sum b/desktop/go.sum index 2493ef7..1f6870f 100644 --- a/desktop/go.sum +++ b/desktop/go.sum @@ -113,8 +113,8 @@ github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBW github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 h1:ah1dvbqPMN5+ocrg/ZSgZ6k8bOk+kcZQ7fnyx6UvOm4= -github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20250211185408-f2b9d978cd7a h1:ckxP/kGzsxvxXo8jO6E/0QJ8MMmwI7IRj4Fys9QbAZA= +github.com/petermattis/goid v0.0.0-20250211185408-f2b9d978cd7a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= @@ -166,8 +166,8 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -go.mau.fi/util v0.8.5-0.20250208141401-fde0c0c733f1 h1:XQ47o9cbYOCtohOkxXIzIM3xnSsR/8lggdgLEZm8PHU= -go.mau.fi/util v0.8.5-0.20250208141401-fde0c0c733f1/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY= +go.mau.fi/util v0.8.5 h1:PwCAAtcfK0XxZ4sdErJyfBMkTEWoQU33aB7QqDDzQRI= +go.mau.fi/util v0.8.5/go.mod h1:Ycug9mrbztlahHPEJ6H5r8Nu/xqZaWbE5vPHVWmfz6M= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= @@ -177,17 +177,17 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg= +golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= -golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= +golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= +golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -196,13 +196,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -222,15 +222,15 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -238,14 +238,14 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -261,7 +261,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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.23.1-0.20250208141851-29319ccfd515 h1:VW2duvA0deqBlNgqRF/6RPJgONAepSROrXUwUY/3HyY= -maunium.net/go/mautrix v0.23.1-0.20250208141851-29319ccfd515/go.mod h1:m83xh+EaK2UMyVwRsohhGeXSZ8b7GFOg/UkMVlWSpPc= +maunium.net/go/mautrix v0.23.1 h1:xZtX43YZF3WRxkdR+oMUrpiQe+jbjc+LeXLxHuXP5IM= +maunium.net/go/mautrix v0.23.1/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= diff --git a/go.mod b/go.mod index ff442d9..100f4a9 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module go.mau.fi/gomuks go 1.23.0 -toolchain go1.23.5 +toolchain go1.24.0 require ( github.com/alecthomas/chroma/v2 v2.15.0 @@ -18,16 +18,16 @@ require ( github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 github.com/yuin/goldmark v1.7.8 - go.mau.fi/util v0.8.5-0.20250208141401-fde0c0c733f1 + go.mau.fi/util v0.8.5 go.mau.fi/webp v0.2.0 go.mau.fi/zeroconfig v0.1.3 - golang.org/x/crypto v0.32.0 - golang.org/x/image v0.23.0 - golang.org/x/net v0.34.0 - golang.org/x/text v0.21.0 + golang.org/x/crypto v0.33.0 + golang.org/x/image v0.24.0 + golang.org/x/net v0.35.0 + golang.org/x/text v0.22.0 gopkg.in/yaml.v3 v3.0.1 maunium.net/go/mauflag v1.0.0 - maunium.net/go/mautrix v0.23.1-0.20250208141851-29319ccfd515 + maunium.net/go/mautrix v0.23.1 mvdan.cc/xurls/v2 v2.6.0 ) @@ -37,11 +37,11 @@ require ( github.com/dlclark/regexp2 v1.11.4 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect + github.com/petermattis/goid v0.0.0-20250211185408-f2b9d978cd7a // indirect github.com/rs/xid v1.6.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect - golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/exp v0.0.0-20250215185904-eff6e970281f // indirect + golang.org/x/sys v0.30.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect ) diff --git a/go.sum b/go.sum index 1360fba..75264eb 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 h1:ah1dvbqPMN5+ocrg/ZSgZ6k8bOk+kcZQ7fnyx6UvOm4= -github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20250211185408-f2b9d978cd7a h1:ckxP/kGzsxvxXo8jO6E/0QJ8MMmwI7IRj4Fys9QbAZA= +github.com/petermattis/goid v0.0.0-20250211185408-f2b9d978cd7a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= 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= @@ -68,30 +68,30 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -go.mau.fi/util v0.8.5-0.20250208141401-fde0c0c733f1 h1:XQ47o9cbYOCtohOkxXIzIM3xnSsR/8lggdgLEZm8PHU= -go.mau.fi/util v0.8.5-0.20250208141401-fde0c0c733f1/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY= +go.mau.fi/util v0.8.5 h1:PwCAAtcfK0XxZ4sdErJyfBMkTEWoQU33aB7QqDDzQRI= +go.mau.fi/util v0.8.5/go.mod h1:Ycug9mrbztlahHPEJ6H5r8Nu/xqZaWbE5vPHVWmfz6M= go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg= +golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= -golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= +golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= 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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= @@ -100,7 +100,7 @@ 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/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.23.1-0.20250208141851-29319ccfd515 h1:VW2duvA0deqBlNgqRF/6RPJgONAepSROrXUwUY/3HyY= -maunium.net/go/mautrix v0.23.1-0.20250208141851-29319ccfd515/go.mod h1:m83xh+EaK2UMyVwRsohhGeXSZ8b7GFOg/UkMVlWSpPc= +maunium.net/go/mautrix v0.23.1 h1:xZtX43YZF3WRxkdR+oMUrpiQe+jbjc+LeXLxHuXP5IM= +maunium.net/go/mautrix v0.23.1/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= From ce60eb8a947264312bd0c6a0ebfdf0c81edcb4d0 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 16 Feb 2025 17:34:11 +0200 Subject: [PATCH 34/78] hicli/paginate: add missing context cancel --- pkg/hicli/paginate.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/hicli/paginate.go b/pkg/hicli/paginate.go index f41ceee..98996d2 100644 --- a/pkg/hicli/paginate.go +++ b/pkg/hicli/paginate.go @@ -279,6 +279,7 @@ func (h *HiClient) GetReceipts(ctx context.Context, roomID id.RoomID, eventIDs [ func (h *HiClient) PaginateServer(ctx context.Context, roomID id.RoomID, limit int) (*PaginationResponse, error) { ctx, cancel := context.WithCancelCause(ctx) + defer cancel(context.Canceled) h.paginationInterrupterLock.Lock() if _, alreadyPaginating := h.paginationInterrupter[roomID]; alreadyPaginating { h.paginationInterrupterLock.Unlock() From d1fd7f576aa3e5c8a928003f0064769e5beeab03 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 16 Feb 2025 18:14:39 +0200 Subject: [PATCH 35/78] ci: update go version --- .github/workflows/go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 6adf818..8a93b65 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,8 +7,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: ["1.23"] - name: Lint Go ${{ matrix.go-version == '1.23' && '(latest)' || '(old)' }} + go-version: ["1.23", "1.24"] + name: Lint Go ${{ matrix.go-version == '1.24' && '(latest)' || '(old)' }} steps: - uses: actions/checkout@v4 From db709896d6e2b9b0ec69d1a23138d60790a6398f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 16 Feb 2025 20:52:25 +0200 Subject: [PATCH 36/78] ci: add missing environment variable --- .github/workflows/go.yml | 3 +++ .gitlab-ci.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 8a93b65..2d3bd11 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -2,6 +2,9 @@ name: Go on: [push, pull_request] +env: + GOTOOLCHAIN: local + jobs: lint: runs-on: ubuntu-latest diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e1c09a7..2fd3ac1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,6 +13,9 @@ cache: paths: - .cache +variables: + GOTOOLCHAIN: local + frontend: image: node:22-alpine stage: frontend From 5c27592b8cacb1bb5208af357b1bc88eeb6b8a88 Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Fri, 21 Feb 2025 10:44:35 -0700 Subject: [PATCH 37/78] web/timeline: add variable for vertical padding (#595) Signed-off-by: Sumner Evans --- web/src/index.css | 1 + web/src/ui/timeline/TimelineEvent.css | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/index.css b/web/src/index.css index 5d6f39d..63b2892 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -86,6 +86,7 @@ --timeline-message-gap-small-event: 0; --timeline-sender-name-timestamp-gap: .25rem; --timeline-sender-name-content-gap: 0; + --timeline-vertical-padding: 0; --timeline-horizontal-padding: 1.5rem; --timeline-status-size: 4rem; diff --git a/web/src/ui/timeline/TimelineEvent.css b/web/src/ui/timeline/TimelineEvent.css index e83f788..912fecd 100644 --- a/web/src/ui/timeline/TimelineEvent.css +++ b/web/src/ui/timeline/TimelineEvent.css @@ -2,7 +2,7 @@ div.timeline-event { width: 100%; max-width: 100%; box-sizing: border-box; - padding: 0 var(--timeline-horizontal-padding); + padding: var(--timeline-vertical-padding) var(--timeline-horizontal-padding); display: grid; grid-template: "cmc cmc cmc empty" 0 From c228b7f1834684b43f3b9d3fa19991992455feec Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 16:18:31 +0200 Subject: [PATCH 38/78] web/timeline: split body types by state key --- web/src/ui/timeline/content/index.ts | 111 +++++++++++++++------------ 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/web/src/ui/timeline/content/index.ts b/web/src/ui/timeline/content/index.ts index 221c6bd..6f91230 100644 --- a/web/src/ui/timeline/content/index.ts +++ b/web/src/ui/timeline/content/index.ts @@ -38,62 +38,73 @@ export function getBodyType(evt: MemDBEvent, forReply = false): React.FunctionCo if (evt.relation_type === "m.replace") { return HiddenEvent } - switch (evt.type) { - case "m.room.message": - if (evt.redacted_by) { - return RedactedBody + if (evt.state_key === "") { + // State events which must have an empty state key + switch (evt.type) { + case "m.room.name": + return RoomNameBody + case "m.room.avatar": + return RoomAvatarBody + case "m.room.pinned_events": + return PinnedEventsBody + case "m.room.power_levels": + return PowerLevelBody } - switch (evt.content?.msgtype) { - case "m.text": - case "m.notice": - case "m.emote": - return TextMessageBody - case "m.image": - case "m.video": - case "m.audio": - case "m.file": - if (forReply) { + } else if (evt.state_key !== undefined) { + // State events which must have a non-empty state key + switch (evt.type) { + case "m.room.member": + return MemberBody + case "m.room.server_acl": + return ACLBody + case "m.policy.rule.user": + return PolicyRuleBody + case "m.policy.rule.room": + return PolicyRuleBody + case "m.policy.rule.server": + return PolicyRuleBody + } + } else { + // Non-state events + switch (evt.type) { + case "m.room.message": + if (evt.redacted_by) { + return RedactedBody + } + switch (evt.content?.msgtype) { + case "m.text": + case "m.notice": + case "m.emote": + return TextMessageBody + case "m.image": + case "m.video": + case "m.audio": + case "m.file": + if (forReply) { + return TextMessageBody + } + return MediaMessageBody + case "m.location": + if (forReply) { + return TextMessageBody + } + return LocationMessageBody + default: + return UnknownMessageBody + } + case "m.sticker": + if (evt.redacted_by) { + return RedactedBody + } else if (forReply) { return TextMessageBody } return MediaMessageBody - case "m.location": - if (forReply) { - return TextMessageBody + case "m.room.encrypted": + if (evt.redacted_by) { + return RedactedBody } - return LocationMessageBody - default: - return UnknownMessageBody + return EncryptedBody } - case "m.sticker": - if (evt.redacted_by) { - return RedactedBody - } else if (forReply) { - return TextMessageBody - } - return MediaMessageBody - case "m.room.encrypted": - if (evt.redacted_by) { - return RedactedBody - } - return EncryptedBody - case "m.room.member": - return MemberBody - case "m.room.name": - return RoomNameBody - case "m.room.avatar": - return RoomAvatarBody - case "m.room.server_acl": - return ACLBody - case "m.policy.rule.user": - return PolicyRuleBody - case "m.policy.rule.room": - return PolicyRuleBody - case "m.policy.rule.server": - return PolicyRuleBody - case "m.room.pinned_events": - return PinnedEventsBody - case "m.room.power_levels": - return PowerLevelBody } return HiddenEvent } From 7c664c270026618d2cd27511173e0f3cdb4a8ab9 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 16:38:12 +0200 Subject: [PATCH 39/78] hicli/sync: fix sync backoff calculation --- pkg/hicli/syncwrap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hicli/syncwrap.go b/pkg/hicli/syncwrap.go index 8492da2..1abeaaa 100644 --- a/pkg/hicli/syncwrap.go +++ b/pkg/hicli/syncwrap.go @@ -69,7 +69,7 @@ func (h *hiSyncer) OnFailedSync(_ *mautrix.RespSync, err error) (time.Duration, c.syncErrors++ delay := 1 * time.Second if c.syncErrors > 5 { - delay = max(time.Duration(c.syncErrors)*time.Second, 30*time.Second) + delay = min(time.Duration(c.syncErrors)*time.Second, 30*time.Second) } c.markSyncErrored(err, false) c.Log.Err(err).Dur("retry_in", delay).Msg("Sync failed") From a9e459b44872de6c5c972456640a365977c70130 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 17:49:42 +0200 Subject: [PATCH 40/78] web/modal: allow two layers of modals --- web/src/ui/MainScreen.tsx | 47 +++++++++++++++++++----------------- web/src/ui/modal/Modal.tsx | 34 ++++++++++++++++---------- web/src/ui/modal/contexts.ts | 5 +++- 3 files changed, 50 insertions(+), 36 deletions(-) diff --git a/web/src/ui/MainScreen.tsx b/web/src/ui/MainScreen.tsx index c0a5b48..bd7c4ed 100644 --- a/web/src/ui/MainScreen.tsx +++ b/web/src/ui/MainScreen.tsx @@ -24,7 +24,7 @@ import ClientContext from "./ClientContext.ts" import MainScreenContext, { MainScreenContextFields } from "./MainScreenContext.ts" import StylePreferences from "./StylePreferences.tsx" import Keybindings from "./keybindings.ts" -import { ModalWrapper } from "./modal" +import { ModalContext, ModalWrapper, NestableModalContext } from "./modal" import RightPanel, { RightPanelProps } from "./rightpanel/RightPanel.tsx" import RoomList from "./roomlist/RoomList.tsx" import RoomPreview, { RoomPreviewProps } from "./roomview/RoomPreview.tsx" @@ -422,28 +422,31 @@ const MainScreen = () => { return () => clearTimeout(timeout) } }, [activeRoom, prevActiveRoom]) + const mainContent =
+ + {resizeHandle1} + {renderedRoom + ? renderedRoom instanceof RoomStateStore + ? + : + : rightPanel && <> +
+ {resizeHandle2} + {rightPanel && } + } +
return - - -
- - {resizeHandle1} - {renderedRoom - ? renderedRoom instanceof RoomStateStore - ? - : - : rightPanel && <> -
- {resizeHandle2} - {rightPanel && } - } -
- {syncLoader} + + + + {mainContent} + {syncLoader} +
} diff --git a/web/src/ui/modal/Modal.tsx b/web/src/ui/modal/Modal.tsx index 4d0baf9..668a266 100644 --- a/web/src/ui/modal/Modal.tsx +++ b/web/src/ui/modal/Modal.tsx @@ -13,10 +13,16 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React, { JSX, useCallback, useEffect, useLayoutEffect, useReducer, useRef } from "react" -import { ModalCloseContext, ModalContext, ModalState } from "./contexts.ts" +import React, { Context, JSX, useCallback, useEffect, useLayoutEffect, useReducer, useRef } from "react" +import { ModalCloseContext, ModalState, openModal } from "./contexts.ts" -const ModalWrapper = ({ children }: { children: React.ReactNode }) => { +interface ModalWrapperProps { + children: React.ReactNode + ContextType: Context + historyStateKey: string +} + +const ModalWrapper = ({ children, ContextType, historyStateKey }: ModalWrapperProps) => { const [state, setState] = useReducer((prevState: ModalState | null, newState: ModalState | null) => { prevState?.onClose?.() return newState @@ -25,26 +31,28 @@ const ModalWrapper = ({ children }: { children: React.ReactNode }) => { if (evt && evt.target !== evt.currentTarget) { return } + console.log("MEOW", evt) + evt?.stopPropagation() setState(null) - if (history.state?.modal) { + if (history.state?.[historyStateKey]) { history.back() } - }, []) + }, [historyStateKey]) const onKeyWrapper = (evt: React.KeyboardEvent) => { if (evt.key === "Escape") { setState(null) - if (history.state?.modal) { + if (history.state?.[historyStateKey]) { history.back() } } evt.stopPropagation() } const openModal = useCallback((newState: ModalState) => { - if (!history.state?.modal && newState.captureInput !== false) { - history.pushState({ ...(history.state ?? {}), modal: true }, "") + if (!history.state?.[historyStateKey] && newState.captureInput !== false) { + history.pushState({ ...(history.state ?? {}), [historyStateKey]: true }, "") } setState(newState) - }, []) + }, [historyStateKey]) const wrapperRef = useRef(null) useLayoutEffect(() => { if (wrapperRef.current && (!document.activeElement || !wrapperRef.current.contains(document.activeElement))) { @@ -54,13 +62,13 @@ const ModalWrapper = ({ children }: { children: React.ReactNode }) => { useEffect(() => { window.closeModal = onClickWrapper const listener = (evt: PopStateEvent) => { - if (!evt.state?.modal) { + if (!evt.state?.[historyStateKey]) { setState(null) } } window.addEventListener("popstate", listener) return () => window.removeEventListener("popstate", listener) - }, [onClickWrapper]) + }, [historyStateKey, onClickWrapper]) let modal: JSX.Element | null = null if (state) { let content = {state.content} @@ -85,10 +93,10 @@ const ModalWrapper = ({ children }: { children: React.ReactNode }) => { modal = content } } - return + return {children} {modal} - + } export default ModalWrapper diff --git a/web/src/ui/modal/contexts.ts b/web/src/ui/modal/contexts.ts index dad5963..083a039 100644 --- a/web/src/ui/modal/contexts.ts +++ b/web/src/ui/modal/contexts.ts @@ -35,9 +35,12 @@ export interface ModalState { captureInput?: boolean } -type openModal = (state: ModalState) => void +export type openModal = (state: ModalState) => void export const ModalContext = createContext(() => console.error("Tried to open modal without being inside context")) +export const NestableModalContext = createContext(() => + console.error("Tried to open nestable modal without being inside context")) + export const ModalCloseContext = createContext<() => void>(() => {}) From 4fc9b88ec6166504d986784168a71a5f873107ee Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 17:53:32 +0200 Subject: [PATCH 41/78] web/timeline: add edit history modal --- pkg/hicli/database/event.go | 10 ++- pkg/hicli/json-commands.go | 17 +++-- pkg/hicli/paginate.go | 31 --------- web/src/api/client.ts | 12 ++++ web/src/api/rpc.ts | 6 +- web/src/api/statestore/room.ts | 10 +++ web/src/api/types/hitypes.ts | 1 + web/src/ui/timeline/EventEditHistory.css | 14 ++++ web/src/ui/timeline/EventEditHistory.tsx | 81 ++++++++++++++++++++++++ web/src/ui/timeline/TimelineEvent.css | 9 ++- web/src/ui/timeline/TimelineEvent.tsx | 45 +++++++++---- 11 files changed, 181 insertions(+), 55 deletions(-) create mode 100644 web/src/ui/timeline/EventEditHistory.css create mode 100644 web/src/ui/timeline/EventEditHistory.tsx diff --git a/pkg/hicli/database/event.go b/pkg/hicli/database/event.go index cfef7e7..e0420de 100644 --- a/pkg/hicli/database/event.go +++ b/pkg/hicli/database/event.go @@ -36,7 +36,11 @@ const ( getEventByID = getEventBaseQuery + `WHERE event_id = $1` getEventByTransactionID = getEventBaseQuery + `WHERE transaction_id = $1` getFailedEventsByMegolmSessionID = getEventBaseQuery + `WHERE room_id = $1 AND megolm_session_id = $2 AND decryption_error IS NOT NULL` - insertEventBaseQuery = ` + getRelatedEventsQuery = getEventBaseQuery + ` + WHERE room_id = $1 AND relates_to = $2 AND ($3 = '' OR relation_type = $3) + ORDER BY timestamp ASC + ` + insertEventBaseQuery = ` INSERT INTO event ( room_id, event_id, sender, type, state_key, timestamp, content, decrypted, decrypted_type, unsigned, local_content, transaction_id, redacted_by, relates_to, relation_type, @@ -112,6 +116,10 @@ func (eq *EventQuery) GetByRowID(ctx context.Context, rowID EventRowID) (*Event, return eq.QueryOne(ctx, getEventByRowID, rowID) } +func (eq *EventQuery) GetRelatedEvents(ctx context.Context, roomID id.RoomID, eventID id.EventID, relationType event.RelationType) ([]*Event, error) { + return eq.QueryMany(ctx, getRelatedEventsQuery, roomID, eventID, relationType) +} + func (eq *EventQuery) GetByRowIDs(ctx context.Context, rowIDs ...EventRowID) ([]*Event, error) { query, params := buildMultiEventGetFunction(nil, rowIDs, getManyEventsByRowID) return eq.QueryMany(ctx, query, params...) diff --git a/pkg/hicli/json-commands.go b/pkg/hicli/json-commands.go index ea7c70f..3b29725 100644 --- a/pkg/hicli/json-commands.go +++ b/pkg/hicli/json-commands.go @@ -125,10 +125,10 @@ func (h *HiClient) handleJSONCommand(ctx context.Context, req *JSONCommand) (any return unmarshalAndCall(req.Data, func(params *getEventParams) (*database.Event, error) { return h.GetEvent(ctx, params.RoomID, params.EventID) }) - //case "get_events_by_rowids": - // return unmarshalAndCall(req.Data, func(params *getEventsByRowIDsParams) ([]*database.Event, error) { - // return h.GetEventsByRowIDs(ctx, params.RowIDs) - // }) + case "get_related_events": + return unmarshalAndCall(req.Data, func(params *getRelatedEventsParams) ([]*database.Event, error) { + return h.DB.Event.GetRelatedEvents(ctx, params.RoomID, params.EventID, params.RelationType) + }) case "get_room_state": return unmarshalAndCall(req.Data, func(params *getRoomStateParams) ([]*database.Event, error) { return h.GetRoomState(ctx, params.RoomID, params.IncludeMembers, params.FetchMembers, params.Refetch) @@ -315,9 +315,12 @@ type getEventParams struct { EventID id.EventID `json:"event_id"` } -//type getEventsByRowIDsParams struct { -// RowIDs []database.EventRowID `json:"row_ids"` -//} +type getRelatedEventsParams struct { + RoomID id.RoomID `json:"room_id"` + EventID id.EventID `json:"event_id"` + + RelationType event.RelationType `json:"relation_type"` +} type getRoomStateParams struct { RoomID id.RoomID `json:"room_id"` diff --git a/pkg/hicli/paginate.go b/pkg/hicli/paginate.go index 98996d2..424635d 100644 --- a/pkg/hicli/paginate.go +++ b/pkg/hicli/paginate.go @@ -22,37 +22,6 @@ import ( var ErrPaginationAlreadyInProgress = errors.New("pagination is already in progress") -/*func (h *HiClient) GetEventsByRowIDs(ctx context.Context, rowIDs []database.EventRowID) ([]*database.Event, error) { - events, err := h.DB.Event.GetByRowIDs(ctx, rowIDs...) - if err != nil { - return nil, err - } else if len(events) == 0 { - return events, nil - } - firstRoomID := events[0].RoomID - allInSameRoom := true - for _, evt := range events { - h.ReprocessExistingEvent(ctx, evt) - if evt.RoomID != firstRoomID { - allInSameRoom = false - break - } - } - if allInSameRoom { - err = h.DB.Event.FillLastEditRowIDs(ctx, firstRoomID, events) - if err != nil { - return events, fmt.Errorf("failed to fill last edit row IDs: %w", err) - } - err = h.DB.Event.FillReactionCounts(ctx, firstRoomID, events) - if err != nil { - return events, fmt.Errorf("failed to fill reaction counts: %w", err) - } - } else { - // TODO slow path where events are collected and filling is done one room at a time? - } - return events, nil -}*/ - func (h *HiClient) GetEvent(ctx context.Context, roomID id.RoomID, eventID id.EventID) (*database.Event, error) { if evt, err := h.DB.Event.GetByID(ctx, eventID); err != nil { return nil, fmt.Errorf("failed to get event from database: %w", err) diff --git a/web/src/api/client.ts b/web/src/api/client.ts index 4f26ef4..331ed47 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -26,6 +26,7 @@ import type { ImagePackRooms, RPCEvent, RawDBEvent, + RelationType, RoomID, RoomStateGUID, SyncStatus, @@ -235,6 +236,17 @@ export default class Client { ) } + async getRelatedEvents(room: RoomStateStore | RoomID | undefined, eventID: EventID, relationType?: RelationType) { + if (typeof room === "string") { + room = this.store.rooms.get(room) + } + if (!room) { + return [] + } + const events = await this.rpc.getRelatedEvents(room.roomID, eventID, relationType) + return events.map(evt => room.getOrApplyEvent(evt)) + } + async pinMessage(room: RoomStateStore, evtID: EventID, wantPinned: boolean) { const pinnedEvents = room.getPinnedEvents() const currentlyPinned = pinnedEvents.includes(evtID) diff --git a/web/src/api/rpc.ts b/web/src/api/rpc.ts index 525c373..e79c820 100644 --- a/web/src/api/rpc.ts +++ b/web/src/api/rpc.ts @@ -19,7 +19,6 @@ import type { ClientWellKnown, DBPushRegistration, EventID, - EventRowID, EventType, JSONValue, LoginFlowsResponse, @@ -34,6 +33,7 @@ import type { RawDBEvent, ReceiptType, RelatesTo, + RelationType, ResolveAliasResponse, RespOpenIDToken, RespRoomJoin, @@ -222,8 +222,8 @@ export default abstract class RPCClient { return this.request("get_event", { room_id, event_id }) } - getEventsByRowIDs(row_ids: EventRowID[]): Promise { - return this.request("get_events_by_row_ids", { row_ids }) + getRelatedEvents(room_id: RoomID, event_id: EventID, relation_type?: RelationType): Promise { + return this.request("get_related_events", { room_id, event_id, relation_type }) } paginate(room_id: RoomID, max_timeline_id: TimelineRowID, limit: number): Promise { diff --git a/web/src/api/statestore/room.ts b/web/src/api/statestore/room.ts index f0f5a0f..497d6e3 100644 --- a/web/src/api/statestore/room.ts +++ b/web/src/api/statestore/room.ts @@ -344,6 +344,14 @@ export class RoomStateStore { return true } + getOrApplyEvent(evt: RawDBEvent) { + const existing = this.eventsByRowID.get(evt.rowid) + if (existing) { + return existing + } + return this.applyEvent(evt) + } + applyEvent(evt: RawDBEvent, pending: boolean = false) { const memEvt = evt as MemDBEvent memEvt.mem = true @@ -362,6 +370,7 @@ export class RoomStateStore { memEvt.last_edit = this.eventsByRowID.get(memEvt.last_edit_rowid) if (memEvt.last_edit) { memEvt.orig_content = memEvt.content + memEvt.orig_local_content = memEvt.local_content memEvt.content = memEvt.last_edit.content["m.new_content"] memEvt.local_content = memEvt.last_edit.local_content } @@ -371,6 +380,7 @@ export class RoomStateStore { this.eventsByRowID.set(editTarget.rowid, { ...editTarget, last_edit: memEvt, + orig_local_content: editTarget.local_content, orig_content: editTarget.content, content: memEvt.content["m.new_content"], local_content: memEvt.local_content, diff --git a/web/src/api/types/hitypes.ts b/web/src/api/types/hitypes.ts index 7defc86..bb352d4 100644 --- a/web/src/api/types/hitypes.ts +++ b/web/src/api/types/hitypes.ts @@ -156,6 +156,7 @@ export interface MemDBEvent extends BaseDBEvent { pending: boolean encrypted?: EncryptedEventContent orig_content?: UnknownEventContent + orig_local_content?: LocalContent last_edit?: MemDBEvent } diff --git a/web/src/ui/timeline/EventEditHistory.css b/web/src/ui/timeline/EventEditHistory.css new file mode 100644 index 0000000..171322a --- /dev/null +++ b/web/src/ui/timeline/EventEditHistory.css @@ -0,0 +1,14 @@ +div.event-edit-history-wrapper { + padding: 0 !important; +} + +div.event-edit-history-modal { + --timeline-status-size: 0; + --timeline-horizontal-padding: 0; + min-width: 20rem; + padding: 1rem; + + > p { + margin: 0; + } +} diff --git a/web/src/ui/timeline/EventEditHistory.tsx b/web/src/ui/timeline/EventEditHistory.tsx new file mode 100644 index 0000000..7fbfb0f --- /dev/null +++ b/web/src/ui/timeline/EventEditHistory.tsx @@ -0,0 +1,81 @@ +// gomuks - A Matrix client written in Go. +// 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 . +import { use, useEffect, useState } from "react" +import { ScaleLoader } from "react-spinners" +import { MemDBEvent } from "@/api/types" +import ClientContext from "../ClientContext.ts" +import { RoomContext, RoomContextData } from "../roomview/roomcontext.ts" +import TimelineEvent from "./TimelineEvent.tsx" +import "./EventEditHistory.css" + +interface EventEditHistoryProps { + evt: MemDBEvent + roomCtx: RoomContextData +} + +const EventEditHistory = ({ evt, roomCtx }: EventEditHistoryProps) => { + const client = use(ClientContext)! + const [revisions, setRevisions] = useState([]) + const [error, setError] = useState("") + const [loading, setLoading] = useState(true) + useEffect(() => { + setLoading(true) + setError("") + setRevisions([]) + client.getRelatedEvents(roomCtx.store, evt.event_id, "m.replace").then( + edits => { + setRevisions([{ + ...evt, + content: evt.orig_content ?? evt.content, + local_content: evt.orig_local_content ?? evt.local_content, + last_edit: undefined, + reactions: undefined, + orig_content: undefined, + orig_local_content: undefined, + }, ...edits.map(editEvt => ({ + ...editEvt, + content: editEvt.content["m.new_content"] ?? editEvt.content, + orig_content: editEvt.content, + relation_type: undefined, + reactions: undefined, + }))]) + }, + err => { + console.error("Failed to get event edit history", err) + setError(`${err}`) + }, + ).finally(() => setLoading(false)) + }, [client, roomCtx, evt]) + + if (loading) { + return + } else if (error) { + return
Failed to load :( {error}
+ } + return <> + +

Event has {revisions.length} revisions

+ {revisions.map((rev, i) => )} +
+ +} + +export default EventEditHistory diff --git a/web/src/ui/timeline/TimelineEvent.css b/web/src/ui/timeline/TimelineEvent.css index 912fecd..0d3c54b 100644 --- a/web/src/ui/timeline/TimelineEvent.css +++ b/web/src/ui/timeline/TimelineEvent.css @@ -79,7 +79,7 @@ div.timeline-event { } } - > span.event-time, > span.event-edited { + > span.event-time { font-size: .7rem; color: var(--secondary-text-color); } @@ -100,6 +100,13 @@ div.timeline-event { overflow: hidden; overflow-wrap: anywhere; contain: content; + + > div.event-edited { + font-size: .7rem; + color: var(--secondary-text-color); + user-select: none; + cursor: var(--clickable-cursor); + } } > div.event-send-status { diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index 0c6e711..72eaafa 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -22,8 +22,9 @@ import { isMobileDevice } from "@/util/ismobile.ts" import { getDisplayname, isEventID } from "@/util/validation.ts" import ClientContext from "../ClientContext.ts" import MainScreenContext from "../MainScreenContext.ts" -import { ModalContext } from "../modal" +import { ModalContext, NestableModalContext } from "../modal" import { useRoomContext } from "../roomview/roomcontext.ts" +import EventEditHistory from "./EventEditHistory.tsx" import ReadReceipts from "./ReadReceipts.tsx" import { ReplyIDBody } from "./ReplyBody.tsx" import URLPreviews from "./URLPreviews.tsx" @@ -40,6 +41,7 @@ export interface TimelineEventProps { disableMenu?: boolean smallReplies?: boolean isFocused?: boolean + editHistoryView?: boolean } const fullTimeFormatter = new Intl.DateTimeFormat("en-GB", { dateStyle: "full", timeStyle: "medium" }) @@ -75,11 +77,14 @@ const EventSendStatus = ({ evt }: { evt: MemDBEvent }) => { } } -const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: TimelineEventProps) => { +const TimelineEvent = ({ + evt, prevEvt, disableMenu, smallReplies, isFocused, editHistoryView, +}: TimelineEventProps) => { const roomCtx = useRoomContext() const client = use(ClientContext)! const mainScreen = use(MainScreenContext) const openModal = use(ModalContext) + const openNestableModal = use(NestableModalContext) const [forceContextMenuOpen, setForceContextMenuOpen] = useState(false) const onContextMenu = (mouseEvt: React.MouseEvent) => { const targetElem = mouseEvt.target as HTMLElement @@ -115,6 +120,15 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T mouseEvt.stopPropagation() roomCtx.setFocusedEventRowID(roomCtx.focusedEventRowID === evt.rowid ? null : evt.rowid) } + const openEditHistory = () => { + openNestableModal({ + content: , + dimmed: true, + boxed: true, + boxClass: "event-edit-history-wrapper", + innerBoxClass: "event-edit-history-modal", + }) + } const memberEvt = useRoomMember(client, roomCtx.store, evt.sender) const memberEvtContent = memberEvt?.content as MemberEventContent | undefined const BodyType = getBodyType(evt) @@ -136,7 +150,7 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T if (evt.sender === client.userID) { wrapperClassNames.push("own-event") } - if (isMobileDevice || disableMenu) { + if ((isMobileDevice && !editHistoryView) || disableMenu) { wrapperClassNames.push("no-hover") } if (isFocused) { @@ -159,7 +173,7 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T const replyTo = relatesTo?.["m.in_reply_to"]?.event_id let replyAboveMessage: JSX.Element | null = null let replyInMessage: JSX.Element | null = null - if (isEventID(replyTo) && BodyType !== HiddenEvent && !evt.redacted_by) { + if (isEventID(replyTo) && BodyType !== HiddenEvent && !evt.redacted_by && !editHistoryView) { const replyElem = - {!disableMenu && !isMobileDevice &&
@@ -258,11 +275,8 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T
} {shortTime} - {(editEventTS && editTime) ? - (edited at {formatShortTime(editEventTS)}) - : null}
:
- {shortTime} + {shortTime}
}
{replyInMessage} @@ -270,11 +284,18 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T {!isSmallBodyType && } + {(!editHistoryView && editEventTS && editTime) ?
+ (edited at {formatShortTime(editEventTS)}) +
: null} {evt.reactions ? : null}
- {!evt.event_id.startsWith("~") && roomCtx.store.preferences.display_read_receipts && + {!evt.event_id.startsWith("~") && roomCtx.store.preferences.display_read_receipts && !editHistoryView && } - {evt.sender === client.userID && evt.transaction_id ? : null} + {evt.sender === client.userID && evt.transaction_id && !editHistoryView ? : null}
return <> {dateSeparator} From 4074e2ca68a351f508a0e358d41205cc809dcc78 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 17:58:15 +0200 Subject: [PATCH 42/78] web/modal: add error boundary for content --- web/src/ui/modal/Modal.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/src/ui/modal/Modal.tsx b/web/src/ui/modal/Modal.tsx index 668a266..9574e2e 100644 --- a/web/src/ui/modal/Modal.tsx +++ b/web/src/ui/modal/Modal.tsx @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import React, { Context, JSX, useCallback, useEffect, useLayoutEffect, useReducer, useRef } from "react" +import ErrorBoundary from "../util/ErrorBoundary.tsx" import { ModalCloseContext, ModalState, openModal } from "./contexts.ts" interface ModalWrapperProps { @@ -71,7 +72,11 @@ const ModalWrapper = ({ children, ContextType, historyStateKey }: ModalWrapperPr }, [historyStateKey, onClickWrapper]) let modal: JSX.Element | null = null if (state) { - let content = {state.content} + let content = + + {state.content} + + if (state.boxed) { content =
From 42a97c8c1b7662dc22495431181711ae8bfcce97 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 18:02:41 +0200 Subject: [PATCH 43/78] dependencies: update --- desktop/go.mod | 2 +- desktop/go.sum | 4 +- go.mod | 2 +- go.sum | 4 +- web/package-lock.json | 723 +++++++++++++++++++++--------------------- 5 files changed, 367 insertions(+), 368 deletions(-) diff --git a/desktop/go.mod b/desktop/go.mod index 4bd1e82..49a6812 100644 --- a/desktop/go.mod +++ b/desktop/go.mod @@ -66,7 +66,7 @@ require ( github.com/yuin/goldmark v1.7.8 // indirect go.mau.fi/webp v0.2.0 // indirect go.mau.fi/zeroconfig v0.1.3 // indirect - golang.org/x/crypto v0.33.0 // indirect + golang.org/x/crypto v0.34.0 // indirect golang.org/x/exp v0.0.0-20250215185904-eff6e970281f // indirect golang.org/x/image v0.24.0 // indirect golang.org/x/mod v0.23.0 // indirect diff --git a/desktop/go.sum b/desktop/go.sum index 1f6870f..0aac851 100644 --- a/desktop/go.sum +++ b/desktop/go.sum @@ -177,8 +177,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA= +golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg= golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= diff --git a/go.mod b/go.mod index 100f4a9..7a3b7ea 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( go.mau.fi/util v0.8.5 go.mau.fi/webp v0.2.0 go.mau.fi/zeroconfig v0.1.3 - golang.org/x/crypto v0.33.0 + golang.org/x/crypto v0.34.0 golang.org/x/image v0.24.0 golang.org/x/net v0.35.0 golang.org/x/text v0.22.0 diff --git a/go.sum b/go.sum index 75264eb..9fb04cf 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ go.mau.fi/webp v0.2.0 h1:QVMenHw7JDb4vall5sV75JNBQj9Hw4u8AKbi1QetHvg= go.mau.fi/webp v0.2.0/go.mod h1:VSg9MyODn12Mb5pyG0NIyNFhujrmoFSsZBs8syOZD1Q= go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM= go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA= +golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg= golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= diff --git a/web/package-lock.json b/web/package-lock.json index 717b50b..015f5ba 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -69,9 +69,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", - "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { @@ -79,22 +79,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -123,14 +123,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", - "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/types": "^7.26.5", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -219,27 +219,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", + "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", - "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.5" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -249,32 +249,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", - "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.5", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.5", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -293,9 +293,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", - "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "dev": true, "license": "MIT", "dependencies": { @@ -774,13 +774,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -789,9 +789,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -802,9 +802,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -839,9 +839,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz", + "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==", "dev": true, "license": "MIT", "engines": { @@ -849,9 +849,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -859,13 +859,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.12.0", "levn": "^0.4.1" }, "engines": { @@ -925,9 +925,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1066,9 +1066,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.31.0.tgz", - "integrity": "sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", + "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", "cpu": [ "arm" ], @@ -1080,9 +1080,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.31.0.tgz", - "integrity": "sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", + "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", "cpu": [ "arm64" ], @@ -1094,9 +1094,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.31.0.tgz", - "integrity": "sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", + "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", "cpu": [ "arm64" ], @@ -1108,9 +1108,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.31.0.tgz", - "integrity": "sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", + "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", "cpu": [ "x64" ], @@ -1122,9 +1122,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.31.0.tgz", - "integrity": "sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", + "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", "cpu": [ "arm64" ], @@ -1136,9 +1136,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.31.0.tgz", - "integrity": "sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", + "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", "cpu": [ "x64" ], @@ -1150,9 +1150,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.31.0.tgz", - "integrity": "sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", + "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", "cpu": [ "arm" ], @@ -1164,9 +1164,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.31.0.tgz", - "integrity": "sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", + "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", "cpu": [ "arm" ], @@ -1178,9 +1178,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.31.0.tgz", - "integrity": "sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", + "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", "cpu": [ "arm64" ], @@ -1192,9 +1192,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.31.0.tgz", - "integrity": "sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", + "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", "cpu": [ "arm64" ], @@ -1206,9 +1206,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.31.0.tgz", - "integrity": "sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", + "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", "cpu": [ "loong64" ], @@ -1220,9 +1220,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.31.0.tgz", - "integrity": "sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", + "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", "cpu": [ "ppc64" ], @@ -1234,9 +1234,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.31.0.tgz", - "integrity": "sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", + "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", "cpu": [ "riscv64" ], @@ -1248,9 +1248,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.31.0.tgz", - "integrity": "sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", + "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", "cpu": [ "s390x" ], @@ -1262,9 +1262,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.31.0.tgz", - "integrity": "sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", + "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", "cpu": [ "x64" ], @@ -1276,9 +1276,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.31.0.tgz", - "integrity": "sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", + "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", "cpu": [ "x64" ], @@ -1290,9 +1290,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.31.0.tgz", - "integrity": "sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", + "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", "cpu": [ "arm64" ], @@ -1304,9 +1304,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.31.0.tgz", - "integrity": "sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", + "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", "cpu": [ "ia32" ], @@ -1318,9 +1318,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.31.0.tgz", - "integrity": "sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", + "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", "cpu": [ "x64" ], @@ -1564,9 +1564,9 @@ } }, "node_modules/@swc/core": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.8.tgz", - "integrity": "sha512-I3G+n9qbHNu6KNraaAG1+Z1S1x5S7MGRA6OEppT8Pt3Z9uD5a/kYAGU33eXy7zY+BoKuKA2X1H0r4vSimAgU8w==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.18.tgz", + "integrity": "sha512-IUWKD6uQYGRy8w2X9EZrtYg1O3SCijlHbCXzMaHQYc1X7yjijQh4H3IVL9ssZZyVp2ZDfQZu4bD5DWxxvpyjvg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -1582,16 +1582,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.10.8", - "@swc/core-darwin-x64": "1.10.8", - "@swc/core-linux-arm-gnueabihf": "1.10.8", - "@swc/core-linux-arm64-gnu": "1.10.8", - "@swc/core-linux-arm64-musl": "1.10.8", - "@swc/core-linux-x64-gnu": "1.10.8", - "@swc/core-linux-x64-musl": "1.10.8", - "@swc/core-win32-arm64-msvc": "1.10.8", - "@swc/core-win32-ia32-msvc": "1.10.8", - "@swc/core-win32-x64-msvc": "1.10.8" + "@swc/core-darwin-arm64": "1.10.18", + "@swc/core-darwin-x64": "1.10.18", + "@swc/core-linux-arm-gnueabihf": "1.10.18", + "@swc/core-linux-arm64-gnu": "1.10.18", + "@swc/core-linux-arm64-musl": "1.10.18", + "@swc/core-linux-x64-gnu": "1.10.18", + "@swc/core-linux-x64-musl": "1.10.18", + "@swc/core-win32-arm64-msvc": "1.10.18", + "@swc/core-win32-ia32-msvc": "1.10.18", + "@swc/core-win32-x64-msvc": "1.10.18" }, "peerDependencies": { "@swc/helpers": "*" @@ -1603,9 +1603,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.8.tgz", - "integrity": "sha512-FtacTu9zS5YuepujQqujveNw8BQ8ESJ+pN1Z7C+WrKCHlCl+5dh0n6gMAlEj+3iRvY6UAYqkzTVeiX/bOMoJKA==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.18.tgz", + "integrity": "sha512-FdGqzAIKVQJu8ROlnHElP59XAUsUzCFSNsou+tY/9ba+lhu8R9v0OI5wXiPErrKGZpQFMmx/BPqqhx3X4SuGNg==", "cpu": [ "arm64" ], @@ -1620,9 +1620,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.8.tgz", - "integrity": "sha512-nfk+iq7EKQwADaCERzZLSi9ovzjJcqDWaO4e2ztyCNaLFi6fP1m6+ij21aki5KAd8AXoY4fue4Mo2fuYbesX9Q==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.18.tgz", + "integrity": "sha512-RZ73gZRituL/ZVLgrW6BYnQ5g8tuStG4cLUiPGJsUZpUm0ullSH6lHFvZTCBNFTfpQChG6eEhi2IdG6DwFp1lw==", "cpu": [ "x64" ], @@ -1637,9 +1637,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.8.tgz", - "integrity": "sha512-CL2zfbnrEc6nIiWbgshOz0mjn/zY8JcYqO12vGcTxmZOrh0n+mmHN2ejX91pYWQnQDtbhCmFTaEndExFpA7Gww==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.18.tgz", + "integrity": "sha512-8iJqI3EkxJuuq21UHoen1VS+QlS23RvynRuk95K+Q2HBjygetztCGGEc+Xelx9a0uPkDaaAtFvds4JMDqb9SAA==", "cpu": [ "arm" ], @@ -1654,9 +1654,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.8.tgz", - "integrity": "sha512-quS8F18DDScW3B7qnbWkz95abZ5p0xp/W8N498NAAls/YQj4jQIlf8WlAWoxVVjY/SmSus5kN5tuwhHD8t0NPw==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.18.tgz", + "integrity": "sha512-8f1kSktWzMB6PG+r8lOlCfXz5E8Qhsmfwonn77T/OfjvGwQaWrcoASh2cdjpk3dydbf8jsKGPQE1lSc7GyjXRQ==", "cpu": [ "arm64" ], @@ -1671,9 +1671,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.8.tgz", - "integrity": "sha512-wI0Hny8fHbBK/OjJ7eFYP0uDKiCMMMr5OBWGKMRRUvWs2zlGeJQZbwUeCnWuLLXzDfL+feMfh5TieYlqKTTtRw==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.18.tgz", + "integrity": "sha512-4rv+E4VLdgQw6zjbTAauCAEExxChvxMpBUMCiZweTNPKbJJ2dY6BX2WGJ1ea8+RcgqR/Xysj3AFbOz1LBz6dGA==", "cpu": [ "arm64" ], @@ -1688,9 +1688,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.8.tgz", - "integrity": "sha512-24FCRUFO8gzPP2eu3soHTm3lk+ktcsIhdM2DTOlXGA+2TBYFWgAZX/yZV+eeRrtIZYSr4OcOWsNWnQ5Ma4budA==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.18.tgz", + "integrity": "sha512-vTNmyRBVP+sZca+vtwygYPGTNudTU6Gl6XhaZZ7cEUTBr8xvSTgEmYXoK/2uzyXpaTUI4Bmtp1x81cGN0mMoLQ==", "cpu": [ "x64" ], @@ -1705,9 +1705,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.8.tgz", - "integrity": "sha512-mBo7M/FmUhoWpUG17MLbS98iRA7t6ThxQBWDJZd322whkN1GqrvumYm2wvvjmoMTeDOPwAL3hIIa5H+Q4vb1zA==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.18.tgz", + "integrity": "sha512-1TZPReKhFCeX776XaT6wegknfg+g3zODve+r4oslFHI+g7cInfWlxoGNDS3niPKyuafgCdOjme2g3OF+zzxfsQ==", "cpu": [ "x64" ], @@ -1722,9 +1722,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.8.tgz", - "integrity": "sha512-rXJ9y77JZZXoZkgFR0mObKa3TethRBJ6Exs/pwhScl9pz4qsfxhj/bQbEu1g1i/ihmd0l+IKZwGSC7Ibh3HA2Q==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.18.tgz", + "integrity": "sha512-o/2CsaWSN3bkzVQ6DA+BiFKSVEYvhWGA1h+wnL2zWmIDs2Knag54sOEXZkCaf8YQyZesGeXJtPEy9hh/vjJgkA==", "cpu": [ "arm64" ], @@ -1739,9 +1739,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.8.tgz", - "integrity": "sha512-n6ekYFJEBPvTpRIqJiu6EHXVzVnuCtDTpFnn/0KVGJI1yQHriGVEovnb/+qyLh8Rwx2AZM9qgZVgMhVtfcFQJg==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.18.tgz", + "integrity": "sha512-eTPASeJtk4mJDfWiYEiOC6OYUi/N7meHbNHcU8e+aKABonhXrIo/FmnTE8vsUtC6+jakT1TQBdiQ8fzJ1kJVwA==", "cpu": [ "ia32" ], @@ -1756,9 +1756,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.8.tgz", - "integrity": "sha512-vplXxtH/lFc/epELnAyvdCvqlDJrM+OKtkphYcbPqq50g/dEZYZ8FYHU5Df9Uo19UooWSo1LaxPk4R7n6i1Axw==", + "version": "1.10.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.18.tgz", + "integrity": "sha512-1Dud8CDBnc34wkBOboFBQud9YlV1bcIQtKSg7zC8LtwR3h+XAaCayZPkpGmmAlCv1DLQPvkF+s0JcaVC9mfffQ==", "cpu": [ "x64" ], @@ -1797,9 +1797,9 @@ "license": "MIT" }, "node_modules/@types/geojson": { - "version": "7946.0.15", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz", - "integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==", + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", "dev": true, "license": "MIT" }, @@ -1835,9 +1835,9 @@ } }, "node_modules/@types/react": { - "version": "19.0.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz", - "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", "dev": true, "license": "MIT", "dependencies": { @@ -1845,9 +1845,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", - "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", + "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1865,21 +1865,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", - "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", + "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/type-utils": "8.20.0", - "@typescript-eslint/utils": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/type-utils": "8.24.1", + "@typescript-eslint/utils": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1895,16 +1895,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", - "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz", + "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4" }, "engines": { @@ -1920,14 +1920,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", - "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz", + "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0" + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1938,16 +1938,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", - "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz", + "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/utils": "8.24.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1962,9 +1962,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", - "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz", + "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==", "dev": true, "license": "MIT", "engines": { @@ -1976,20 +1976,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", - "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz", + "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2029,9 +2029,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -2042,16 +2042,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", - "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz", + "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0" + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2066,13 +2066,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", - "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz", + "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/types": "8.24.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2084,26 +2084,23 @@ } }, "node_modules/@vitejs/plugin-react-swc": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.7.2.tgz", - "integrity": "sha512-y0byko2b2tSVVf5Gpng1eEhX1OvPC7x8yns1Fx8jDzlJp4LS6CMkCPfLw47cjyoMrshQDoQw4qcgjsU9VvlCew==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.8.0.tgz", + "integrity": "sha512-T4sHPvS+DIqDP51ifPqa9XIRAz/kIvIi8oXcnOZZgHmMotgmmdxe/DD5tMFlt5nuIRzT0/QuiwmKlH0503Aapw==", "dev": true, "license": "MIT", "dependencies": { - "@swc/core": "^1.7.26" + "@swc/core": "^1.10.15" }, "peerDependencies": { "vite": "^4 || ^5 || ^6" } }, "node_modules/@wailsio/runtime": { - "version": "3.0.0-alpha.36", - "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.36.tgz", - "integrity": "sha512-IPxzYLxgX8tOWcB1x2RHzx3VwRFTLAUrdeMQL2wZyaV7Xvtybt1h1WYaEp0iZiiNB/KCuCKIrnhnrN5sNDoDYg==", - "license": "MIT", - "dependencies": { - "nanoid": "^5.0.7" - } + "version": "3.0.0-alpha.56", + "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.56.tgz", + "integrity": "sha512-IXumYWGPscpPPb9R8bVU/elfEEY6hVQu4tpmjc3hR3tKp3D7mEp0C3ccesdB2jkljU+RyutVRn1L+mTrKCg2BA==", + "license": "MIT" }, "node_modules/acorn": { "version": "8.14.0", @@ -2287,6 +2284,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2393,9 +2400,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2447,9 +2454,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001695", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", - "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", + "version": "1.0.30001700", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", + "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", "dev": true, "funding": [ { @@ -2790,9 +2797,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.83", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz", - "integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==", + "version": "1.5.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.103.tgz", + "integrity": "sha512-P6+XzIkfndgsrjROJWfSvVEgNHtPgbhVyTkwLjUM2HU/h7pZRORgaTlHqfAikqxKmdJMLW8fftrdGWbd/Ds0FA==", "dev": true, "license": "ISC" }, @@ -2935,13 +2942,16 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -3027,22 +3037,22 @@ } }, "node_modules/eslint": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", + "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", - "@eslint/plugin-kit": "^0.2.5", + "@eslint/config-array": "^0.19.2", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.0", + "@eslint/js": "9.21.0", + "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", @@ -3194,9 +3204,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.18.tgz", - "integrity": "sha512-IRGEoFn3OKalm3hjfolEWGqoF/jPqeEYFp+C8B0WMzwGwBMvlRDQd06kghDhF0C61uJ6WfSDhEZE/sAQjduKgw==", + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", + "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3356,9 +3366,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "dev": true, "license": "ISC", "dependencies": { @@ -3423,20 +3433,26 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/fsevents": { @@ -3506,18 +3522,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -3576,9 +3592,9 @@ } }, "node_modules/globals": { - "version": "15.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", - "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, "license": "MIT", "engines": { @@ -3750,9 +3766,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3817,12 +3833,13 @@ "license": "MIT" }, "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -3852,13 +3869,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -4143,13 +4160,13 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -4430,9 +4447,10 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.9.tgz", - "integrity": "sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, "funding": [ { "type": "github", @@ -4441,10 +4459,10 @@ ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.js" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^18 || >=20" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/natural-compare": { @@ -4473,9 +4491,9 @@ "license": "MIT" }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -4727,9 +4745,9 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", "engines": { @@ -4737,9 +4755,9 @@ } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -4765,25 +4783,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4953,9 +4952,9 @@ } }, "node_modules/rollup": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.31.0.tgz", - "integrity": "sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==", + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", + "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4969,25 +4968,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.31.0", - "@rollup/rollup-android-arm64": "4.31.0", - "@rollup/rollup-darwin-arm64": "4.31.0", - "@rollup/rollup-darwin-x64": "4.31.0", - "@rollup/rollup-freebsd-arm64": "4.31.0", - "@rollup/rollup-freebsd-x64": "4.31.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.31.0", - "@rollup/rollup-linux-arm-musleabihf": "4.31.0", - "@rollup/rollup-linux-arm64-gnu": "4.31.0", - "@rollup/rollup-linux-arm64-musl": "4.31.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.31.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.31.0", - "@rollup/rollup-linux-riscv64-gnu": "4.31.0", - "@rollup/rollup-linux-s390x-gnu": "4.31.0", - "@rollup/rollup-linux-x64-gnu": "4.31.0", - "@rollup/rollup-linux-x64-musl": "4.31.0", - "@rollup/rollup-win32-arm64-msvc": "4.31.0", - "@rollup/rollup-win32-ia32-msvc": "4.31.0", - "@rollup/rollup-win32-x64-msvc": "4.31.0", + "@rollup/rollup-android-arm-eabi": "4.34.8", + "@rollup/rollup-android-arm64": "4.34.8", + "@rollup/rollup-darwin-arm64": "4.34.8", + "@rollup/rollup-darwin-x64": "4.34.8", + "@rollup/rollup-freebsd-arm64": "4.34.8", + "@rollup/rollup-freebsd-x64": "4.34.8", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", + "@rollup/rollup-linux-arm-musleabihf": "4.34.8", + "@rollup/rollup-linux-arm64-gnu": "4.34.8", + "@rollup/rollup-linux-arm64-musl": "4.34.8", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", + "@rollup/rollup-linux-riscv64-gnu": "4.34.8", + "@rollup/rollup-linux-s390x-gnu": "4.34.8", + "@rollup/rollup-linux-x64-gnu": "4.34.8", + "@rollup/rollup-linux-x64-musl": "4.34.8", + "@rollup/rollup-win32-arm64-msvc": "4.34.8", + "@rollup/rollup-win32-ia32-msvc": "4.34.8", + "@rollup/rollup-win32-x64-msvc": "4.34.8", "fsevents": "~2.3.2" } }, @@ -5384,9 +5383,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, "license": "MIT", "engines": { @@ -5522,15 +5521,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.20.0.tgz", - "integrity": "sha512-Kxz2QRFsgbWj6Xcftlw3Dd154b3cEPFqQC+qMZrMypSijPd4UanKKvoKDrJ4o8AIfZFKAF+7sMaEIR8mTElozA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.24.1.tgz", + "integrity": "sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.20.0", - "@typescript-eslint/parser": "8.20.0", - "@typescript-eslint/utils": "8.20.0" + "@typescript-eslint/eslint-plugin": "8.24.1", + "@typescript-eslint/parser": "8.24.1", + "@typescript-eslint/utils": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5611,15 +5610,15 @@ } }, "node_modules/vite": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.9.tgz", - "integrity": "sha512-MSgUxHcaXLtnBPktkbUSoQUANApKYuxZ6DrbVENlIorbhL2dZydTLaZ01tjUoE3szeFzlFk9ANOKk0xurh4MKA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.1.tgz", + "integrity": "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.24.2", - "postcss": "^8.4.49", - "rollup": "^4.23.0" + "postcss": "^8.5.2", + "rollup": "^4.30.1" }, "bin": { "vite": "bin/vite.js" From 4885dab2e1c07c4b7a73eac094d4aae318cd84a7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 18:04:08 +0200 Subject: [PATCH 44/78] web/modal: remove debug print --- web/src/ui/modal/Modal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/ui/modal/Modal.tsx b/web/src/ui/modal/Modal.tsx index 9574e2e..620d8bf 100644 --- a/web/src/ui/modal/Modal.tsx +++ b/web/src/ui/modal/Modal.tsx @@ -32,7 +32,6 @@ const ModalWrapper = ({ children, ContextType, historyStateKey }: ModalWrapperPr if (evt && evt.target !== evt.currentTarget) { return } - console.log("MEOW", evt) evt?.stopPropagation() setState(null) if (history.state?.[historyStateKey]) { From a14f01a3ec228be1119d3337b185b7bbdd92ef8e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 18:33:16 +0200 Subject: [PATCH 45/78] web/timeline: implement MSC2815 Fixes #510 --- desktop/go.mod | 2 +- desktop/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- pkg/hicli/json-commands.go | 8 ++++++-- pkg/hicli/paginate.go | 20 +++++++++++++++++++ web/src/api/client.ts | 20 ++++++++++++++----- web/src/api/rpc.ts | 4 ++-- web/src/api/statestore/room.ts | 11 +++++++++- web/src/api/types/hitypes.ts | 1 + web/src/icons/restore-trash.svg | 1 + web/src/ui/timeline/TimelineEvent.tsx | 5 +++-- web/src/ui/timeline/content/index.ts | 7 ++++--- web/src/ui/timeline/menu/usePrimaryItems.tsx | 2 +- .../ui/timeline/menu/useSecondaryItems.tsx | 20 +++++++++++++++++++ 15 files changed, 89 insertions(+), 22 deletions(-) create mode 100644 web/src/icons/restore-trash.svg diff --git a/desktop/go.mod b/desktop/go.mod index 49a6812..402586b 100644 --- a/desktop/go.mod +++ b/desktop/go.mod @@ -79,7 +79,7 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - maunium.net/go/mautrix v0.23.1 // indirect + maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca // indirect mvdan.cc/xurls/v2 v2.6.0 // indirect ) diff --git a/desktop/go.sum b/desktop/go.sum index 0aac851..09a6b4f 100644 --- a/desktop/go.sum +++ b/desktop/go.sum @@ -261,7 +261,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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.23.1 h1:xZtX43YZF3WRxkdR+oMUrpiQe+jbjc+LeXLxHuXP5IM= -maunium.net/go/mautrix v0.23.1/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= +maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca h1:xPbRPallD4qh/XuQWheRsvxsf/5stfdA+uIj0S0P2kQ= +maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= diff --git a/go.mod b/go.mod index 7a3b7ea..3e19779 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( golang.org/x/text v0.22.0 gopkg.in/yaml.v3 v3.0.1 maunium.net/go/mauflag v1.0.0 - maunium.net/go/mautrix v0.23.1 + maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca mvdan.cc/xurls/v2 v2.6.0 ) diff --git a/go.sum b/go.sum index 9fb04cf..8ba720c 100644 --- a/go.sum +++ b/go.sum @@ -100,7 +100,7 @@ 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/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.23.1 h1:xZtX43YZF3WRxkdR+oMUrpiQe+jbjc+LeXLxHuXP5IM= -maunium.net/go/mautrix v0.23.1/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= +maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca h1:xPbRPallD4qh/XuQWheRsvxsf/5stfdA+uIj0S0P2kQ= +maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= diff --git a/pkg/hicli/json-commands.go b/pkg/hicli/json-commands.go index 3b29725..ba5b551 100644 --- a/pkg/hicli/json-commands.go +++ b/pkg/hicli/json-commands.go @@ -123,6 +123,9 @@ func (h *HiClient) handleJSONCommand(ctx context.Context, req *JSONCommand) (any }) case "get_event": return unmarshalAndCall(req.Data, func(params *getEventParams) (*database.Event, error) { + if params.Unredact { + return h.GetUnredactedEvent(ctx, params.RoomID, params.EventID) + } return h.GetEvent(ctx, params.RoomID, params.EventID) }) case "get_related_events": @@ -311,8 +314,9 @@ type setProfileFieldParams struct { } type getEventParams struct { - RoomID id.RoomID `json:"room_id"` - EventID id.EventID `json:"event_id"` + RoomID id.RoomID `json:"room_id"` + EventID id.EventID `json:"event_id"` + Unredact bool `json:"unredact"` } type getRelatedEventsParams struct { diff --git a/pkg/hicli/paginate.go b/pkg/hicli/paginate.go index 424635d..4cbf64c 100644 --- a/pkg/hicli/paginate.go +++ b/pkg/hicli/paginate.go @@ -35,6 +35,26 @@ func (h *HiClient) GetEvent(ctx context.Context, roomID id.RoomID, eventID id.Ev } } +func (h *HiClient) GetUnredactedEvent(ctx context.Context, roomID id.RoomID, eventID id.EventID) (*database.Event, error) { + if evt, err := h.DB.Event.GetByID(ctx, eventID); err != nil { + return nil, fmt.Errorf("failed to get event from database: %w", err) + // TODO this check doesn't handle events which keep some fields on redaction + } else if evt != nil && len(evt.Content) > 2 { + h.ReprocessExistingEvent(ctx, evt) + return evt, nil + } else if serverEvt, err := h.Client.GetUnredactedEventContent(ctx, roomID, eventID); err != nil { + return nil, fmt.Errorf("failed to get event from server: %w", err) + } else if redactedServerEvt, err := h.Client.GetEvent(ctx, roomID, eventID); err != nil { + return nil, fmt.Errorf("failed to get redacted event from server: %w", err) + // TODO this check will have false positives on actually empty events + } else if len(serverEvt.Content.VeryRaw) == 2 { + return nil, fmt.Errorf("server didn't return content") + } else { + serverEvt.Unsigned.RedactedBecause = redactedServerEvt.Unsigned.RedactedBecause + return h.processEvent(ctx, serverEvt, nil, nil, false) + } +} + func (h *HiClient) processGetRoomState(ctx context.Context, roomID id.RoomID, fetchMembers, refetch, dispatchEvt bool) error { var evts []*event.Event if refetch { diff --git a/web/src/api/client.ts b/web/src/api/client.ts index 331ed47..600ec1a 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -222,17 +222,27 @@ export default class Client { }) } - requestEvent(room: RoomStateStore | RoomID | undefined, eventID: EventID) { + requestEvent(room: RoomStateStore | RoomID | undefined, eventID: EventID, unredact?: boolean) { if (typeof room === "string") { room = this.store.rooms.get(room) } - if (!room || room.eventsByID.has(eventID) || room.requestedEvents.has(eventID)) { + if (!room || (!unredact && room.eventsByID.has(eventID)) ||room.requestedEvents.has(eventID)) { return } room.requestedEvents.add(eventID) - this.rpc.getEvent(room.roomID, eventID).then( - evt => room.applyEvent(evt), - err => console.error(`Failed to fetch event ${eventID}`, err), + this.rpc.getEvent(room.roomID, eventID, unredact).then( + evt => { + room.applyEvent(evt, false, unredact) + if (unredact) { + room.notifyTimelineSubscribers() + } + }, + err => { + console.error(`Failed to fetch event ${eventID}`, err) + if (unredact) { + window.alert(`Failed to get unredacted content: ${err}`) + } + }, ) } diff --git a/web/src/api/rpc.ts b/web/src/api/rpc.ts index e79c820..3cc7236 100644 --- a/web/src/api/rpc.ts +++ b/web/src/api/rpc.ts @@ -218,8 +218,8 @@ export default abstract class RPCClient { return this.request("get_room_state", { room_id, include_members, fetch_members, refetch }) } - getEvent(room_id: RoomID, event_id: EventID): Promise { - return this.request("get_event", { room_id, event_id }) + getEvent(room_id: RoomID, event_id: EventID, unredact?: boolean): Promise { + return this.request("get_event", { room_id, event_id, unredact }) } getRelatedEvents(room_id: RoomID, event_id: EventID, relation_type?: RelationType): Promise { diff --git a/web/src/api/statestore/room.ts b/web/src/api/statestore/room.ts index 497d6e3..a1334f9 100644 --- a/web/src/api/statestore/room.ts +++ b/web/src/api/statestore/room.ts @@ -352,13 +352,22 @@ export class RoomStateStore { return this.applyEvent(evt) } - applyEvent(evt: RawDBEvent, pending: boolean = false) { + setViewingRedacted(evt: MemDBEvent, view: boolean) { + evt.viewing_redacted = view + this.eventSubs.notify(evt.event_id) + this.notifyTimelineSubscribers() + } + + applyEvent(evt: RawDBEvent, pending: boolean = false, viewRedacted: boolean = false) { const memEvt = evt as MemDBEvent memEvt.mem = true memEvt.pending = pending if (pending) { memEvt.timeline_rowid = UNSENT_TIMELINE_ROWID_BASE + memEvt.timestamp } + if (viewRedacted) { + memEvt.viewing_redacted = true + } if (evt.type === "m.room.encrypted" && evt.decrypted && evt.decrypted_type) { memEvt.type = evt.decrypted_type memEvt.encrypted = evt.content as EncryptedEventContent diff --git a/web/src/api/types/hitypes.ts b/web/src/api/types/hitypes.ts index bb352d4..688ecc4 100644 --- a/web/src/api/types/hitypes.ts +++ b/web/src/api/types/hitypes.ts @@ -158,6 +158,7 @@ export interface MemDBEvent extends BaseDBEvent { orig_content?: UnknownEventContent orig_local_content?: LocalContent last_edit?: MemDBEvent + viewing_redacted?: boolean } export interface DBAccountData { diff --git a/web/src/icons/restore-trash.svg b/web/src/icons/restore-trash.svg new file mode 100644 index 0000000..80812ca --- /dev/null +++ b/web/src/icons/restore-trash.svg @@ -0,0 +1 @@ + diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index 72eaafa..5ee6628 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -138,7 +138,8 @@ const TimelineEvent = ({ if (evt.unread_type & UnreadType.Highlight) { wrapperClassNames.push("highlight") } - if (evt.redacted_by) { + const isRedacted = evt.redacted_by && !evt.viewing_redacted + if (isRedacted) { wrapperClassNames.push("redacted-event") } if (evt.type === "m.room.member") { @@ -173,7 +174,7 @@ const TimelineEvent = ({ const replyTo = relatesTo?.["m.in_reply_to"]?.event_id let replyAboveMessage: JSX.Element | null = null let replyInMessage: JSX.Element | null = null - if (isEventID(replyTo) && BodyType !== HiddenEvent && !evt.redacted_by && !editHistoryView) { + if (isEventID(replyTo) && BodyType !== HiddenEvent && !isRedacted && !editHistoryView) { const replyElem = window.alert(`Failed to resend message: ${err}`)) } const onClickMore = (mevt: React.MouseEvent) => { - const moreMenuHeight = 4 * 40 + const moreMenuHeight = 5 * 40 setForceOpen!(true) openModal({ content: , }) } + const onClickHideUnredacted = () => { + closeModal() + roomCtx.store.setViewingRedacted(evt, false) + } + const onClickUnredact = () => { + closeModal() + if (Object.entries(evt.content).length > 0) { + roomCtx.store.setViewingRedacted(evt, true) + } else { + client.requestEvent(roomCtx.store, evt.event_id, true) + } + } const onClickPin = (pin: boolean) => () => { closeModal() client.pinMessage(roomCtx.store, evt.event_id, pin) @@ -146,6 +159,8 @@ export const useSecondaryItems = ( const canRedact = !evt.redacted_by && ownPL >= redactEvtPL && (evt.sender === client.userID || ownPL >= redactOtherPL) + // TODO check server admin status and room PLs + const canUnredact = Boolean(evt.redacted_by) return <> @@ -166,5 +181,10 @@ export const useSecondaryItems = ( title={pendingTitle} className="redact-button" >{names && "Remove"}} + {canUnredact && (evt.viewing_redacted ? : )} } From bd2ece9ff2cb3e22a154cc3830087990f73bf132 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 18:44:59 +0200 Subject: [PATCH 46/78] hicli/sync: fix unredacting content of encrypted events --- pkg/hicli/sync.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hicli/sync.go b/pkg/hicli/sync.go index 1b5eaa1..464ba0f 100644 --- a/pkg/hicli/sync.go +++ b/pkg/hicli/sync.go @@ -621,7 +621,7 @@ func (h *HiClient) processEvent( } var decryptionErr error var decryptedMautrixEvt *event.Event - if evt.Type == event.EventEncrypted && dbEvt.RedactedBy == "" { + if evt.Type == event.EventEncrypted && (dbEvt.RedactedBy == "" || len(dbEvt.Content) > 2) { decryptedMautrixEvt, decryptionErr = h.decryptEventInto(ctx, evt, dbEvt) } else if evt.Type == event.EventRedaction { if evt.Redacts != "" && gjson.GetBytes(evt.Content.VeryRaw, "redacts").Str != evt.Redacts.String() { From 0c3d3686e42af140ab90c06e7c0cc08746dc16e8 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 21:06:53 +0200 Subject: [PATCH 47/78] hicli/sync: fill prev_content with cached event if it's missing --- pkg/hicli/sync.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/hicli/sync.go b/pkg/hicli/sync.go index 464ba0f..3821776 100644 --- a/pkg/hicli/sync.go +++ b/pkg/hicli/sync.go @@ -613,6 +613,14 @@ func (h *HiClient) processEvent( return dbEvt, nil } } + if evt.StateKey != nil && evt.Unsigned.PrevContent == nil && evt.Unsigned.ReplacesState != "" { + replacesState, err := h.DB.Event.GetByID(ctx, evt.Unsigned.ReplacesState) + if err != nil { + return nil, fmt.Errorf("failed to get prev content for %s from %s: %w", evt.ID, evt.Unsigned.ReplacesState, err) + } else if replacesState != nil { + evt.Unsigned.PrevContent = &event.Content{VeryRaw: replacesState.Content} + } + } dbEvt := database.MautrixToEvent(evt) contentWithoutFallback := removeReplyFallback(evt) if contentWithoutFallback != nil { From c210f696cc05c7931b023286f200aed003ab5100 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 21:12:19 +0200 Subject: [PATCH 48/78] web/timeline: fix server ACL rendering --- web/src/ui/timeline/content/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/ui/timeline/content/index.ts b/web/src/ui/timeline/content/index.ts index 9198ac1..c3931e9 100644 --- a/web/src/ui/timeline/content/index.ts +++ b/web/src/ui/timeline/content/index.ts @@ -45,6 +45,8 @@ export function getBodyType(evt: MemDBEvent, forReply = false): React.FunctionCo return RoomNameBody case "m.room.avatar": return RoomAvatarBody + case "m.room.server_acl": + return ACLBody case "m.room.pinned_events": return PinnedEventsBody case "m.room.power_levels": @@ -55,8 +57,6 @@ export function getBodyType(evt: MemDBEvent, forReply = false): React.FunctionCo switch (evt.type) { case "m.room.member": return MemberBody - case "m.room.server_acl": - return ACLBody case "m.policy.rule.user": return PolicyRuleBody case "m.policy.rule.room": From 4a728e187c959a9c1733adcc10a0381f211d5b51 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 21:30:36 +0200 Subject: [PATCH 49/78] web/client: allow retrying unredacting --- web/src/api/client.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/api/client.ts b/web/src/api/client.ts index 600ec1a..1c7f5d0 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -240,6 +240,7 @@ export default class Client { err => { console.error(`Failed to fetch event ${eventID}`, err) if (unredact) { + room.requestedEvents.delete(eventID) window.alert(`Failed to get unredacted content: ${err}`) } }, From 7ed0f2633c2b0b027aeb975b9f1a473964b8faa4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 23:21:01 +0200 Subject: [PATCH 50/78] web/modal: make edit history modal fullscreen on mobile --- web/src/ui/modal/Lightbox.css | 10 ++++++++++ web/src/ui/timeline/EventEditHistory.css | 6 ++++++ web/src/ui/timeline/TimelineEvent.tsx | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/web/src/ui/modal/Lightbox.css b/web/src/ui/modal/Lightbox.css index 9d65ab7..b629b60 100644 --- a/web/src/ui/modal/Lightbox.css +++ b/web/src/ui/modal/Lightbox.css @@ -19,6 +19,16 @@ div.overlay { overflow: hidden; display: flex; + &.full-screen-mobile { + @media screen and (max-width: 30rem) { + max-width: 100%; + max-height: 100%; + width: 100%; + height: 100%; + border-radius: 0; + } + } + > div.modal-box-inner { overflow: scroll; } diff --git a/web/src/ui/timeline/EventEditHistory.css b/web/src/ui/timeline/EventEditHistory.css index 171322a..7396cbe 100644 --- a/web/src/ui/timeline/EventEditHistory.css +++ b/web/src/ui/timeline/EventEditHistory.css @@ -6,8 +6,14 @@ div.event-edit-history-modal { --timeline-status-size: 0; --timeline-horizontal-padding: 0; min-width: 20rem; + box-sizing: border-box; padding: 1rem; + @media screen and (max-width: 30rem) { + min-width: 100%; + padding: .5rem; + } + > p { margin: 0; } diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index 5ee6628..1ade3e5 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -125,7 +125,7 @@ const TimelineEvent = ({ content: , dimmed: true, boxed: true, - boxClass: "event-edit-history-wrapper", + boxClass: "full-screen-mobile event-edit-history-wrapper", innerBoxClass: "event-edit-history-modal", }) } From f9a8d3e042d19e67a3ab71094e9611e85e567475 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 23 Feb 2025 23:35:53 +0200 Subject: [PATCH 51/78] web/timeline: use profile from prev_content for left users --- web/src/ui/timeline/TimelineEvent.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index 1ade3e5..831517f 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -17,7 +17,7 @@ import React, { JSX, use, useState } from "react" import { createPortal } from "react-dom" import { getAvatarThumbnailURL, getMediaURL, getUserColorIndex } from "@/api/media.ts" import { useRoomMember } from "@/api/statestore" -import { MemDBEvent, MemberEventContent, UnreadType } from "@/api/types" +import { MemDBEvent, UnreadType, UserProfile } from "@/api/types" import { isMobileDevice } from "@/util/ismobile.ts" import { getDisplayname, isEventID } from "@/util/validation.ts" import ClientContext from "../ClientContext.ts" @@ -129,8 +129,6 @@ const TimelineEvent = ({ innerBoxClass: "event-edit-history-modal", }) } - const memberEvt = useRoomMember(client, roomCtx.store, evt.sender) - const memberEvtContent = memberEvt?.content as MemberEventContent | undefined const BodyType = getBodyType(evt) const eventTS = new Date(evt.timestamp) const editEventTS = evt.last_edit ? new Date(evt.last_edit.timestamp) : null @@ -190,10 +188,21 @@ const TimelineEvent = ({ } const perMessageSender = getPerMessageProfile(evt) const prevPerMessageSender = getPerMessageProfile(prevEvt) + const memberEvt = useRoomMember(client, roomCtx.store, evt.sender) + let memberEvtContent = memberEvt?.content as UserProfile | undefined + if (memberEvt?.redacted_by && !memberEvt?.viewing_redacted) { + memberEvtContent = {} + } else if ( + memberEvtContent?.displayname === undefined + && memberEvtContent?.avatar_url === undefined + && memberEvt?.content.membership === "leave" + && memberEvt.unsigned.prev_content + ) { + memberEvtContent = memberEvt.unsigned.prev_content as UserProfile | undefined + } let renderMemberEvtContent = memberEvtContent if (perMessageSender) { renderMemberEvtContent = { - membership: "join", displayname: perMessageSender.displayname ?? memberEvtContent?.displayname, avatar_url: perMessageSender.avatar_url ?? memberEvtContent?.avatar_url, avatar_file: perMessageSender.avatar_file ?? memberEvtContent?.avatar_file, From 548c8a9a947dbb20ea88776f68ae63fb080be3e6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 24 Feb 2025 00:00:45 +0200 Subject: [PATCH 52/78] web/timeline: remove unnecessary variable --- web/src/ui/timeline/TimelineEvent.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index 831517f..b78cc06 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -233,7 +233,6 @@ const TimelineEvent = ({ } const fullTime = fullTimeFormatter.format(eventTS) const shortTime = formatShortTime(eventTS) - const editTime = editEventTS ? `Edited at ${fullTimeFormatter.format(editEventTS)}` : null const mainEvent =
{!isSmallBodyType && } - {(!editHistoryView && editEventTS && editTime) ?
(edited at {formatShortTime(editEventTS)}) From 5d41b494627ccf2b454ba1041344351a49040feb Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 24 Feb 2025 00:08:32 +0200 Subject: [PATCH 53/78] web/composer: add option to start new thread when replying Fixes #594 --- web/src/ui/composer/MessageComposer.tsx | 19 ++++++++++++++++++- web/src/ui/timeline/ReplyBody.tsx | 17 ++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/web/src/ui/composer/MessageComposer.tsx b/web/src/ui/composer/MessageComposer.tsx index 87749c6..67152d9 100644 --- a/web/src/ui/composer/MessageComposer.tsx +++ b/web/src/ui/composer/MessageComposer.tsx @@ -55,6 +55,7 @@ export interface ComposerState { replyTo: EventID | null silentReply: boolean explicitReplyInThread: boolean + startNewThread: boolean uninited?: boolean } @@ -67,6 +68,7 @@ const emptyComposer: ComposerState = { location: null, silentReply: false, explicitReplyInThread: false, + startNewThread: false, } const uninitedComposer: ComposerState = { ...emptyComposer, uninited: true } const composerReducer = ( @@ -116,7 +118,7 @@ const MessageComposer = () => { document.execCommand("insertText", false, text) }, []) roomCtx.setReplyTo = useCallback((evt: EventID | null) => { - setState({ replyTo: evt, silentReply: false, explicitReplyInThread: false }) + setState({ replyTo: evt, silentReply: false, explicitReplyInThread: false, startNewThread: false }) textInput.current?.focus() }, []) const setSilentReply = useCallback((newVal: boolean | React.MouseEvent) => { @@ -135,6 +137,14 @@ const MessageComposer = () => { setState(state => ({ explicitReplyInThread: !state.explicitReplyInThread })) } }, []) + const setStartNewThread = useCallback((newVal: boolean | React.MouseEvent) => { + if (typeof newVal === "boolean") { + setState({ startNewThread: newVal }) + } else { + newVal.stopPropagation() + setState(state => ({ startNewThread: !state.startNewThread })) + } + }, []) roomCtx.setEditing = useCallback((evt: MemDBEvent | null) => { if (evt === null) { rawSetEditing(null) @@ -160,6 +170,7 @@ const MessageComposer = () => { replyTo: null, silentReply: false, explicitReplyInThread: false, + startNewThread: false, }) textInput.current?.focus() }, [room.roomID]) @@ -204,6 +215,10 @@ const MessageComposer = () => { relates_to.rel_type = "m.thread" relates_to.event_id = replyToEvt.content?.["m.relates_to"].event_id relates_to.is_falling_back = !state.explicitReplyInThread + } else if (state.startNewThread) { + relates_to.rel_type = "m.thread" + relates_to.event_id = replyToEvt.event_id + relates_to.is_falling_back = true } } let base_content: MessageEventContent | undefined @@ -580,6 +595,8 @@ const MessageComposer = () => { onSetSilent={setSilentReply} isExplicitInThread={state.explicitReplyInThread} onSetExplicitInThread={setExplicitReplyInThread} + startNewThread={state.startNewThread} + onSetStartNewThread={setStartNewThread} />} {editing && void isExplicitInThread?: boolean onSetExplicitInThread?: (evt: React.MouseEvent) => void + startNewThread?: boolean + onSetStartNewThread?: (evt: React.MouseEvent) => void } interface ReplyIDBodyProps { @@ -83,7 +85,10 @@ const onClickReply = (evt: React.MouseEvent) => { } export const ReplyBody = ({ - room, event, onClose, isThread, isEditing, isSilent, onSetSilent, isExplicitInThread, onSetExplicitInThread, small, + room, event, onClose, isThread, isEditing, small, + isSilent, onSetSilent, + isExplicitInThread, onSetExplicitInThread, + startNewThread, onSetStartNewThread, }: ReplyBodyProps) => { const client = use(ClientContext) const memberEvt = useRoomMember(client, room, event.sender) @@ -164,6 +169,16 @@ export const ReplyBody = ({ > {isExplicitInThread ? : } } + {!isThread && onSetStartNewThread && + {startNewThread ? : } + } {onClose && }
}
From b7cc6aff86afe8e6cb866174d6bf8f518cc53614 Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Tue, 25 Feb 2025 08:33:13 -0700 Subject: [PATCH 54/78] timeline: fix scrollbar issues on URL previews (#597) * Prevents rendering an empty url-previews div * Sets overflow-x to "auto" instead of "scroll" Signed-off-by: Sumner Evans --- web/src/ui/timeline/URLPreviews.css | 2 +- web/src/ui/timeline/URLPreviews.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/ui/timeline/URLPreviews.css b/web/src/ui/timeline/URLPreviews.css index bd5b921..8b5df43 100644 --- a/web/src/ui/timeline/URLPreviews.css +++ b/web/src/ui/timeline/URLPreviews.css @@ -2,7 +2,7 @@ div.url-previews { display: flex; flex-direction: row; gap: 1rem; - overflow-x: scroll; + overflow-x: auto; > div.url-preview { margin: 0.5rem 0; diff --git a/web/src/ui/timeline/URLPreviews.tsx b/web/src/ui/timeline/URLPreviews.tsx index a68f1a3..5798ccf 100644 --- a/web/src/ui/timeline/URLPreviews.tsx +++ b/web/src/ui/timeline/URLPreviews.tsx @@ -33,7 +33,7 @@ const URLPreviews = ({ event, room }: { } const previews = (event.content["com.beeper.linkpreviews"] ?? event.content["m.url_previews"]) as URLPreview[] - if (!previews) { + if (!previews || !previews.length) { return null } return
From bdae0c416f155661f3d147427f7674656a18c5bc Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 25 Feb 2025 20:48:39 +0200 Subject: [PATCH 55/78] web/rightpanel: use border-bottom instead of hr for separators --- web/src/ui/rightpanel/RightPanel.css | 6 +++++ web/src/ui/rightpanel/UserExtendedProfile.tsx | 23 ++++++++----------- web/src/ui/rightpanel/UserInfo.tsx | 13 ++--------- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/web/src/ui/rightpanel/RightPanel.css b/web/src/ui/rightpanel/RightPanel.css index ef7138a..597f4af 100644 --- a/web/src/ui/rightpanel/RightPanel.css +++ b/web/src/ui/rightpanel/RightPanel.css @@ -91,6 +91,12 @@ div.right-panel-content.user { word-break: break-word; } + div.userid, div.extended-profile, div.devices, div.user-moderation, div.mutual-rooms, div.errors { + border-bottom: 1px solid var(--border-color); + padding-bottom: .5rem; + margin-bottom: .5rem; + } + div.extended-profile { display: grid; gap: 0.25rem; diff --git a/web/src/ui/rightpanel/UserExtendedProfile.tsx b/web/src/ui/rightpanel/UserExtendedProfile.tsx index afdea59..529f751 100644 --- a/web/src/ui/rightpanel/UserExtendedProfile.tsx +++ b/web/src/ui/rightpanel/UserExtendedProfile.tsx @@ -4,7 +4,7 @@ import { PronounSet, UserProfile } from "@/api/types" import { ensureArray, ensureString } from "@/util/validation.ts" interface ExtendedProfileProps { - profile: UserProfile + profile: UserProfile | null refreshProfile: () => void client: Client userID: string @@ -105,18 +105,15 @@ const UserExtendedProfile = ({ profile, refreshProfile, client, userID }: Extend const pronouns = ensureArray(profile["io.fsky.nyx.pronouns"]) as PronounSet[] const userTimeZone = ensureString(profile["us.cloke.msc4175.tz"]) - return <> -
-
- {userTimeZone && } - {userID === client.userID && - } - {pronouns.length > 0 && <> -
Pronouns:
-
{pronouns.map(pronounSet => ensureString(pronounSet.summary)).join(", ")}
- } -
- + return
+ {userTimeZone && } + {userID === client.userID && + } + {pronouns.length > 0 && <> +
Pronouns:
+
{pronouns.map(pronounSet => ensureString(pronounSet.summary)).join(", ")}
+ } +
} export default UserExtendedProfile diff --git a/web/src/ui/rightpanel/UserInfo.tsx b/web/src/ui/rightpanel/UserInfo.tsx index f50ef13..0b7cc37 100644 --- a/web/src/ui/rightpanel/UserInfo.tsx +++ b/web/src/ui/rightpanel/UserInfo.tsx @@ -70,22 +70,13 @@ const UserInfo = ({ userID }: UserInfoProps) => {
{displayname}
{userID}
- {globalProfile && } -
+ -
{userID !== client.userID && <> -
-
} - {errors?.length ? <> - -
- : null} + } From 2203c18e155fba1ef36ca0ecfab9bf6523df37bf Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 25 Feb 2025 21:02:46 +0200 Subject: [PATCH 56/78] web/rightpanel: add start DM button Fixes #592 --- pkg/hicli/json-commands.go | 4 + web/src/api/rpc.ts | 6 ++ web/src/api/types/mxtypes.ts | 26 ++++++ web/src/icons/chat.svg | 1 + web/src/ui/rightpanel/StartDMButton.tsx | 102 +++++++++++++++++++++ web/src/ui/rightpanel/UserIgnoreButton.tsx | 55 +++++++++++ web/src/ui/rightpanel/UserModeration.tsx | 41 ++------- 7 files changed, 200 insertions(+), 35 deletions(-) create mode 100644 web/src/icons/chat.svg create mode 100644 web/src/ui/rightpanel/StartDMButton.tsx create mode 100644 web/src/ui/rightpanel/UserIgnoreButton.tsx diff --git a/pkg/hicli/json-commands.go b/pkg/hicli/json-commands.go index ba5b551..d6fdcf6 100644 --- a/pkg/hicli/json-commands.go +++ b/pkg/hicli/json-commands.go @@ -223,6 +223,10 @@ func (h *HiClient) handleJSONCommand(ctx context.Context, req *JSONCommand) (any return unmarshalAndCall(req.Data, func(params *database.PushRegistration) (bool, error) { return true, h.DB.PushRegistration.Put(ctx, params) }) + case "create_room": + return unmarshalAndCall(req.Data, func(params *mautrix.ReqCreateRoom) (*mautrix.RespCreateRoom, error) { + return h.Client.CreateRoom(ctx, params) + }) default: return nil, fmt.Errorf("unknown command %q", req.Command) } diff --git a/web/src/api/rpc.ts b/web/src/api/rpc.ts index 3cc7236..e93b420 100644 --- a/web/src/api/rpc.ts +++ b/web/src/api/rpc.ts @@ -34,7 +34,9 @@ import type { ReceiptType, RelatesTo, RelationType, + ReqCreateRoom, ResolveAliasResponse, + RespCreateRoom, RespOpenIDToken, RespRoomJoin, RoomAlias, @@ -277,4 +279,8 @@ export default abstract class RPCClient { registerPush(reg: DBPushRegistration): Promise { return this.request("register_push", reg) } + + createRoom(request: ReqCreateRoom): Promise { + return this.request("create_room", request) + } } diff --git a/web/src/api/types/mxtypes.ts b/web/src/api/types/mxtypes.ts index 94059e5..9413e49 100644 --- a/web/src/api/types/mxtypes.ts +++ b/web/src/api/types/mxtypes.ts @@ -320,3 +320,29 @@ export interface RespOpenIDToken { matrix_server_name: string token_type: "Bearer" } + +export type RoomVisibility = "public" | "private" +export type RoomPreset = "private_chat" | "public_chat" | "trusted_private_chat" + +export interface ReqCreateRoom { + visibility?: RoomVisibility + room_alias_name?: string + name?: string + topic?: string + invite?: UserID[] + preset?: RoomPreset + is_direct?: boolean + initial_state?: { + type: EventType + state_key?: string + content: Record + }[] + room_version?: string + creation_content?: Record + power_level_content_override?: Record + "fi.mau.room_id"?: RoomID +} + +export interface RespCreateRoom { + room_id: RoomID +} diff --git a/web/src/icons/chat.svg b/web/src/icons/chat.svg new file mode 100644 index 0000000..4d4d233 --- /dev/null +++ b/web/src/icons/chat.svg @@ -0,0 +1 @@ + diff --git a/web/src/ui/rightpanel/StartDMButton.tsx b/web/src/ui/rightpanel/StartDMButton.tsx new file mode 100644 index 0000000..b00a415 --- /dev/null +++ b/web/src/ui/rightpanel/StartDMButton.tsx @@ -0,0 +1,102 @@ +// gomuks - A Matrix client written in Go. +// 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 . +import { use, useMemo, useState } from "react" +import Client from "@/api/client.ts" +import { UserID } from "@/api/types" +import MainScreenContext from "../MainScreenContext.ts" +import ChatIcon from "@/icons/chat.svg?react" + +const StartDMButton = ({ userID, client }: { userID: UserID; client: Client }) => { + const mainScreen = use(MainScreenContext)! + const [isCreating, setIsCreating] = useState(false) + + const findExistingRoom = () => { + for (const room of client.store.rooms.values()) { + if (room.meta.current.dm_user_id === userID) { + return room.roomID + } + } + } + const existingRoom = useMemo(findExistingRoom, [userID, client]) + + const startDM = async () => { + if (existingRoom) { + mainScreen.setActiveRoom(existingRoom) + return + } + if (!window.confirm(`Are you sure you want to start a chat with ${userID}?`)) { + return + } + const existingRoomRelookup = findExistingRoom() + if (existingRoomRelookup) { + mainScreen.setActiveRoom(existingRoomRelookup) + return + } + + try { + setIsCreating(true) + + let shouldEncrypt = false + const initialState = [] + + try { + shouldEncrypt = (await client.rpc.trackUserDevices(userID)).devices.length > 0 + + if (shouldEncrypt) { + console.log("User has encryption devices, creating encrypted room") + initialState.push({ + type: "m.room.encryption", + content: { + algorithm: "m.megolm.v1.aes-sha2", + }, + }) + } + } catch (err) { + console.warn("Failed to check user encryption status:", err) + } + + // Create the room with encryption if needed + const response = await client.rpc.createRoom({ + is_direct: true, + preset: "trusted_private_chat", + invite: [userID], + initial_state: initialState, + }) + console.log("Created DM room:", response.room_id) + + // FIXME this is a hacky way to work around the room taking time to come down /sync + setTimeout(() => { + mainScreen.setActiveRoom(response.room_id) + }, 1000) + } catch (err) { + console.error("Failed to create DM room:", err) + window.alert(`Failed to create DM room: ${err}`) + } finally { + setIsCreating(false) + } + } + + return +} + +export default StartDMButton diff --git a/web/src/ui/rightpanel/UserIgnoreButton.tsx b/web/src/ui/rightpanel/UserIgnoreButton.tsx new file mode 100644 index 0000000..965fcfb --- /dev/null +++ b/web/src/ui/rightpanel/UserIgnoreButton.tsx @@ -0,0 +1,55 @@ +// gomuks - A Matrix client written in Go. +// Copyright (C) 2025 Nexus Nicholson +// +// 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 . +import Client from "@/api/client.ts" +import { useAccountData } from "@/api/statestore" +import { IgnoredUsersEventContent } from "@/api/types" +import IgnoreIcon from "@/icons/block.svg?react" + +const UserIgnoreButton = ({ userID, client }: { userID: string; client: Client }) => { + const ignoredUsers = useAccountData(client.store, "m.ignored_user_list") as IgnoredUsersEventContent | null + + const isIgnored = Boolean(ignoredUsers?.ignored_users?.[userID]) + const ignoreUser = () => { + if (!window.confirm(`Are you sure you want to ignore ${userID}?`)) { + return + } + const newIgnoredUsers = { ...(ignoredUsers || { ignored_users: {}}) } + newIgnoredUsers.ignored_users[userID] = {} + client.rpc.setAccountData("m.ignored_user_list", newIgnoredUsers).catch(err => { + console.error("Failed to ignore user", err) + window.alert(`Failed to ignore ${userID}: ${err}`) + }) + } + const unignoreUser = () => { + const newIgnoredUsers = { ...(ignoredUsers || { ignored_users: {}}) } + delete newIgnoredUsers.ignored_users[userID] + client.rpc.setAccountData("m.ignored_user_list", newIgnoredUsers).catch(err => { + console.error("Failed to unignore user", err) + window.alert(`Failed to unignore ${userID}: ${err}`) + }) + } + + return ( + + ) +} + +export default UserIgnoreButton diff --git a/web/src/ui/rightpanel/UserModeration.tsx b/web/src/ui/rightpanel/UserModeration.tsx index d5133a6..c7a7d0a 100644 --- a/web/src/ui/rightpanel/UserModeration.tsx +++ b/web/src/ui/rightpanel/UserModeration.tsx @@ -15,12 +15,13 @@ // along with this program. If not, see . import { use } from "react" import Client from "@/api/client.ts" -import { RoomStateStore, useAccountData } from "@/api/statestore" -import { IgnoredUsersEventContent, MemDBEvent, MembershipAction } from "@/api/types" +import { RoomStateStore } from "@/api/statestore" +import { MemDBEvent, MembershipAction } from "@/api/types" import { ModalContext } from "../modal" import ConfirmWithMessageModal from "../timeline/menu/ConfirmWithMessageModal.tsx" import { getPowerLevels } from "../timeline/menu/util.ts" -import IgnoreIcon from "@/icons/block.svg?react" +import StartDMButton from "./StartDMButton.tsx" +import UserIgnoreButton from "./UserIgnoreButton.tsx" import BanIcon from "@/icons/gavel.svg?react" import InviteIcon from "@/icons/person-add.svg?react" import KickIcon from "@/icons/person-remove.svg?react" @@ -32,37 +33,6 @@ interface UserModerationProps { member: MemDBEvent | null; } -const UserIgnoreButton = ({ userID, client }: { userID: string; client: Client }) => { - const ignoredUsers = useAccountData(client.store, "m.ignored_user_list") as IgnoredUsersEventContent | null - - const isIgnored = Boolean(ignoredUsers?.ignored_users?.[userID]) - const ignoreUser = () => { - const newIgnoredUsers = { ...(ignoredUsers || { ignored_users: {}}) } - newIgnoredUsers.ignored_users[userID] = {} - client.rpc.setAccountData("m.ignored_user_list", newIgnoredUsers).catch(err => { - console.error("Failed to ignore user", err) - window.alert(`Failed to ignore ${userID}: ${err}`) - }) - } - const unignoreUser = () => { - const newIgnoredUsers = { ...(ignoredUsers || { ignored_users: {}}) } - delete newIgnoredUsers.ignored_users[userID] - client.rpc.setAccountData("m.ignored_user_list", newIgnoredUsers).catch(err => { - console.error("Failed to unignore user", err) - window.alert(`Failed to unignore ${userID}: ${err}`) - }) - } - - return ( - - ) -} - const UserModeration = ({ userID, client, member, room }: UserModerationProps) => { const openModal = use(ModalContext) const hasPL = (action: "invite" | "kick" | "ban") => { @@ -109,7 +79,8 @@ const UserModeration = ({ userID, client, member, room }: UserModerationProps) = const membership = member?.content.membership || "leave" return
-

Moderation

+

Actions

+ {!room || room.meta.current.dm_user_id !== userID ? : null} {room && (["knock", "leave"].includes(membership) || !member) && hasPL("invite") && ( +} + +export const RoomMenu = ({ room, style }: RoomMenuProps) => { + const openModal = use(ModalContext) + const openSettings = () => { + openModal({ + dimmed: true, + boxed: true, + innerBoxClass: "settings-view", + content: , + }) + } + return
+ + +
+} diff --git a/web/src/ui/timeline/menu/index.css b/web/src/ui/timeline/menu/index.css index b9f8bcc..14b4727 100644 --- a/web/src/ui/timeline/menu/index.css +++ b/web/src/ui/timeline/menu/index.css @@ -43,7 +43,7 @@ div.event-fixed-menu { } } -div.event-context-menu { +div.context-menu { position: fixed; background-color: var(--background-color); border-radius: .5rem; @@ -80,6 +80,10 @@ div.event-context-menu { color: var(--error-color); } } + + &.event-context-menu, &.room-list-menu { + width: 10rem; + } } div.confirm-message-modal > form { diff --git a/web/src/ui/timeline/menu/index.ts b/web/src/ui/timeline/menu/index.ts index 4f89140..07a73d0 100644 --- a/web/src/ui/timeline/menu/index.ts +++ b/web/src/ui/timeline/menu/index.ts @@ -14,4 +14,5 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . export { EventExtraMenu, EventFixedMenu, EventFullMenu, EventHoverMenu } from "./EventMenu.tsx" +export { RoomMenu } from "./RoomMenu.tsx" export { getModalStyleFromMouse } from "./util.ts" diff --git a/web/src/ui/timeline/menu/util.ts b/web/src/ui/timeline/menu/util.ts index 1692a12..556d729 100644 --- a/web/src/ui/timeline/menu/util.ts +++ b/web/src/ui/timeline/menu/util.ts @@ -39,10 +39,10 @@ export const getEncryption = (room: RoomStateStore): boolean =>{ export function getModalStyleFromMouse( evt: React.MouseEvent, modalHeight: number, modalWidth = 10 * 16, ): CSSProperties { - const style: CSSProperties = { right: window.innerWidth - evt.clientX } - if (evt.clientX - modalWidth < 4) { - delete style.right - style.left = "4px" + const style: CSSProperties = { left: evt.clientX } + if (evt.clientX + modalWidth > window.innerWidth) { + delete style.left + style.right = "4px" } if (evt.clientY + modalHeight > window.innerHeight) { style.bottom = window.innerHeight - evt.clientY From 1ba4d532c96968a00901a2127b3923bb32634cb4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 25 Feb 2025 21:45:28 +0200 Subject: [PATCH 58/78] web/menu: move directory out of timeline --- web/src/ui/{timeline => }/menu/ConfirmWithMessageModal.tsx | 4 ++-- web/src/ui/{timeline => }/menu/EventMenu.tsx | 4 ++-- web/src/ui/{timeline => }/menu/RoomMenu.css | 0 web/src/ui/{timeline => }/menu/RoomMenu.tsx | 6 +++--- web/src/ui/{timeline => }/menu/ShareModal.tsx | 0 web/src/ui/{timeline => }/menu/index.css | 0 web/src/ui/{timeline => }/menu/index.ts | 0 web/src/ui/{timeline => }/menu/usePrimaryItems.tsx | 6 +++--- web/src/ui/{timeline => }/menu/useSecondaryItems.tsx | 6 +++--- web/src/ui/{timeline => }/menu/util.ts | 0 web/src/ui/rightpanel/UserModeration.tsx | 4 ++-- web/src/ui/roomlist/Entry.tsx | 2 +- web/src/ui/timeline/TimelineEvent.tsx | 2 +- 13 files changed, 17 insertions(+), 17 deletions(-) rename web/src/ui/{timeline => }/menu/ConfirmWithMessageModal.tsx (95%) rename web/src/ui/{timeline => }/menu/EventMenu.tsx (95%) rename web/src/ui/{timeline => }/menu/RoomMenu.css (100%) rename web/src/ui/{timeline => }/menu/RoomMenu.tsx (94%) rename web/src/ui/{timeline => }/menu/ShareModal.tsx (100%) rename web/src/ui/{timeline => }/menu/index.css (100%) rename web/src/ui/{timeline => }/menu/index.ts (100%) rename web/src/ui/{timeline => }/menu/usePrimaryItems.tsx (96%) rename web/src/ui/{timeline => }/menu/useSecondaryItems.tsx (97%) rename web/src/ui/{timeline => }/menu/util.ts (100%) diff --git a/web/src/ui/timeline/menu/ConfirmWithMessageModal.tsx b/web/src/ui/menu/ConfirmWithMessageModal.tsx similarity index 95% rename from web/src/ui/timeline/menu/ConfirmWithMessageModal.tsx rename to web/src/ui/menu/ConfirmWithMessageModal.tsx index 05d11e1..4d44084 100644 --- a/web/src/ui/timeline/menu/ConfirmWithMessageModal.tsx +++ b/web/src/ui/menu/ConfirmWithMessageModal.tsx @@ -16,8 +16,8 @@ import React, { JSX, use, useState } from "react" import { MemDBEvent } from "@/api/types" import { isMobileDevice } from "@/util/ismobile.ts" -import { ModalCloseContext } from "../../modal" -import TimelineEvent from "../TimelineEvent.tsx" +import { ModalCloseContext } from "../modal" +import TimelineEvent from "../timeline/TimelineEvent.tsx" interface ConfirmWithMessageProps { evt?: MemDBEvent diff --git a/web/src/ui/timeline/menu/EventMenu.tsx b/web/src/ui/menu/EventMenu.tsx similarity index 95% rename from web/src/ui/timeline/menu/EventMenu.tsx rename to web/src/ui/menu/EventMenu.tsx index d0c7a74..afa4f54 100644 --- a/web/src/ui/timeline/menu/EventMenu.tsx +++ b/web/src/ui/menu/EventMenu.tsx @@ -15,8 +15,8 @@ // along with this program. If not, see . import { CSSProperties, use } from "react" import { MemDBEvent } from "@/api/types" -import ClientContext from "../../ClientContext.ts" -import { RoomContextData } from "../../roomview/roomcontext.ts" +import ClientContext from "../ClientContext.ts" +import { RoomContextData } from "../roomview/roomcontext.ts" import { usePrimaryItems } from "./usePrimaryItems.tsx" import { useSecondaryItems } from "./useSecondaryItems.tsx" import CloseIcon from "@/icons/close.svg?react" diff --git a/web/src/ui/timeline/menu/RoomMenu.css b/web/src/ui/menu/RoomMenu.css similarity index 100% rename from web/src/ui/timeline/menu/RoomMenu.css rename to web/src/ui/menu/RoomMenu.css diff --git a/web/src/ui/timeline/menu/RoomMenu.tsx b/web/src/ui/menu/RoomMenu.tsx similarity index 94% rename from web/src/ui/timeline/menu/RoomMenu.tsx rename to web/src/ui/menu/RoomMenu.tsx index 46cad47..6adb172 100644 --- a/web/src/ui/timeline/menu/RoomMenu.tsx +++ b/web/src/ui/menu/RoomMenu.tsx @@ -16,9 +16,9 @@ import { CSSProperties, use } from "react" import { RoomListEntry, RoomStateStore, useAccountData } from "@/api/statestore" import { RoomID } from "@/api/types" -import ClientContext from "../../ClientContext.ts" -import { ModalContext } from "../../modal" -import SettingsView from "../../settings/SettingsView.tsx" +import ClientContext from "../ClientContext.ts" +import { ModalContext } from "../modal" +import SettingsView from "../settings/SettingsView.tsx" import NotificationsOffIcon from "@/icons/notifications-off.svg?react" import NotificationsIcon from "@/icons/notifications.svg?react" import SettingsIcon from "@/icons/settings.svg?react" diff --git a/web/src/ui/timeline/menu/ShareModal.tsx b/web/src/ui/menu/ShareModal.tsx similarity index 100% rename from web/src/ui/timeline/menu/ShareModal.tsx rename to web/src/ui/menu/ShareModal.tsx diff --git a/web/src/ui/timeline/menu/index.css b/web/src/ui/menu/index.css similarity index 100% rename from web/src/ui/timeline/menu/index.css rename to web/src/ui/menu/index.css diff --git a/web/src/ui/timeline/menu/index.ts b/web/src/ui/menu/index.ts similarity index 100% rename from web/src/ui/timeline/menu/index.ts rename to web/src/ui/menu/index.ts diff --git a/web/src/ui/timeline/menu/usePrimaryItems.tsx b/web/src/ui/menu/usePrimaryItems.tsx similarity index 96% rename from web/src/ui/timeline/menu/usePrimaryItems.tsx rename to web/src/ui/menu/usePrimaryItems.tsx index 8193fd3..a9974e2 100644 --- a/web/src/ui/timeline/menu/usePrimaryItems.tsx +++ b/web/src/ui/menu/usePrimaryItems.tsx @@ -18,9 +18,9 @@ import Client from "@/api/client.ts" import { MemDBEvent } from "@/api/types" import { emojiToReactionContent } from "@/util/emoji" import { useEventAsState } from "@/util/eventdispatcher.ts" -import EmojiPicker from "../../emojipicker/EmojiPicker.tsx" -import { ModalCloseContext, ModalContext } from "../../modal" -import { RoomContextData } from "../../roomview/roomcontext.ts" +import EmojiPicker from "../emojipicker/EmojiPicker.tsx" +import { ModalCloseContext, ModalContext } from "../modal" +import { RoomContextData } from "../roomview/roomcontext.ts" import { EventExtraMenu } from "./EventMenu.tsx" import { getEncryption, getModalStyleFromButton, getPending, getPowerLevels } from "./util.ts" import EditIcon from "@/icons/edit.svg?react" diff --git a/web/src/ui/timeline/menu/useSecondaryItems.tsx b/web/src/ui/menu/useSecondaryItems.tsx similarity index 97% rename from web/src/ui/timeline/menu/useSecondaryItems.tsx rename to web/src/ui/menu/useSecondaryItems.tsx index 5c9ea15..76ecb4d 100644 --- a/web/src/ui/timeline/menu/useSecondaryItems.tsx +++ b/web/src/ui/menu/useSecondaryItems.tsx @@ -17,9 +17,9 @@ import { use } from "react" import Client from "@/api/client.ts" import { useRoomState } from "@/api/statestore" import { MemDBEvent } from "@/api/types" -import { ModalCloseContext, ModalContext } from "../../modal" -import { RoomContext, RoomContextData } from "../../roomview/roomcontext.ts" -import JSONView from "../../util/JSONView.tsx" +import { ModalCloseContext, ModalContext } from "../modal" +import { RoomContext, RoomContextData } from "../roomview/roomcontext.ts" +import JSONView from "../util/JSONView.tsx" import ConfirmWithMessageModal from "./ConfirmWithMessageModal.tsx" import ShareModal from "./ShareModal.tsx" import { getPending, getPowerLevels } from "./util.ts" diff --git a/web/src/ui/timeline/menu/util.ts b/web/src/ui/menu/util.ts similarity index 100% rename from web/src/ui/timeline/menu/util.ts rename to web/src/ui/menu/util.ts diff --git a/web/src/ui/rightpanel/UserModeration.tsx b/web/src/ui/rightpanel/UserModeration.tsx index c7a7d0a..3f2d586 100644 --- a/web/src/ui/rightpanel/UserModeration.tsx +++ b/web/src/ui/rightpanel/UserModeration.tsx @@ -17,9 +17,9 @@ import { use } from "react" import Client from "@/api/client.ts" import { RoomStateStore } from "@/api/statestore" import { MemDBEvent, MembershipAction } from "@/api/types" +import ConfirmWithMessageModal from "../menu/ConfirmWithMessageModal.tsx" +import { getPowerLevels } from "../menu/util.ts" import { ModalContext } from "../modal" -import ConfirmWithMessageModal from "../timeline/menu/ConfirmWithMessageModal.tsx" -import { getPowerLevels } from "../timeline/menu/util.ts" import StartDMButton from "./StartDMButton.tsx" import UserIgnoreButton from "./UserIgnoreButton.tsx" import BanIcon from "@/icons/gavel.svg?react" diff --git a/web/src/ui/roomlist/Entry.tsx b/web/src/ui/roomlist/Entry.tsx index c456bc3..6e4c7f4 100644 --- a/web/src/ui/roomlist/Entry.tsx +++ b/web/src/ui/roomlist/Entry.tsx @@ -21,8 +21,8 @@ import useContentVisibility from "@/util/contentvisibility.ts" import { getDisplayname } from "@/util/validation.ts" import ClientContext from "../ClientContext.ts" import MainScreenContext from "../MainScreenContext.ts" +import { RoomMenu, getModalStyleFromMouse } from "../menu" import { ModalContext } from "../modal" -import { RoomMenu, getModalStyleFromMouse } from "../timeline/menu" import UnreadCount from "./UnreadCount.tsx" export interface RoomListEntryProps { diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index b78cc06..2f0a4fa 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -22,6 +22,7 @@ import { isMobileDevice } from "@/util/ismobile.ts" import { getDisplayname, isEventID } from "@/util/validation.ts" import ClientContext from "../ClientContext.ts" import MainScreenContext from "../MainScreenContext.ts" +import { EventFixedMenu, EventFullMenu, EventHoverMenu, getModalStyleFromMouse } from "../menu" import { ModalContext, NestableModalContext } from "../modal" import { useRoomContext } from "../roomview/roomcontext.ts" import EventEditHistory from "./EventEditHistory.tsx" @@ -29,7 +30,6 @@ import ReadReceipts from "./ReadReceipts.tsx" import { ReplyIDBody } from "./ReplyBody.tsx" import URLPreviews from "./URLPreviews.tsx" import { ContentErrorBoundary, HiddenEvent, getBodyType, getPerMessageProfile, isSmallEvent } from "./content" -import { EventFixedMenu, EventFullMenu, EventHoverMenu, getModalStyleFromMouse } from "./menu" import ErrorIcon from "@/icons/error.svg?react" import PendingIcon from "@/icons/pending.svg?react" import SentIcon from "@/icons/sent.svg?react" From 62d8d061297cddbc43cdc5e2a9d1f4e5a168bdf1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 25 Feb 2025 21:58:45 +0200 Subject: [PATCH 59/78] web/menu/roomlist: implement marking as read/unread and leaving room Fixes #523 --- web/src/icons/door-open.svg | 1 + web/src/icons/mark-read.svg | 1 + web/src/icons/mark-unread.svg | 1 + web/src/ui/menu/RoomMenu.tsx | 52 ++++++++++++++++++++++++++++++++++- web/src/ui/roomlist/Entry.tsx | 1 + 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 web/src/icons/door-open.svg create mode 100644 web/src/icons/mark-read.svg create mode 100644 web/src/icons/mark-unread.svg diff --git a/web/src/icons/door-open.svg b/web/src/icons/door-open.svg new file mode 100644 index 0000000..d4a5719 --- /dev/null +++ b/web/src/icons/door-open.svg @@ -0,0 +1 @@ + diff --git a/web/src/icons/mark-read.svg b/web/src/icons/mark-read.svg new file mode 100644 index 0000000..3819d33 --- /dev/null +++ b/web/src/icons/mark-read.svg @@ -0,0 +1 @@ + diff --git a/web/src/icons/mark-unread.svg b/web/src/icons/mark-unread.svg new file mode 100644 index 0000000..2228f31 --- /dev/null +++ b/web/src/icons/mark-unread.svg @@ -0,0 +1 @@ + diff --git a/web/src/ui/menu/RoomMenu.tsx b/web/src/ui/menu/RoomMenu.tsx index 6adb172..a0ccd20 100644 --- a/web/src/ui/menu/RoomMenu.tsx +++ b/web/src/ui/menu/RoomMenu.tsx @@ -16,9 +16,13 @@ import { CSSProperties, use } from "react" import { RoomListEntry, RoomStateStore, useAccountData } from "@/api/statestore" import { RoomID } from "@/api/types" +import { useEventAsState } from "@/util/eventdispatcher.ts" import ClientContext from "../ClientContext.ts" -import { ModalContext } from "../modal" +import { ModalCloseContext, ModalContext } from "../modal" import SettingsView from "../settings/SettingsView.tsx" +import DoorOpenIcon from "@/icons/door-open.svg?react" +import MarkReadIcon from "@/icons/mark-read.svg?react" +import MarkUnreadIcon from "@/icons/mark-unread.svg?react" import NotificationsOffIcon from "@/icons/notifications-off.svg?react" import NotificationsIcon from "@/icons/notifications.svg?react" import SettingsIcon from "@/icons/settings.svg?react" @@ -36,6 +40,7 @@ const hasNotifyingActions = (actions: unknown) => { const MuteButton = ({ roomID }: { roomID: RoomID }) => { const client = use(ClientContext)! + const closeModal = use(ModalCloseContext) const roomRules = useAccountData(client.store, "m.push_rules")?.global?.room const pushRule = Array.isArray(roomRules) ? roomRules.find(rule => rule?.rule_id === roomID) : null const muted = pushRule?.enabled === true && !hasNotifyingActions(pushRule.actions) @@ -44,6 +49,7 @@ const MuteButton = ({ roomID }: { roomID: RoomID }) => { console.error("Failed to mute room", err) window.alert(`Failed to ${muted ? "unmute" : "mute"} room: ${err}`) }) + closeModal() } return } +const MarkReadButton = ({ room }: { room: RoomStateStore }) => { + const meta = useEventAsState(room.meta) + const client = use(ClientContext)! + const closeModal = use(ModalCloseContext) + const read = !meta.marked_unread && meta.unread_messages === 0 + const markRead = () => { + const evt = room.eventsByRowID.get( + room.timeline[room.timeline.length-1]?.event_rowid ?? meta.preview_event_rowid, + ) + if (!evt) { + window.alert("Can't mark room as read: last event not found in cache") + return + } + const rrType = room.preferences.send_read_receipts ? "m.read" : "m.read.private" + client.rpc.markRead(room.roomID, evt.event_id, rrType).catch(err => { + console.error("Failed to mark room as read", err) + window.alert(`Failed to mark room as read: ${err}`) + }) + closeModal() + } + const markUnread = () => { + client.rpc.setAccountData("m.marked_unread", { unread: true }, room.roomID).catch(err => { + console.error("Failed to mark room as unread", err) + window.alert(`Failed to mark room as unread: ${err}`) + }) + closeModal() + } + return +} + export const RoomMenu = ({ room, style }: RoomMenuProps) => { const openModal = use(ModalContext) + const closeModal = use(ModalCloseContext) + const client = use(ClientContext)! const openSettings = () => { openModal({ dimmed: true, @@ -61,8 +102,17 @@ export const RoomMenu = ({ room, style }: RoomMenuProps) => { content: , }) } + const leaveRoom = () => { + client.rpc.leaveRoom(room.roomID).catch(err => { + console.error("Failed to leave room", err) + window.alert(`Failed to leave room: ${err}`) + }) + closeModal() + } return
+ +
} diff --git a/web/src/ui/roomlist/Entry.tsx b/web/src/ui/roomlist/Entry.tsx index 6e4c7f4..020517b 100644 --- a/web/src/ui/roomlist/Entry.tsx +++ b/web/src/ui/roomlist/Entry.tsx @@ -85,6 +85,7 @@ const Entry = ({ room, isActive, hidden }: RoomListEntryProps) => { const onContextMenu = (evt: React.MouseEvent) => { const realRoom = client.store.rooms.get(room.room_id) if (!realRoom) { + // TODO implement separate menu for invite rooms console.error("Room state store not found for", room.room_id) return } From 4247e171601fc0b21ea2b47f8658bcfe300aeaf1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 25 Feb 2025 23:24:43 +0200 Subject: [PATCH 60/78] web/menu/roomlist: add confirmation for leaving --- web/src/ui/menu/RoomMenu.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/ui/menu/RoomMenu.tsx b/web/src/ui/menu/RoomMenu.tsx index a0ccd20..859b88c 100644 --- a/web/src/ui/menu/RoomMenu.tsx +++ b/web/src/ui/menu/RoomMenu.tsx @@ -103,6 +103,9 @@ export const RoomMenu = ({ room, style }: RoomMenuProps) => { }) } const leaveRoom = () => { + if (!window.confirm(`Really leave ${room.meta.current.name}?`)) { + return + } client.rpc.leaveRoom(room.roomID).catch(err => { console.error("Failed to leave room", err) window.alert(`Failed to leave room: ${err}`) From 7168683a4be8e209d70f154f3fdb14d636c0516d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 26 Feb 2025 22:57:21 +0200 Subject: [PATCH 61/78] dependencies: update mautrix-go --- desktop/go.mod | 2 +- desktop/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/desktop/go.mod b/desktop/go.mod index 402586b..927bee3 100644 --- a/desktop/go.mod +++ b/desktop/go.mod @@ -79,7 +79,7 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca // indirect + maunium.net/go/mautrix v0.23.2-0.20250226205639-b72caa948c18 // indirect mvdan.cc/xurls/v2 v2.6.0 // indirect ) diff --git a/desktop/go.sum b/desktop/go.sum index 09a6b4f..7d4501d 100644 --- a/desktop/go.sum +++ b/desktop/go.sum @@ -261,7 +261,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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.23.2-0.20250223161309-1cc073cde6ca h1:xPbRPallD4qh/XuQWheRsvxsf/5stfdA+uIj0S0P2kQ= -maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= +maunium.net/go/mautrix v0.23.2-0.20250226205639-b72caa948c18 h1:1JVivuS1whIdai/Yurqe1OXiHAarCh0UgR/zh61coiQ= +maunium.net/go/mautrix v0.23.2-0.20250226205639-b72caa948c18/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= diff --git a/go.mod b/go.mod index 3e19779..efd1233 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( golang.org/x/text v0.22.0 gopkg.in/yaml.v3 v3.0.1 maunium.net/go/mauflag v1.0.0 - maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca + maunium.net/go/mautrix v0.23.2-0.20250226205639-b72caa948c18 mvdan.cc/xurls/v2 v2.6.0 ) diff --git a/go.sum b/go.sum index 8ba720c..44b9c84 100644 --- a/go.sum +++ b/go.sum @@ -100,7 +100,7 @@ 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/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca h1:xPbRPallD4qh/XuQWheRsvxsf/5stfdA+uIj0S0P2kQ= -maunium.net/go/mautrix v0.23.2-0.20250223161309-1cc073cde6ca/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= +maunium.net/go/mautrix v0.23.2-0.20250226205639-b72caa948c18 h1:1JVivuS1whIdai/Yurqe1OXiHAarCh0UgR/zh61coiQ= +maunium.net/go/mautrix v0.23.2-0.20250226205639-b72caa948c18/go.mod h1:kldoZQDneV/jquIhwG1MmMw5j2A2M/MnQYRSWt863cY= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= From b48c285f5c3e1e7923f1bf6a5c80e48c7d8cf017 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 28 Feb 2025 19:33:51 +0200 Subject: [PATCH 62/78] hicli/send: move extra to `m.new_content` when editing --- pkg/hicli/send.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/hicli/send.go b/pkg/hicli/send.go index 197af9c..3ecd3d3 100644 --- a/pkg/hicli/send.go +++ b/pkg/hicli/send.go @@ -167,6 +167,11 @@ func (h *HiClient) SendMessage( if contentCopy.File != nil { content.URL = contentCopy.File.URL } + if extra != nil { + extra = map[string]any{ + "m.new_content": extra, + } + } } else { content.RelatesTo = relatesTo } From 91fa59d5ba0527f189be6b80d56ee28a824ad49c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 1 Mar 2025 21:39:11 +0200 Subject: [PATCH 63/78] web/statestore: fix updating view when viewing redacted events --- web/src/api/statestore/room.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/src/api/statestore/room.ts b/web/src/api/statestore/room.ts index a1334f9..b308489 100644 --- a/web/src/api/statestore/room.ts +++ b/web/src/api/statestore/room.ts @@ -353,7 +353,12 @@ export class RoomStateStore { } setViewingRedacted(evt: MemDBEvent, view: boolean) { - evt.viewing_redacted = view + const modified = { + ...evt, + viewing_redacted: view, + } + this.eventsByRowID.set(evt.rowid, modified) + this.eventsByID.set(evt.event_id, modified) this.eventSubs.notify(evt.event_id) this.notifyTimelineSubscribers() } From 014c63f0e78b39bcdb763ade086611695fe58949 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 1 Mar 2025 21:43:09 +0200 Subject: [PATCH 64/78] hicli/sync: ignore m.unavailable withheld events from own devices --- pkg/hicli/sync.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/hicli/sync.go b/pkg/hicli/sync.go index 3821776..074d7e0 100644 --- a/pkg/hicli/sync.go +++ b/pkg/hicli/sync.go @@ -82,7 +82,12 @@ func (h *HiClient) preProcessSyncResponse(ctx context.Context, resp *mautrix.Res case *event.EncryptedEventContent: h.Crypto.HandleEncryptedEvent(ctx, evt) case *event.RoomKeyWithheldEventContent: - h.Crypto.HandleRoomKeyWithheld(ctx, content) + // TODO move this check to mautrix-go? + if evt.Sender == h.Account.UserID && content.Code == event.RoomKeyWithheldUnavailable { + log.Debug().Any("withheld_content", content).Msg("Ignoring m.unavailable megolm session withheld event") + } else { + h.Crypto.HandleRoomKeyWithheld(ctx, content) + } default: postponedToDevices = append(postponedToDevices, evt) } From 83ea1c12ad3fdecac087ca5d6a0eaec350e58a95 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 1 Mar 2025 21:44:40 +0200 Subject: [PATCH 65/78] web/statestore: fix applying undecryptable edits --- web/src/api/statestore/room.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/web/src/api/statestore/room.ts b/web/src/api/statestore/room.ts index b308489..02a53a4 100644 --- a/web/src/api/statestore/room.ts +++ b/web/src/api/statestore/room.ts @@ -383,22 +383,24 @@ export class RoomStateStore { if (memEvt.last_edit_rowid) { memEvt.last_edit = this.eventsByRowID.get(memEvt.last_edit_rowid) if (memEvt.last_edit) { - memEvt.orig_content = memEvt.content - memEvt.orig_local_content = memEvt.local_content - memEvt.content = memEvt.last_edit.content["m.new_content"] + memEvt.orig_content = memEvt.orig_content ?? memEvt.content + memEvt.orig_local_content = memEvt.orig_local_content ?? memEvt.local_content + memEvt.content = memEvt.last_edit.content["m.new_content"] ?? memEvt.last_edit.content memEvt.local_content = memEvt.last_edit.local_content } } else if (memEvt.relation_type === "m.replace" && memEvt.relates_to) { const editTarget = this.eventsByID.get(memEvt.relates_to) - if (editTarget?.last_edit_rowid === memEvt.rowid && !editTarget.last_edit) { - this.eventsByRowID.set(editTarget.rowid, { + if (editTarget?.last_edit_rowid === memEvt.rowid) { + const modified: MemDBEvent = { ...editTarget, last_edit: memEvt, - orig_local_content: editTarget.local_content, - orig_content: editTarget.content, - content: memEvt.content["m.new_content"], + orig_local_content: editTarget.orig_local_content ?? editTarget.local_content, + orig_content: editTarget.orig_content ?? editTarget.content, + content: memEvt.content["m.new_content"] ?? memEvt.content, local_content: memEvt.local_content, - }) + } + this.eventsByRowID.set(editTarget.rowid, modified) + this.eventsByID.set(editTarget.event_id, modified) this.eventSubs.notify(editTarget.event_id) } } From 1ffb44fc27e79a3009fec2495ee563913cffe24c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 1 Mar 2025 21:44:51 +0200 Subject: [PATCH 66/78] web/timeline: fix message for unknown session errors --- web/src/ui/timeline/content/EncryptedBody.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/src/ui/timeline/content/EncryptedBody.tsx b/web/src/ui/timeline/content/EncryptedBody.tsx index 4906043..70e611d 100644 --- a/web/src/ui/timeline/content/EncryptedBody.tsx +++ b/web/src/ui/timeline/content/EncryptedBody.tsx @@ -17,9 +17,12 @@ import EventContentProps from "./props.ts" import LockIcon from "../../../icons/lock.svg?react" import LockClockIcon from "../../../icons/lock.svg?react" +const unknownSessionErrorPrefix = "failed to decrypt megolm event: no session with given ID found" + const EncryptedBody = ({ event }: EventContentProps) => { - if (event.decryption_error) { - return
Failed to decrypt: {event.decryption_error}
+ const decryptionError = event.last_edit?.decryption_error ?? event.decryption_error + if (decryptionError && !decryptionError.startsWith(unknownSessionErrorPrefix)) { + return
Failed to decrypt: {decryptionError}
} return
Waiting for message
} From d06ed8637cc7fe9c80cd9f3963a0e4b337ecac76 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 1 Mar 2025 21:51:39 +0200 Subject: [PATCH 67/78] hicli/send: include geo_uri in edit fallback --- pkg/hicli/send.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/hicli/send.go b/pkg/hicli/send.go index 3ecd3d3..4155bb1 100644 --- a/pkg/hicli/send.go +++ b/pkg/hicli/send.go @@ -161,6 +161,7 @@ func (h *HiClient) SendMessage( Body: "", MsgType: contentCopy.MsgType, URL: contentCopy.URL, + GeoURI: contentCopy.GeoURI, NewContent: &contentCopy, RelatesTo: relatesTo, } From 5aacc3424d82e3e13395a84830189fa2e37d513e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 2 Mar 2025 17:16:21 +0200 Subject: [PATCH 68/78] web/settings: add readOnly flags to hidden fields to fix warnings --- web/src/ui/settings/SettingsView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/ui/settings/SettingsView.tsx b/web/src/ui/settings/SettingsView.tsx index c1b9903..b0d5854 100644 --- a/web/src/ui/settings/SettingsView.tsx +++ b/web/src/ui/settings/SettingsView.tsx @@ -303,7 +303,7 @@ const KeyExportView = ({ room }: SettingsViewProps) => { method="post" target="_blank" > - + {
- +
- +
From aeabda449d28ed967ba58c6d9bc10ad8a13929f1 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 2 Mar 2025 18:35:30 +0200 Subject: [PATCH 69/78] web/settings: add room state explorer Fixes #516 Closes #526 --- web/src/ui/menu/RoomMenu.tsx | 6 +- web/src/ui/modal/Modal.tsx | 3 + web/src/ui/roomview/RoomViewHeader.tsx | 5 +- web/src/ui/settings/RoomStateExplorer.css | 78 +++++++ web/src/ui/settings/RoomStateExplorer.tsx | 246 ++++++++++++++++++++++ web/src/ui/settings/SettingsView.css | 17 +- web/src/ui/settings/SettingsView.tsx | 18 +- web/src/vite-env.d.ts | 2 + 8 files changed, 361 insertions(+), 14 deletions(-) create mode 100644 web/src/ui/settings/RoomStateExplorer.css create mode 100644 web/src/ui/settings/RoomStateExplorer.tsx diff --git a/web/src/ui/menu/RoomMenu.tsx b/web/src/ui/menu/RoomMenu.tsx index 859b88c..d430f97 100644 --- a/web/src/ui/menu/RoomMenu.tsx +++ b/web/src/ui/menu/RoomMenu.tsx @@ -18,7 +18,7 @@ import { RoomListEntry, RoomStateStore, useAccountData } from "@/api/statestore" import { RoomID } from "@/api/types" import { useEventAsState } from "@/util/eventdispatcher.ts" import ClientContext from "../ClientContext.ts" -import { ModalCloseContext, ModalContext } from "../modal" +import { ModalCloseContext } from "../modal" import SettingsView from "../settings/SettingsView.tsx" import DoorOpenIcon from "@/icons/door-open.svg?react" import MarkReadIcon from "@/icons/mark-read.svg?react" @@ -91,11 +91,11 @@ const MarkReadButton = ({ room }: { room: RoomStateStore }) => { } export const RoomMenu = ({ room, style }: RoomMenuProps) => { - const openModal = use(ModalContext) const closeModal = use(ModalCloseContext) const client = use(ClientContext)! const openSettings = () => { - openModal({ + closeModal() + window.openNestableModal({ dimmed: true, boxed: true, innerBoxClass: "settings-view", diff --git a/web/src/ui/modal/Modal.tsx b/web/src/ui/modal/Modal.tsx index 620d8bf..1188cd1 100644 --- a/web/src/ui/modal/Modal.tsx +++ b/web/src/ui/modal/Modal.tsx @@ -97,6 +97,9 @@ const ModalWrapper = ({ children, ContextType, historyStateKey }: ModalWrapperPr modal = content } } + if (historyStateKey === "nestable_modal") { + window.openNestableModal = openModal + } return {children} {modal} diff --git a/web/src/ui/roomview/RoomViewHeader.tsx b/web/src/ui/roomview/RoomViewHeader.tsx index 16ce3af..12413fe 100644 --- a/web/src/ui/roomview/RoomViewHeader.tsx +++ b/web/src/ui/roomview/RoomViewHeader.tsx @@ -18,8 +18,7 @@ import { getRoomAvatarThumbnailURL, getRoomAvatarURL } from "@/api/media.ts" import { RoomStateStore } from "@/api/statestore" import { useEventAsState } from "@/util/eventdispatcher.ts" import MainScreenContext from "../MainScreenContext.ts" -import { LightboxContext } from "../modal" -import { ModalContext } from "../modal" +import { LightboxContext, NestableModalContext } from "../modal" import SettingsView from "../settings/SettingsView.tsx" import BackIcon from "@/icons/back.svg?react" import PeopleIcon from "@/icons/group.svg?react" @@ -34,7 +33,7 @@ interface RoomViewHeaderProps { const RoomViewHeader = ({ room }: RoomViewHeaderProps) => { const roomMeta = useEventAsState(room.meta) const mainScreen = use(MainScreenContext) - const openModal = use(ModalContext) + const openModal = use(NestableModalContext) const openSettings = () => { openModal({ dimmed: true, diff --git a/web/src/ui/settings/RoomStateExplorer.css b/web/src/ui/settings/RoomStateExplorer.css new file mode 100644 index 0000000..fd98533 --- /dev/null +++ b/web/src/ui/settings/RoomStateExplorer.css @@ -0,0 +1,78 @@ +div.state-explorer-box { + overflow: hidden !important; +} + +div.state-explorer { + width: min(50rem, 80vw); + max-height: 100%; + display: flex; + flex-direction: column; + + div.state-button-list { + display: flex; + flex-wrap: wrap; + gap: .5rem; + overflow: auto; + + > button { + padding: .5rem; + border: 1px solid var(--border-color); + border-radius: 0.5rem; + } + } + + > div.nav-buttons { + display: flex; + flex-wrap: wrap; + gap: .5rem; + margin-top: .5rem; + justify-content: space-between; + + > button { + padding: .5rem 1rem; + border: 1px solid var(--border-color); + border-radius: .5rem; + } + } + + &.state-event-view { + > div.state-event-content { + flex: 1; + overflow: auto; + + > textarea { + width: 100%; + padding: .5rem; + box-sizing: border-box; + resize: vertical; + border: 1px solid var(--border-color); + outline: none; + border-radius: .5rem; + + &:focus { + border: 1px solid var(--primary-color); + } + } + } + + > div.state-header > div.new-event-type { + display: flex; + gap: .25rem; + margin-bottom: .25rem; + + > input { + flex: 1; + padding: .5rem; + border: 1px solid var(--border-color); + box-sizing: border-box; + border-radius: .5rem; + outline: none; + font-family: var(--monospace-font-stack); + + &:focus { + border: 1px solid var(--primary-color); + } + } + } + } +} diff --git a/web/src/ui/settings/RoomStateExplorer.tsx b/web/src/ui/settings/RoomStateExplorer.tsx new file mode 100644 index 0000000..c93c86a --- /dev/null +++ b/web/src/ui/settings/RoomStateExplorer.tsx @@ -0,0 +1,246 @@ +// gomuks - A Matrix client written in Go. +// 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 . +import { use, useCallback, useState } from "react" +import { RoomStateStore, useRoomState } from "@/api/statestore" +import ClientContext from "../ClientContext.ts" +import JSONView from "../util/JSONView" +import "./RoomStateExplorer.css" + +interface StateExplorerProps { + room: RoomStateStore +} + +interface StateEventViewProps { + room: RoomStateStore + type?: string + stateKey?: string + onBack: () => void + onDone?: (type: string, stateKey: string) => void +} + +interface StateKeyListProps { + room: RoomStateStore + type: string + onSelectStateKey: (stateKey: string) => void + onBack: () => void +} + +const StateEventView = ({ room, type, stateKey, onBack, onDone }: StateEventViewProps) => { + const event = useRoomState(room, type, stateKey) + const isNewEvent = type === undefined + const [editingContent, setEditingContent] = useState(isNewEvent ? "{\n\n}" : null) + const [newType, setNewType] = useState("") + const [newStateKey, setNewStateKey] = useState("") + const client = use(ClientContext)! + + const sendEdit = () => { + let parsedContent + try { + parsedContent = JSON.parse(editingContent || "{}") + } catch (err) { + window.alert(`Failed to parse JSON: ${err}`) + return + } + client.rpc.setState( + room.roomID, + type ?? newType, + stateKey ?? newStateKey, + parsedContent, + ).then( + () => { + console.log("Updated room state", room.roomID, type, stateKey) + setEditingContent(null) + if (isNewEvent) { + onDone?.(newType, newStateKey) + } + }, + err => { + console.error("Failed to update room state", err) + window.alert(`Failed to update room state: ${err}`) + }, + ) + } + const stopEdit = () => setEditingContent(null) + const startEdit = () => setEditingContent(JSON.stringify(event?.content || {}, null, 4)) + + return ( +
+
+ {isNewEvent + ? <> +

New state event

+
+ setNewType(evt.target.value)} + placeholder="Event type" + /> + setNewStateKey(evt.target.value)} + placeholder="State key" + /> +
+ + :

{type} ({stateKey ? {stateKey} : "no state key"})

+ } +
+
+ {editingContent !== null + ?