+16
-1
backend/go.mod
+16
-1
backend/go.mod
···
3
3
go 1.24.0
4
4
5
5
require (
6
+
github.com/fxamacker/cbor/v2 v2.9.0
6
7
github.com/go-chi/chi/v5 v5.1.0
7
8
github.com/go-chi/cors v1.2.1
8
9
github.com/go-jose/go-jose/v4 v4.0.4
10
+
github.com/gorilla/websocket v1.5.3
11
+
github.com/ipfs/go-cid v0.6.0
9
12
github.com/joho/godotenv v1.5.1
10
13
github.com/lib/pq v1.10.9
11
14
github.com/mattn/go-sqlite3 v1.14.22
···
14
17
15
18
require (
16
19
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
20
+
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
21
+
github.com/minio/sha256-simd v1.0.0 // indirect
22
+
github.com/mr-tron/base58 v1.2.0 // indirect
23
+
github.com/multiformats/go-base32 v0.0.3 // indirect
24
+
github.com/multiformats/go-base36 v0.1.0 // indirect
25
+
github.com/multiformats/go-multibase v0.2.0 // indirect
26
+
github.com/multiformats/go-multihash v0.2.3 // indirect
27
+
github.com/multiformats/go-varint v0.1.0 // indirect
17
28
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
29
+
github.com/spaolacci/murmur3 v1.1.0 // indirect
18
30
github.com/stretchr/testify v1.10.0 // indirect
19
-
golang.org/x/crypto v0.31.0 // indirect
31
+
github.com/x448/float16 v0.8.4 // indirect
32
+
golang.org/x/crypto v0.35.0 // indirect
33
+
golang.org/x/sys v0.30.0 // indirect
20
34
golang.org/x/text v0.32.0 // indirect
35
+
lukechampine.com/blake3 v1.1.6 // indirect
21
36
)
+33
-2
backend/go.sum
+33
-2
backend/go.sum
···
1
1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
2
2
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3
+
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
4
+
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
3
5
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
4
6
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
5
7
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
···
8
10
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
9
11
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
10
12
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
13
+
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
14
+
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
15
+
github.com/ipfs/go-cid v0.6.0 h1:DlOReBV1xhHBhhfy/gBNNTSyfOM6rLiIx9J7A4DGf30=
16
+
github.com/ipfs/go-cid v0.6.0/go.mod h1:NC4kS1LZjzfhK40UGmpXv5/qD2kcMzACYJNntCUiDhQ=
11
17
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
12
18
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
19
+
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
20
+
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
21
+
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
13
22
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
14
23
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
15
24
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
16
25
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
26
+
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
27
+
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
28
+
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
29
+
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
30
+
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
31
+
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
32
+
github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
33
+
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
34
+
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
35
+
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
36
+
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
37
+
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
38
+
github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI=
39
+
github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI=
17
40
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
18
41
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
42
+
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
43
+
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
19
44
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
20
45
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
21
-
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
22
-
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
46
+
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
47
+
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
48
+
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
49
+
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
23
50
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
24
51
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
52
+
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
53
+
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
25
54
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
26
55
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
27
56
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
28
57
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
58
+
lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c=
59
+
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
+4
-1
backend/internal/api/annotations.go
+4
-1
backend/internal/api/annotations.go
···
480
480
481
481
func resolveDIDToPDS(did string) (string, error) {
482
482
if strings.HasPrefix(did, "did:plc:") {
483
-
resp, err := http.Get("https://plc.directory/" + did)
483
+
client := &http.Client{
484
+
Timeout: 10 * time.Second,
485
+
}
486
+
resp, err := client.Get("https://plc.directory/" + did)
484
487
if err != nil {
485
488
return "", err
486
489
}
+435
backend/internal/api/apikey.go
+435
backend/internal/api/apikey.go
···
1
+
package api
2
+
3
+
import (
4
+
"crypto/rand"
5
+
"crypto/sha256"
6
+
"crypto/x509"
7
+
"encoding/hex"
8
+
"encoding/json"
9
+
"encoding/pem"
10
+
"fmt"
11
+
"net/http"
12
+
"strings"
13
+
"time"
14
+
15
+
"github.com/go-chi/chi/v5"
16
+
17
+
"margin.at/internal/db"
18
+
"margin.at/internal/xrpc"
19
+
)
20
+
21
+
type APIKeyHandler struct {
22
+
db *db.DB
23
+
refresher *TokenRefresher
24
+
}
25
+
26
+
func NewAPIKeyHandler(database *db.DB, refresher *TokenRefresher) *APIKeyHandler {
27
+
return &APIKeyHandler{db: database, refresher: refresher}
28
+
}
29
+
30
+
type CreateKeyRequest struct {
31
+
Name string `json:"name"`
32
+
}
33
+
34
+
type CreateKeyResponse struct {
35
+
ID string `json:"id"`
36
+
Name string `json:"name"`
37
+
Key string `json:"key"`
38
+
CreatedAt time.Time `json:"createdAt"`
39
+
}
40
+
41
+
func (h *APIKeyHandler) CreateKey(w http.ResponseWriter, r *http.Request) {
42
+
session, err := h.refresher.GetSessionWithAutoRefresh(r)
43
+
if err != nil {
44
+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
45
+
return
46
+
}
47
+
48
+
var req CreateKeyRequest
49
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
50
+
http.Error(w, "Invalid request body", http.StatusBadRequest)
51
+
return
52
+
}
53
+
54
+
if req.Name == "" {
55
+
req.Name = "API Key"
56
+
}
57
+
58
+
rawKey := generateAPIKey()
59
+
keyHash := hashAPIKey(rawKey)
60
+
keyID := generateKeyID()
61
+
62
+
apiKey := &db.APIKey{
63
+
ID: keyID,
64
+
OwnerDID: session.DID,
65
+
Name: req.Name,
66
+
KeyHash: keyHash,
67
+
CreatedAt: time.Now(),
68
+
}
69
+
70
+
if err := h.db.CreateAPIKey(apiKey); err != nil {
71
+
http.Error(w, "Failed to create key", http.StatusInternalServerError)
72
+
return
73
+
}
74
+
75
+
w.Header().Set("Content-Type", "application/json")
76
+
json.NewEncoder(w).Encode(CreateKeyResponse{
77
+
ID: keyID,
78
+
Name: req.Name,
79
+
Key: rawKey,
80
+
CreatedAt: apiKey.CreatedAt,
81
+
})
82
+
}
83
+
84
+
func (h *APIKeyHandler) ListKeys(w http.ResponseWriter, r *http.Request) {
85
+
session, err := h.refresher.GetSessionWithAutoRefresh(r)
86
+
if err != nil {
87
+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
88
+
return
89
+
}
90
+
91
+
keys, err := h.db.GetAPIKeysByOwner(session.DID)
92
+
if err != nil {
93
+
http.Error(w, "Failed to get keys", http.StatusInternalServerError)
94
+
return
95
+
}
96
+
97
+
if keys == nil {
98
+
keys = []db.APIKey{}
99
+
}
100
+
101
+
w.Header().Set("Content-Type", "application/json")
102
+
json.NewEncoder(w).Encode(map[string]interface{}{"keys": keys})
103
+
}
104
+
105
+
func (h *APIKeyHandler) DeleteKey(w http.ResponseWriter, r *http.Request) {
106
+
session, err := h.refresher.GetSessionWithAutoRefresh(r)
107
+
if err != nil {
108
+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
109
+
return
110
+
}
111
+
112
+
keyID := chi.URLParam(r, "id")
113
+
if keyID == "" {
114
+
http.Error(w, "Key ID required", http.StatusBadRequest)
115
+
return
116
+
}
117
+
118
+
if err := h.db.DeleteAPIKey(keyID, session.DID); err != nil {
119
+
http.Error(w, "Failed to delete key", http.StatusInternalServerError)
120
+
return
121
+
}
122
+
123
+
w.Header().Set("Content-Type", "application/json")
124
+
json.NewEncoder(w).Encode(map[string]bool{"success": true})
125
+
}
126
+
127
+
type QuickBookmarkRequest struct {
128
+
URL string `json:"url"`
129
+
Title string `json:"title,omitempty"`
130
+
Description string `json:"description,omitempty"`
131
+
}
132
+
133
+
func (h *APIKeyHandler) QuickBookmark(w http.ResponseWriter, r *http.Request) {
134
+
apiKey, err := h.authenticateAPIKey(r)
135
+
if err != nil {
136
+
http.Error(w, err.Error(), http.StatusUnauthorized)
137
+
return
138
+
}
139
+
140
+
var req QuickBookmarkRequest
141
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
142
+
http.Error(w, "Invalid request body", http.StatusBadRequest)
143
+
return
144
+
}
145
+
146
+
if req.URL == "" {
147
+
http.Error(w, "URL is required", http.StatusBadRequest)
148
+
return
149
+
}
150
+
151
+
session, err := h.getSessionByDID(apiKey.OwnerDID)
152
+
if err != nil {
153
+
http.Error(w, "User session not found. Please log in to margin.at first.", http.StatusUnauthorized)
154
+
return
155
+
}
156
+
157
+
urlHash := db.HashURL(req.URL)
158
+
record := xrpc.NewBookmarkRecord(req.URL, urlHash, req.Title, req.Description)
159
+
160
+
var result *xrpc.CreateRecordOutput
161
+
err = h.refresher.ExecuteWithAutoRefresh(r, session, func(client *xrpc.Client, did string) error {
162
+
var createErr error
163
+
result, createErr = client.CreateRecord(r.Context(), did, xrpc.CollectionBookmark, record)
164
+
return createErr
165
+
})
166
+
if err != nil {
167
+
http.Error(w, "Failed to create bookmark: "+err.Error(), http.StatusInternalServerError)
168
+
return
169
+
}
170
+
171
+
h.db.UpdateAPIKeyLastUsed(apiKey.ID)
172
+
173
+
var titlePtr, descPtr *string
174
+
if req.Title != "" {
175
+
titlePtr = &req.Title
176
+
}
177
+
if req.Description != "" {
178
+
descPtr = &req.Description
179
+
}
180
+
181
+
cid := result.CID
182
+
bookmark := &db.Bookmark{
183
+
URI: result.URI,
184
+
AuthorDID: apiKey.OwnerDID,
185
+
Source: req.URL,
186
+
SourceHash: urlHash,
187
+
Title: titlePtr,
188
+
Description: descPtr,
189
+
CreatedAt: time.Now(),
190
+
IndexedAt: time.Now(),
191
+
CID: &cid,
192
+
}
193
+
h.db.CreateBookmark(bookmark)
194
+
195
+
w.Header().Set("Content-Type", "application/json")
196
+
json.NewEncoder(w).Encode(map[string]string{
197
+
"uri": result.URI,
198
+
"cid": result.CID,
199
+
"message": "Bookmark created successfully",
200
+
})
201
+
}
202
+
203
+
type QuickAnnotationRequest struct {
204
+
URL string `json:"url"`
205
+
Text string `json:"text"`
206
+
}
207
+
208
+
func (h *APIKeyHandler) QuickAnnotation(w http.ResponseWriter, r *http.Request) {
209
+
apiKey, err := h.authenticateAPIKey(r)
210
+
if err != nil {
211
+
http.Error(w, err.Error(), http.StatusUnauthorized)
212
+
return
213
+
}
214
+
215
+
var req QuickAnnotationRequest
216
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
217
+
http.Error(w, "Invalid request body", http.StatusBadRequest)
218
+
return
219
+
}
220
+
221
+
if req.URL == "" || req.Text == "" {
222
+
http.Error(w, "URL and text are required", http.StatusBadRequest)
223
+
return
224
+
}
225
+
226
+
session, err := h.getSessionByDID(apiKey.OwnerDID)
227
+
if err != nil {
228
+
http.Error(w, "User session not found. Please log in to margin.at first.", http.StatusUnauthorized)
229
+
return
230
+
}
231
+
232
+
urlHash := db.HashURL(req.URL)
233
+
record := xrpc.NewAnnotationRecord(req.URL, urlHash, req.Text, nil, "")
234
+
235
+
var result *xrpc.CreateRecordOutput
236
+
err = h.refresher.ExecuteWithAutoRefresh(r, session, func(client *xrpc.Client, did string) error {
237
+
var createErr error
238
+
result, createErr = client.CreateRecord(r.Context(), did, xrpc.CollectionAnnotation, record)
239
+
return createErr
240
+
})
241
+
if err != nil {
242
+
http.Error(w, "Failed to create annotation: "+err.Error(), http.StatusInternalServerError)
243
+
return
244
+
}
245
+
246
+
h.db.UpdateAPIKeyLastUsed(apiKey.ID)
247
+
248
+
bodyValue := req.Text
249
+
annotation := &db.Annotation{
250
+
URI: result.URI,
251
+
AuthorDID: apiKey.OwnerDID,
252
+
Motivation: "commenting",
253
+
BodyValue: &bodyValue,
254
+
TargetSource: req.URL,
255
+
TargetHash: urlHash,
256
+
CreatedAt: time.Now(),
257
+
IndexedAt: time.Now(),
258
+
CID: &result.CID,
259
+
}
260
+
h.db.CreateAnnotation(annotation)
261
+
262
+
w.Header().Set("Content-Type", "application/json")
263
+
json.NewEncoder(w).Encode(map[string]string{
264
+
"uri": result.URI,
265
+
"cid": result.CID,
266
+
"message": "Annotation created successfully",
267
+
})
268
+
}
269
+
270
+
type QuickHighlightRequest struct {
271
+
URL string `json:"url"`
272
+
Selector interface{} `json:"selector"`
273
+
Color string `json:"color,omitempty"`
274
+
}
275
+
276
+
func (h *APIKeyHandler) QuickHighlight(w http.ResponseWriter, r *http.Request) {
277
+
apiKey, err := h.authenticateAPIKey(r)
278
+
if err != nil {
279
+
http.Error(w, err.Error(), http.StatusUnauthorized)
280
+
return
281
+
}
282
+
283
+
var req QuickHighlightRequest
284
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
285
+
http.Error(w, "Invalid request body", http.StatusBadRequest)
286
+
return
287
+
}
288
+
289
+
if req.URL == "" || req.Selector == nil {
290
+
http.Error(w, "URL and selector are required", http.StatusBadRequest)
291
+
return
292
+
}
293
+
294
+
session, err := h.getSessionByDID(apiKey.OwnerDID)
295
+
if err != nil {
296
+
http.Error(w, "User session not found. Please log in to margin.at first.", http.StatusUnauthorized)
297
+
return
298
+
}
299
+
300
+
urlHash := db.HashURL(req.URL)
301
+
color := req.Color
302
+
if color == "" {
303
+
color = "yellow"
304
+
}
305
+
306
+
record := xrpc.NewHighlightRecord(req.URL, urlHash, req.Selector, color, nil)
307
+
308
+
var result *xrpc.CreateRecordOutput
309
+
err = h.refresher.ExecuteWithAutoRefresh(r, session, func(client *xrpc.Client, did string) error {
310
+
var createErr error
311
+
result, createErr = client.CreateRecord(r.Context(), did, xrpc.CollectionHighlight, record)
312
+
return createErr
313
+
})
314
+
if err != nil {
315
+
http.Error(w, "Failed to create highlight: "+err.Error(), http.StatusInternalServerError)
316
+
return
317
+
}
318
+
319
+
h.db.UpdateAPIKeyLastUsed(apiKey.ID)
320
+
321
+
selectorJSON, _ := json.Marshal(req.Selector)
322
+
selectorStr := string(selectorJSON)
323
+
colorPtr := &color
324
+
325
+
highlight := &db.Highlight{
326
+
URI: result.URI,
327
+
AuthorDID: apiKey.OwnerDID,
328
+
TargetSource: req.URL,
329
+
TargetHash: urlHash,
330
+
SelectorJSON: &selectorStr,
331
+
Color: colorPtr,
332
+
CreatedAt: time.Now(),
333
+
IndexedAt: time.Now(),
334
+
CID: &result.CID,
335
+
}
336
+
if err := h.db.CreateHighlight(highlight); err != nil {
337
+
fmt.Printf("Warning: failed to index highlight in local DB: %v\n", err)
338
+
}
339
+
340
+
w.Header().Set("Content-Type", "application/json")
341
+
json.NewEncoder(w).Encode(map[string]string{
342
+
"uri": result.URI,
343
+
"cid": result.CID,
344
+
"message": "Highlight created successfully",
345
+
})
346
+
}
347
+
348
+
func (h *APIKeyHandler) authenticateAPIKey(r *http.Request) (*db.APIKey, error) {
349
+
auth := r.Header.Get("Authorization")
350
+
if auth == "" {
351
+
return nil, fmt.Errorf("missing Authorization header")
352
+
}
353
+
354
+
if !strings.HasPrefix(auth, "Bearer ") {
355
+
return nil, fmt.Errorf("invalid Authorization format, expected 'Bearer <key>'")
356
+
}
357
+
358
+
rawKey := strings.TrimPrefix(auth, "Bearer ")
359
+
keyHash := hashAPIKey(rawKey)
360
+
361
+
apiKey, err := h.db.GetAPIKeyByHash(keyHash)
362
+
if err != nil {
363
+
return nil, fmt.Errorf("invalid API key")
364
+
}
365
+
366
+
return apiKey, nil
367
+
}
368
+
369
+
func (h *APIKeyHandler) getSessionByDID(did string) (*SessionData, error) {
370
+
rows, err := h.db.Query(h.db.Rebind(`
371
+
SELECT id, did, handle, access_token, refresh_token, COALESCE(dpop_key, '')
372
+
FROM sessions
373
+
WHERE did = ? AND expires_at > ?
374
+
ORDER BY created_at DESC
375
+
LIMIT 1
376
+
`), did, time.Now())
377
+
if err != nil {
378
+
return nil, err
379
+
}
380
+
defer rows.Close()
381
+
382
+
if !rows.Next() {
383
+
return nil, fmt.Errorf("no active session")
384
+
}
385
+
386
+
var sessionID, sessDID, handle, accessToken, refreshToken, dpopKeyStr string
387
+
if err := rows.Scan(&sessionID, &sessDID, &handle, &accessToken, &refreshToken, &dpopKeyStr); err != nil {
388
+
return nil, err
389
+
}
390
+
391
+
block, _ := pem.Decode([]byte(dpopKeyStr))
392
+
if block == nil {
393
+
return nil, fmt.Errorf("invalid session DPoP key")
394
+
}
395
+
dpopKey, err := x509.ParseECPrivateKey(block.Bytes)
396
+
if err != nil {
397
+
return nil, fmt.Errorf("invalid session DPoP key: %w", err)
398
+
}
399
+
400
+
pds, err := resolveDIDToPDS(sessDID)
401
+
if err != nil {
402
+
return nil, fmt.Errorf("failed to resolve PDS: %w", err)
403
+
}
404
+
if pds == "" {
405
+
return nil, fmt.Errorf("PDS not found for DID: %s", sessDID)
406
+
}
407
+
408
+
return &SessionData{
409
+
ID: sessionID,
410
+
DID: sessDID,
411
+
Handle: handle,
412
+
AccessToken: accessToken,
413
+
RefreshToken: refreshToken,
414
+
DPoPKey: dpopKey,
415
+
PDS: pds,
416
+
}, nil
417
+
}
418
+
419
+
func generateAPIKey() string {
420
+
b := make([]byte, 32)
421
+
rand.Read(b)
422
+
return "mk_" + hex.EncodeToString(b)
423
+
}
424
+
425
+
func generateKeyID() string {
426
+
b := make([]byte, 16)
427
+
rand.Read(b)
428
+
return hex.EncodeToString(b)
429
+
}
430
+
431
+
func hashAPIKey(key string) string {
432
+
h := sha256.New()
433
+
h.Write([]byte(key))
434
+
return hex.EncodeToString(h.Sum(nil))
435
+
}
+117
backend/internal/api/avatar.go
+117
backend/internal/api/avatar.go
···
1
+
package api
2
+
3
+
import (
4
+
"encoding/json"
5
+
"io"
6
+
"net/http"
7
+
"net/url"
8
+
"os"
9
+
"sync"
10
+
"time"
11
+
12
+
"github.com/go-chi/chi/v5"
13
+
)
14
+
15
+
type avatarCache struct {
16
+
url string
17
+
fetchedAt time.Time
18
+
}
19
+
20
+
var (
21
+
avatarCacheMu sync.RWMutex
22
+
avatarCacheMap = make(map[string]avatarCache)
23
+
avatarCacheTTL = 5 * time.Minute
24
+
)
25
+
26
+
func (h *Handler) HandleAvatarProxy(w http.ResponseWriter, r *http.Request) {
27
+
did := chi.URLParam(r, "did")
28
+
if did == "" {
29
+
http.Error(w, "DID required", http.StatusBadRequest)
30
+
return
31
+
}
32
+
33
+
if decoded, err := url.QueryUnescape(did); err == nil {
34
+
did = decoded
35
+
}
36
+
37
+
avatarURL := getAvatarURL(did)
38
+
if avatarURL == "" {
39
+
http.Error(w, "Avatar not found", http.StatusNotFound)
40
+
return
41
+
}
42
+
43
+
client := &http.Client{Timeout: 10 * time.Second}
44
+
resp, err := client.Get(avatarURL)
45
+
if err != nil {
46
+
http.Error(w, "Failed to fetch avatar", http.StatusBadGateway)
47
+
return
48
+
}
49
+
defer resp.Body.Close()
50
+
51
+
if resp.StatusCode != http.StatusOK {
52
+
http.Error(w, "Avatar not available", http.StatusNotFound)
53
+
return
54
+
}
55
+
56
+
contentType := resp.Header.Get("Content-Type")
57
+
if contentType == "" {
58
+
contentType = "image/jpeg"
59
+
}
60
+
61
+
w.Header().Set("Content-Type", contentType)
62
+
w.Header().Set("Cache-Control", "public, max-age=3600")
63
+
w.Header().Set("Access-Control-Allow-Origin", "*")
64
+
65
+
io.Copy(w, resp.Body)
66
+
}
67
+
68
+
func getAvatarURL(did string) string {
69
+
avatarCacheMu.RLock()
70
+
if cached, ok := avatarCacheMap[did]; ok && time.Since(cached.fetchedAt) < avatarCacheTTL {
71
+
avatarCacheMu.RUnlock()
72
+
return cached.url
73
+
}
74
+
avatarCacheMu.RUnlock()
75
+
76
+
q := url.Values{}
77
+
q.Add("actor", did)
78
+
79
+
resp, err := http.Get("https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?" + q.Encode())
80
+
if err != nil {
81
+
return ""
82
+
}
83
+
defer resp.Body.Close()
84
+
85
+
if resp.StatusCode != 200 {
86
+
return ""
87
+
}
88
+
89
+
var profile struct {
90
+
Avatar string `json:"avatar"`
91
+
}
92
+
if err := json.NewDecoder(resp.Body).Decode(&profile); err != nil {
93
+
return ""
94
+
}
95
+
96
+
avatarCacheMu.Lock()
97
+
avatarCacheMap[did] = avatarCache{
98
+
url: profile.Avatar,
99
+
fetchedAt: time.Now(),
100
+
}
101
+
avatarCacheMu.Unlock()
102
+
103
+
return profile.Avatar
104
+
}
105
+
106
+
func getProxiedAvatarURL(did, originalURL string) string {
107
+
if originalURL == "" {
108
+
return ""
109
+
}
110
+
111
+
baseURL := os.Getenv("BASE_URL")
112
+
if baseURL == "" {
113
+
return originalURL
114
+
}
115
+
116
+
return baseURL + "/api/avatar/" + url.PathEscape(did)
117
+
}
+16
-1
backend/internal/api/handler.go
+16
-1
backend/internal/api/handler.go
···
19
19
db *db.DB
20
20
annotationService *AnnotationService
21
21
refresher *TokenRefresher
22
+
apiKeys *APIKeyHandler
22
23
}
23
24
24
25
func NewHandler(database *db.DB, annotationService *AnnotationService, refresher *TokenRefresher) *Handler {
25
-
return &Handler{db: database, annotationService: annotationService, refresher: refresher}
26
+
return &Handler{
27
+
db: database,
28
+
annotationService: annotationService,
29
+
refresher: refresher,
30
+
apiKeys: NewAPIKeyHandler(database, refresher),
31
+
}
26
32
}
27
33
28
34
func (h *Handler) RegisterRoutes(r chi.Router) {
···
64
70
r.Get("/notifications", h.GetNotifications)
65
71
r.Get("/notifications/count", h.GetUnreadNotificationCount)
66
72
r.Post("/notifications/read", h.MarkNotificationsRead)
73
+
r.Get("/avatar/{did}", h.HandleAvatarProxy)
74
+
75
+
r.Post("/keys", h.apiKeys.CreateKey)
76
+
r.Get("/keys", h.apiKeys.ListKeys)
77
+
r.Delete("/keys/{id}", h.apiKeys.DeleteKey)
78
+
79
+
r.Post("/quick/bookmark", h.apiKeys.QuickBookmark)
80
+
r.Post("/quick/annotation", h.apiKeys.QuickAnnotation)
81
+
r.Post("/quick/highlight", h.apiKeys.QuickHighlight)
67
82
})
68
83
}
69
84
+1
-1
backend/internal/api/hydration.go
+1
-1
backend/internal/api/hydration.go
+10
-4
backend/internal/api/token_refresh.go
+10
-4
backend/internal/api/token_refresh.go
···
52
52
}
53
53
54
54
type SessionData struct {
55
+
ID string
55
56
DID string
56
57
Handle string
57
58
AccessToken string
···
94
95
}
95
96
96
97
return &SessionData{
98
+
ID: sessionID,
97
99
DID: did,
98
100
Handle: handle,
99
101
AccessToken: accessToken,
···
104
106
}
105
107
106
108
func (tr *TokenRefresher) RefreshSessionToken(r *http.Request, session *SessionData) (*SessionData, error) {
107
-
cookie, err := r.Cookie("margin_session")
108
-
if err != nil {
109
-
return nil, fmt.Errorf("not authenticated")
109
+
if session.ID == "" {
110
+
return nil, fmt.Errorf("invalid session ID")
110
111
}
111
112
112
113
oauthClient := tr.getOAuthClient(r)
···
138
139
139
140
expiresAt := time.Now().Add(7 * 24 * time.Hour)
140
141
if err := tr.db.SaveSession(
141
-
cookie.Value,
142
+
session.ID,
142
143
session.DID,
143
144
session.Handle,
144
145
tokenResp.AccessToken,
···
152
153
log.Printf("Successfully refreshed token for user %s", session.Handle)
153
154
154
155
return &SessionData{
156
+
ID: session.ID,
155
157
DID: session.DID,
156
158
Handle: session.Handle,
157
159
AccessToken: tokenResp.AccessToken,
···
196
198
client = xrpc.NewClient(newSession.PDS, newSession.AccessToken, newSession.DPoPKey)
197
199
return fn(client, newSession.DID)
198
200
}
201
+
202
+
func (tr *TokenRefresher) CreateClientFromSession(session *SessionData) *xrpc.Client {
203
+
return xrpc.NewClient(session.PDS, session.AccessToken, session.DPoPKey)
204
+
}
+25
-1
backend/internal/db/db.go
+25
-1
backend/internal/db/db.go
···
120
120
ReadAt *time.Time `json:"readAt,omitempty"`
121
121
}
122
122
123
+
type APIKey struct {
124
+
ID string `json:"id"`
125
+
OwnerDID string `json:"ownerDid"`
126
+
Name string `json:"name"`
127
+
KeyHash string `json:"-"`
128
+
CreatedAt time.Time `json:"createdAt"`
129
+
LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
130
+
}
131
+
123
132
func New(dsn string) (*DB, error) {
124
133
driver := "sqlite3"
125
134
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
···
296
305
db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_recipient ON notifications(recipient_did)`)
297
306
db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at DESC)`)
298
307
308
+
db.Exec(`CREATE TABLE IF NOT EXISTS api_keys (
309
+
id TEXT PRIMARY KEY,
310
+
owner_did TEXT NOT NULL,
311
+
name TEXT NOT NULL,
312
+
key_hash TEXT NOT NULL,
313
+
created_at ` + dateType + ` NOT NULL,
314
+
last_used_at ` + dateType + `
315
+
)`)
316
+
db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_owner ON api_keys(owner_did)`)
317
+
db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash)`)
318
+
299
319
db.runMigrations()
300
320
301
321
db.Exec(`CREATE TABLE IF NOT EXISTS cursors (
302
322
id TEXT PRIMARY KEY,
303
-
last_cursor INTEGER NOT NULL,
323
+
last_cursor BIGINT NOT NULL,
304
324
updated_at ` + dateType + ` NOT NULL
305
325
)`)
306
326
···
353
373
db.Exec(`UPDATE annotations SET body_value = text WHERE body_value IS NULL AND text IS NOT NULL`)
354
374
db.Exec(`UPDATE annotations SET target_title = title WHERE target_title IS NULL AND title IS NOT NULL`)
355
375
db.Exec(`UPDATE annotations SET motivation = 'commenting' WHERE motivation IS NULL`)
376
+
377
+
if db.driver == "postgres" {
378
+
db.Exec(`ALTER TABLE cursors ALTER COLUMN last_cursor TYPE BIGINT`)
379
+
}
356
380
}
357
381
358
382
func (db *DB) Close() error {
+57
backend/internal/db/queries.go
+57
backend/internal/db/queries.go
···
825
825
}
826
826
827
827
normalized := strings.ToLower(parsed.Host) + parsed.Path
828
+
if parsed.RawQuery != "" {
829
+
normalized += "?" + parsed.RawQuery
830
+
}
828
831
normalized = strings.TrimSuffix(normalized, "/")
829
832
830
833
return hashString(normalized)
···
907
910
908
911
return "", fmt.Errorf("uri not found or no author")
909
912
}
913
+
914
+
func (db *DB) CreateAPIKey(key *APIKey) error {
915
+
_, err := db.Exec(db.Rebind(`
916
+
INSERT INTO api_keys (id, owner_did, name, key_hash, created_at)
917
+
VALUES (?, ?, ?, ?, ?)
918
+
`), key.ID, key.OwnerDID, key.Name, key.KeyHash, key.CreatedAt)
919
+
return err
920
+
}
921
+
922
+
func (db *DB) GetAPIKeysByOwner(ownerDID string) ([]APIKey, error) {
923
+
rows, err := db.Query(db.Rebind(`
924
+
SELECT id, owner_did, name, key_hash, created_at, last_used_at
925
+
FROM api_keys
926
+
WHERE owner_did = ?
927
+
ORDER BY created_at DESC
928
+
`), ownerDID)
929
+
if err != nil {
930
+
return nil, err
931
+
}
932
+
defer rows.Close()
933
+
934
+
var keys []APIKey
935
+
for rows.Next() {
936
+
var k APIKey
937
+
if err := rows.Scan(&k.ID, &k.OwnerDID, &k.Name, &k.KeyHash, &k.CreatedAt, &k.LastUsedAt); err != nil {
938
+
return nil, err
939
+
}
940
+
keys = append(keys, k)
941
+
}
942
+
return keys, nil
943
+
}
944
+
945
+
func (db *DB) GetAPIKeyByHash(keyHash string) (*APIKey, error) {
946
+
var k APIKey
947
+
err := db.QueryRow(db.Rebind(`
948
+
SELECT id, owner_did, name, key_hash, created_at, last_used_at
949
+
FROM api_keys
950
+
WHERE key_hash = ?
951
+
`), keyHash).Scan(&k.ID, &k.OwnerDID, &k.Name, &k.KeyHash, &k.CreatedAt, &k.LastUsedAt)
952
+
if err != nil {
953
+
return nil, err
954
+
}
955
+
return &k, nil
956
+
}
957
+
958
+
func (db *DB) DeleteAPIKey(id, ownerDID string) error {
959
+
_, err := db.Exec(db.Rebind(`DELETE FROM api_keys WHERE id = ? AND owner_did = ?`), id, ownerDID)
960
+
return err
961
+
}
962
+
963
+
func (db *DB) UpdateAPIKeyLastUsed(id string) error {
964
+
_, err := db.Exec(db.Rebind(`UPDATE api_keys SET last_used_at = ? WHERE id = ?`), time.Now(), id)
965
+
return err
966
+
}
+236
-84
backend/internal/firehose/ingester.go
+236
-84
backend/internal/firehose/ingester.go
···
3
3
import (
4
4
"bytes"
5
5
"context"
6
+
"encoding/binary"
6
7
"encoding/json"
7
8
"fmt"
8
9
"io"
9
10
"log"
10
-
"net/http"
11
-
"strings"
12
11
"time"
12
+
13
+
"github.com/fxamacker/cbor/v2"
14
+
"github.com/gorilla/websocket"
15
+
"github.com/ipfs/go-cid"
13
16
14
17
"margin.at/internal/db"
15
18
)
···
56
59
return
57
60
default:
58
61
if err := i.subscribe(ctx); err != nil {
62
+
log.Printf("Firehose error: %v, reconnecting in 5s...", err)
59
63
if ctx.Err() != nil {
60
64
return
61
65
}
62
-
time.Sleep(30 * time.Second)
66
+
time.Sleep(5 * time.Second)
63
67
}
64
68
}
65
69
}
66
70
}
67
71
72
+
type FrameHeader struct {
73
+
Op int `cbor:"op"`
74
+
T string `cbor:"t"`
75
+
}
76
+
type Commit struct {
77
+
Repo string `cbor:"repo"`
78
+
Rev string `cbor:"rev"`
79
+
Seq int64 `cbor:"seq"`
80
+
Prev *cid.Cid `cbor:"prev"`
81
+
Time string `cbor:"time"`
82
+
Blocks []byte `cbor:"blocks"`
83
+
Ops []RepoOp `cbor:"ops"`
84
+
}
85
+
86
+
type RepoOp struct {
87
+
Action string `cbor:"action"`
88
+
Path string `cbor:"path"`
89
+
Cid *cid.Cid `cbor:"cid"`
90
+
}
91
+
68
92
func (i *Ingester) subscribe(ctx context.Context) error {
69
93
cursor := i.getLastCursor()
70
94
···
73
97
url = fmt.Sprintf("%s?cursor=%d", RelayURL, cursor)
74
98
}
75
99
76
-
req, err := http.NewRequestWithContext(ctx, "GET", strings.Replace(url, "wss://", "https://", 1), nil)
77
-
if err != nil {
78
-
return err
79
-
}
100
+
log.Printf("Connecting to firehose: %s", url)
80
101
81
-
resp, err := http.DefaultClient.Do(req)
102
+
conn, _, err := websocket.DefaultDialer.DialContext(ctx, url, nil)
82
103
if err != nil {
83
-
return err
104
+
return fmt.Errorf("websocket dial failed: %w", err)
84
105
}
85
-
defer resp.Body.Close()
106
+
defer conn.Close()
86
107
87
-
if resp.StatusCode != 200 {
88
-
body, _ := io.ReadAll(resp.Body)
89
-
return fmt.Errorf("firehose returned %d: %s", resp.StatusCode, string(body))
90
-
}
108
+
log.Printf("Connected to firehose")
91
109
92
-
decoder := json.NewDecoder(resp.Body)
93
110
for {
94
111
select {
95
112
case <-ctx.Done():
···
97
114
default:
98
115
}
99
116
100
-
var event FirehoseEvent
101
-
if err := decoder.Decode(&event); err != nil {
102
-
if err == io.EOF {
103
-
return nil
104
-
}
105
-
return err
117
+
_, message, err := conn.ReadMessage()
118
+
if err != nil {
119
+
return fmt.Errorf("websocket read failed: %w", err)
106
120
}
107
121
108
-
i.handleEvent(&event)
122
+
i.handleMessage(message)
109
123
}
110
124
}
111
125
112
-
type FirehoseEvent struct {
113
-
Repo string `json:"repo"`
114
-
Collection string `json:"collection"`
115
-
Rkey string `json:"rkey"`
116
-
Record json.RawMessage `json:"record"`
117
-
Operation string `json:"operation"`
118
-
Cursor int64 `json:"cursor"`
119
-
}
126
+
func (i *Ingester) handleMessage(data []byte) {
127
+
reader := bytes.NewReader(data)
120
128
121
-
func (i *Ingester) handleEvent(event *FirehoseEvent) {
122
-
uri := fmt.Sprintf("at://%s/%s/%s", event.Repo, event.Collection, event.Rkey)
129
+
var header FrameHeader
130
+
decoder := cbor.NewDecoder(reader)
131
+
if err := decoder.Decode(&header); err != nil {
132
+
return
133
+
}
123
134
124
-
switch event.Collection {
125
-
case CollectionAnnotation:
126
-
switch event.Operation {
127
-
case "create", "update":
128
-
i.handleAnnotation(event)
129
-
case "delete":
130
-
i.db.DeleteAnnotation(uri)
135
+
if header.Op != 1 {
136
+
return
137
+
}
138
+
139
+
if header.T != "#commit" {
140
+
return
141
+
}
142
+
143
+
var commit Commit
144
+
if err := decoder.Decode(&commit); err != nil {
145
+
return
146
+
}
147
+
148
+
for _, op := range commit.Ops {
149
+
collection, rkey := parseOpPath(op.Path)
150
+
if !isMarginCollection(collection) {
151
+
continue
131
152
}
132
-
case CollectionHighlight:
133
-
switch event.Operation {
153
+
154
+
uri := fmt.Sprintf("at://%s/%s/%s", commit.Repo, collection, rkey)
155
+
156
+
switch op.Action {
134
157
case "create", "update":
135
-
i.handleHighlight(event)
158
+
if op.Cid != nil && len(commit.Blocks) > 0 {
159
+
record := extractRecord(commit.Blocks, *op.Cid)
160
+
if record != nil {
161
+
i.handleRecord(commit.Repo, collection, rkey, record, commit.Seq)
162
+
}
163
+
}
136
164
case "delete":
137
-
i.db.DeleteHighlight(uri)
165
+
i.handleDelete(collection, uri)
138
166
}
139
-
case CollectionBookmark:
140
-
switch event.Operation {
141
-
case "create", "update":
142
-
i.handleBookmark(event)
143
-
case "delete":
144
-
i.db.DeleteBookmark(uri)
167
+
}
168
+
169
+
if commit.Seq > 0 {
170
+
if err := i.db.SetCursor("firehose_cursor", commit.Seq); err != nil {
171
+
log.Printf("Failed to save cursor: %v", err)
145
172
}
146
-
case CollectionReply:
147
-
switch event.Operation {
148
-
case "create", "update":
149
-
i.handleReply(event)
150
-
case "delete":
151
-
i.db.DeleteReply(uri)
173
+
}
174
+
}
175
+
176
+
func parseOpPath(path string) (collection, rkey string) {
177
+
for i := len(path) - 1; i >= 0; i-- {
178
+
if path[i] == '/' {
179
+
return path[:i], path[i+1:]
152
180
}
153
-
case CollectionLike:
154
-
switch event.Operation {
155
-
case "create":
156
-
i.handleLike(event)
157
-
case "delete":
158
-
i.db.DeleteLike(uri)
181
+
}
182
+
return path, ""
183
+
}
184
+
185
+
func isMarginCollection(collection string) bool {
186
+
switch collection {
187
+
case CollectionAnnotation, CollectionHighlight, CollectionBookmark,
188
+
CollectionReply, CollectionLike, CollectionCollection, CollectionCollectionItem:
189
+
return true
190
+
}
191
+
return false
192
+
}
193
+
194
+
func extractRecord(blocks []byte, targetCid cid.Cid) map[string]interface{} {
195
+
reader := bytes.NewReader(blocks)
196
+
197
+
headerLen, err := binary.ReadUvarint(reader)
198
+
if err != nil {
199
+
return nil
200
+
}
201
+
reader.Seek(int64(headerLen), io.SeekCurrent)
202
+
203
+
for reader.Len() > 0 {
204
+
blockLen, err := binary.ReadUvarint(reader)
205
+
if err != nil {
206
+
break
159
207
}
160
-
case CollectionCollection:
161
-
switch event.Operation {
162
-
case "create", "update":
163
-
i.handleCollection(event)
164
-
case "delete":
165
-
i.db.DeleteCollection(uri)
208
+
209
+
blockData := make([]byte, blockLen)
210
+
if _, err := io.ReadFull(reader, blockData); err != nil {
211
+
break
166
212
}
167
-
case CollectionCollectionItem:
168
-
switch event.Operation {
169
-
case "create", "update":
170
-
i.handleCollectionItem(event)
171
-
case "delete":
172
-
i.db.RemoveFromCollection(uri)
213
+
214
+
blockCid, cidLen, err := parseCidFromBlock(blockData)
215
+
if err != nil {
216
+
continue
217
+
}
218
+
219
+
if blockCid.Equals(targetCid) {
220
+
var record map[string]interface{}
221
+
if err := cbor.Unmarshal(blockData[cidLen:], &record); err != nil {
222
+
return nil
223
+
}
224
+
return record
173
225
}
174
226
}
175
227
176
-
if event.Cursor > 0 {
177
-
if err := i.db.SetCursor("firehose_cursor", event.Cursor); err != nil {
178
-
log.Printf("Failed to save cursor: %v", err)
228
+
return nil
229
+
}
230
+
231
+
func parseCidFromBlock(data []byte) (cid.Cid, int, error) {
232
+
if len(data) < 2 {
233
+
return cid.Cid{}, 0, fmt.Errorf("data too short")
234
+
}
235
+
version, n1 := binary.Uvarint(data)
236
+
if n1 <= 0 {
237
+
return cid.Cid{}, 0, fmt.Errorf("invalid version varint")
238
+
}
239
+
240
+
if version == 1 {
241
+
codec, n2 := binary.Uvarint(data[n1:])
242
+
if n2 <= 0 {
243
+
return cid.Cid{}, 0, fmt.Errorf("invalid codec varint")
179
244
}
245
+
246
+
mhStart := n1 + n2
247
+
hashType, n3 := binary.Uvarint(data[mhStart:])
248
+
if n3 <= 0 {
249
+
return cid.Cid{}, 0, fmt.Errorf("invalid hash type varint")
250
+
}
251
+
252
+
hashLen, n4 := binary.Uvarint(data[mhStart+n3:])
253
+
if n4 <= 0 {
254
+
return cid.Cid{}, 0, fmt.Errorf("invalid hash length varint")
255
+
}
256
+
257
+
totalCidLen := mhStart + n3 + n4 + int(hashLen)
258
+
259
+
c, err := cid.Cast(data[:totalCidLen])
260
+
if err != nil {
261
+
return cid.Cid{}, 0, err
262
+
}
263
+
264
+
_ = codec
265
+
_ = hashType
266
+
267
+
return c, totalCidLen, nil
180
268
}
269
+
270
+
return cid.Cid{}, 0, fmt.Errorf("unsupported CID version")
181
271
}
182
272
183
-
func (i *Ingester) handleAnnotation(event *FirehoseEvent) {
273
+
func (i *Ingester) handleDelete(collection, uri string) {
274
+
switch collection {
275
+
case CollectionAnnotation:
276
+
i.db.DeleteAnnotation(uri)
277
+
case CollectionHighlight:
278
+
i.db.DeleteHighlight(uri)
279
+
case CollectionBookmark:
280
+
i.db.DeleteBookmark(uri)
281
+
case CollectionReply:
282
+
i.db.DeleteReply(uri)
283
+
case CollectionLike:
284
+
i.db.DeleteLike(uri)
285
+
case CollectionCollection:
286
+
i.db.DeleteCollection(uri)
287
+
case CollectionCollectionItem:
288
+
i.db.RemoveFromCollection(uri)
289
+
}
290
+
}
184
291
292
+
func (i *Ingester) handleRecord(repo, collection, rkey string, record map[string]interface{}, seq int64) {
293
+
_ = fmt.Sprintf("at://%s/%s/%s", repo, collection, rkey)
294
+
295
+
recordJSON, err := json.Marshal(record)
296
+
if err != nil {
297
+
return
298
+
}
299
+
300
+
event := &FirehoseEvent{
301
+
Repo: repo,
302
+
Collection: collection,
303
+
Rkey: rkey,
304
+
Record: recordJSON,
305
+
Operation: "create",
306
+
Cursor: seq,
307
+
}
308
+
309
+
switch collection {
310
+
case CollectionAnnotation:
311
+
i.handleAnnotation(event)
312
+
case CollectionHighlight:
313
+
i.handleHighlight(event)
314
+
case CollectionBookmark:
315
+
i.handleBookmark(event)
316
+
case CollectionReply:
317
+
i.handleReply(event)
318
+
case CollectionLike:
319
+
i.handleLike(event)
320
+
case CollectionCollection:
321
+
i.handleCollection(event)
322
+
case CollectionCollectionItem:
323
+
i.handleCollectionItem(event)
324
+
}
325
+
}
326
+
327
+
type FirehoseEvent struct {
328
+
Repo string `json:"repo"`
329
+
Collection string `json:"collection"`
330
+
Rkey string `json:"rkey"`
331
+
Record json.RawMessage `json:"record"`
332
+
Operation string `json:"operation"`
333
+
Cursor int64 `json:"cursor"`
334
+
}
335
+
336
+
func (i *Ingester) handleAnnotation(event *FirehoseEvent) {
185
337
var record struct {
186
338
Motivation string `json:"motivation"`
187
339
Body struct {
···
205
357
Title string `json:"title"`
206
358
}
207
359
208
-
if err := json.NewDecoder(bytes.NewReader(event.Record)).Decode(&record); err != nil {
360
+
if err := json.Unmarshal(event.Record, &record); err != nil {
209
361
return
210
362
}
211
363
···
302
454
CreatedAt string `json:"createdAt"`
303
455
}
304
456
305
-
if err := json.NewDecoder(bytes.NewReader(event.Record)).Decode(&record); err != nil {
457
+
if err := json.Unmarshal(event.Record, &record); err != nil {
306
458
return
307
459
}
308
460
···
334
486
CreatedAt string `json:"createdAt"`
335
487
}
336
488
337
-
if err := json.NewDecoder(bytes.NewReader(event.Record)).Decode(&record); err != nil {
489
+
if err := json.Unmarshal(event.Record, &record); err != nil {
338
490
return
339
491
}
340
492
···
369
521
CreatedAt string `json:"createdAt"`
370
522
}
371
523
372
-
if err := json.NewDecoder(bytes.NewReader(event.Record)).Decode(&record); err != nil {
524
+
if err := json.Unmarshal(event.Record, &record); err != nil {
373
525
return
374
526
}
375
527
···
432
584
CreatedAt string `json:"createdAt"`
433
585
}
434
586
435
-
if err := json.NewDecoder(bytes.NewReader(event.Record)).Decode(&record); err != nil {
587
+
if err := json.Unmarshal(event.Record, &record); err != nil {
436
588
return
437
589
}
438
590
···
488
640
CreatedAt string `json:"createdAt"`
489
641
}
490
642
491
-
if err := json.NewDecoder(bytes.NewReader(event.Record)).Decode(&record); err != nil {
643
+
if err := json.Unmarshal(event.Record, &record); err != nil {
492
644
return
493
645
}
494
646
···
532
684
CreatedAt string `json:"createdAt"`
533
685
}
534
686
535
-
if err := json.NewDecoder(bytes.NewReader(event.Record)).Decode(&record); err != nil {
687
+
if err := json.Unmarshal(event.Record, &record); err != nil {
536
688
return
537
689
}
538
690
+2
-1
docker-compose.yml
+2
-1
docker-compose.yml
···
9
9
- OAUTH_KEY_PATH=/data/oauth_private_key.pem
10
10
env_file:
11
11
- .env
12
+
volumes:
13
+
- margin-data:/data
12
14
depends_on:
13
15
db:
14
16
condition: service_healthy
···
23
25
24
26
volumes:
25
27
- db-data:/var/lib/postgresql/data
26
-
- margin-data:/data
27
28
healthcheck:
28
29
test: ["CMD-SHELL", "pg_isready -U margin"]
29
30
interval: 5s
+98
-49
extension/background/service-worker.js
+98
-49
extension/background/service-worker.js
···
6
6
const hasSidebarAction =
7
7
typeof browser !== "undefined" &&
8
8
typeof browser.sidebarAction !== "undefined";
9
-
const hasSessionStorage =
10
-
typeof chrome !== "undefined" &&
11
-
chrome.storage &&
12
-
typeof chrome.storage.session !== "undefined";
13
9
const hasNotifications =
14
10
typeof chrome !== "undefined" && typeof chrome.notifications !== "undefined";
15
11
···
43
39
}
44
40
}
45
41
46
-
async function openAnnotationUI(tabId) {
42
+
async function openAnnotationUI(tabId, windowId) {
47
43
if (hasSidePanel) {
48
44
try {
49
-
const tab = await chrome.tabs.get(tabId);
50
-
await chrome.sidePanel.setOptions({
51
-
tabId: tabId,
52
-
path: "sidepanel/sidepanel.html",
53
-
enabled: true,
54
-
});
55
-
await chrome.sidePanel.open({ windowId: tab.windowId });
45
+
let targetWindowId = windowId;
46
+
47
+
if (!targetWindowId) {
48
+
const tab = await chrome.tabs.get(tabId);
49
+
targetWindowId = tab.windowId;
50
+
}
51
+
52
+
await chrome.sidePanel.open({ windowId: targetWindowId });
56
53
return true;
57
54
} catch (err) {
58
55
console.error("Could not open Chrome side panel:", err);
···
71
68
return false;
72
69
}
73
70
74
-
async function storePendingAnnotation(data) {
75
-
if (hasSessionStorage) {
76
-
await chrome.storage.session.set({ pendingAnnotation: data });
77
-
} else {
78
-
await chrome.storage.local.set({
79
-
pendingAnnotation: data,
80
-
pendingAnnotationExpiry: Date.now() + 60000,
81
-
});
82
-
}
83
-
}
84
-
85
71
chrome.runtime.onInstalled.addListener(async () => {
86
72
const stored = await chrome.storage.local.get(["apiUrl"]);
87
73
if (!stored.apiUrl) {
···
118
104
if (hasSidebarAction) {
119
105
try {
120
106
await browser.sidebarAction.close();
121
-
} catch (e) {}
107
+
} catch {
108
+
/* ignore */
109
+
}
122
110
}
123
111
});
124
112
125
-
chrome.action.onClicked.addListener(async (tab) => {
113
+
chrome.action.onClicked.addListener(async () => {
126
114
const stored = await chrome.storage.local.get(["apiUrl"]);
127
115
const webUrl = stored.apiUrl || WEB_BASE;
128
116
chrome.tabs.create({ url: webUrl });
···
130
118
131
119
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
132
120
if (info.menuItemId === "margin-open-sidebar") {
133
-
if (hasSidePanel && chrome.sidePanel && chrome.sidePanel.open) {
134
-
try {
135
-
await chrome.sidePanel.open({ windowId: tab.windowId });
136
-
} catch (err) {
137
-
console.error("Failed to open side panel:", err);
138
-
}
139
-
} else if (hasSidebarAction) {
140
-
try {
141
-
await browser.sidebarAction.open();
142
-
} catch (err) {
143
-
console.error("Failed to open Firefox sidebar:", err);
144
-
}
145
-
}
121
+
await openAnnotationUI(tab.id, tab.windowId);
146
122
return;
147
123
}
148
124
···
189
165
selectionText: info.selectionText,
190
166
});
191
167
selector = response?.selector;
192
-
} catch (err) {}
193
-
194
-
if (selector && (hasSidePanel || hasSidebarAction)) {
195
-
await storePendingAnnotation({
196
-
url: tab.url,
197
-
title: tab.title,
198
-
selector: selector,
199
-
});
200
-
const opened = await openAnnotationUI(tab.id);
201
-
if (opened) return;
168
+
} catch {
169
+
/* ignore */
202
170
}
203
171
204
172
if (!selector && info.selectionText) {
···
208
176
};
209
177
}
210
178
179
+
if (selector) {
180
+
try {
181
+
await chrome.tabs.sendMessage(tab.id, {
182
+
type: "SHOW_INLINE_ANNOTATE",
183
+
data: {
184
+
url: tab.url,
185
+
title: tab.title,
186
+
selector: selector,
187
+
},
188
+
});
189
+
return;
190
+
} catch (e) {
191
+
console.debug("Inline annotate failed, falling back to new tab:", e);
192
+
}
193
+
}
194
+
211
195
if (WEB_BASE) {
212
196
let composeUrl = `${WEB_BASE}/new?url=${encodeURIComponent(tab.url)}`;
213
197
if (selector) {
···
227
211
selectionText: info.selectionText,
228
212
});
229
213
if (response && response.success) return;
230
-
} catch (err) {}
214
+
} catch {
215
+
/* ignore */
216
+
}
231
217
232
218
if (info.selectionText) {
233
219
selector = {
···
657
643
throw new Error(
658
644
`Failed to add to collection: ${res.status} ${errText}`,
659
645
);
646
+
}
647
+
648
+
const data = await res.json();
649
+
sendResponse({ success: true, data });
650
+
break;
651
+
}
652
+
653
+
case "GET_REPLIES": {
654
+
if (!API_BASE) {
655
+
sendResponse({ success: false, error: "API URL not configured" });
656
+
return;
657
+
}
658
+
659
+
const uri = request.data.uri;
660
+
const res = await fetch(
661
+
`${API_BASE}/api/replies?uri=${encodeURIComponent(uri)}`,
662
+
);
663
+
664
+
if (!res.ok) {
665
+
throw new Error(`Failed to fetch replies: ${res.status}`);
666
+
}
667
+
668
+
const data = await res.json();
669
+
sendResponse({ success: true, data: data.items || [] });
670
+
break;
671
+
}
672
+
673
+
case "CREATE_REPLY": {
674
+
if (!API_BASE) {
675
+
sendResponse({ success: false, error: "API URL not configured" });
676
+
return;
677
+
}
678
+
679
+
const cookie = await chrome.cookies.get({
680
+
url: API_BASE,
681
+
name: "margin_session",
682
+
});
683
+
684
+
if (!cookie) {
685
+
sendResponse({ success: false, error: "Not authenticated" });
686
+
return;
687
+
}
688
+
689
+
const { parentUri, parentCid, rootUri, rootCid, text } = request.data;
690
+
const res = await fetch(`${API_BASE}/api/annotations/reply`, {
691
+
method: "POST",
692
+
credentials: "include",
693
+
headers: {
694
+
"Content-Type": "application/json",
695
+
"X-Session-Token": cookie.value,
696
+
},
697
+
body: JSON.stringify({
698
+
parentUri,
699
+
parentCid,
700
+
rootUri,
701
+
rootCid,
702
+
text,
703
+
}),
704
+
});
705
+
706
+
if (!res.ok) {
707
+
const errText = await res.text();
708
+
throw new Error(`Failed to create reply: ${res.status} ${errText}`);
660
709
}
661
710
662
711
const data = await res.json();
+584
-214
extension/content/content.js
+584
-214
extension/content/content.js
···
4
4
let popoverEl = null;
5
5
6
6
let activeItems = [];
7
-
8
-
let hoveredItems = [];
9
-
let tooltipEl = null;
10
-
let hideTimer = null;
7
+
let currentSelection = null;
11
8
12
9
const OVERLAY_STYLES = `
13
10
:host { all: initial; }
···
18
15
width: 100%;
19
16
height: 100%;
20
17
pointer-events: none;
21
-
}
22
-
.margin-badge {
23
-
position: absolute;
24
-
background: #6366f1;
25
-
color: white;
26
-
padding: 4px 10px;
27
-
border-radius: 99px;
28
-
font-size: 11px;
29
-
font-weight: 600;
30
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
31
-
cursor: pointer;
32
-
pointer-events: auto;
33
-
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
34
-
display: flex;
35
-
align-items: center;
36
-
gap: 6px;
37
-
transform: translateY(-120%);
38
-
white-space: nowrap;
39
-
transition: transform 0.15s, background-color 0.15s;
40
-
z-index: 2147483647;
41
-
}
42
-
.margin-badge:hover {
43
-
transform: translateY(-125%) scale(1.05);
44
-
background: #4f46e5;
45
-
z-index: 2147483647;
46
-
}
47
-
.margin-badge-avatar {
48
-
width: 16px;
49
-
height: 16px;
50
-
border-radius: 50%;
51
-
background: rgba(255,255,255,0.2);
52
-
display: flex;
53
-
align-items: center;
54
-
justify-content: center;
55
-
font-size: 9px;
56
-
object-fit: cover;
57
-
}
58
-
.margin-badge-stack {
59
-
display: flex;
60
-
align-items: center;
61
-
}
62
-
.margin-badge-stack .margin-badge-avatar {
63
-
margin-left: -6px;
64
-
border: 1px solid #6366f1;
65
-
}
66
-
.margin-badge-stack .margin-badge-avatar:first-child {
67
-
margin-left: 0;
68
-
}
69
-
.margin-badge-stem {
70
-
position: absolute;
71
-
left: 14px;
72
-
bottom: -6px;
73
-
width: 2px;
74
-
height: 6px;
75
-
background: #6366f1;
76
-
border-radius: 2px;
77
18
}
78
19
79
20
.margin-popover {
···
149
90
padding: 4px 8px; color: #a1a1aa; font-size: 11px; cursor: pointer;
150
91
}
151
92
.btn-action:hover { background: #27272a; color: #e4e4e7; }
93
+
94
+
.margin-selection-popup {
95
+
position: fixed;
96
+
display: flex;
97
+
gap: 4px;
98
+
padding: 6px;
99
+
background: #09090b;
100
+
border: 1px solid #27272a;
101
+
border-radius: 8px;
102
+
box-shadow: 0 8px 16px rgba(0,0,0,0.4);
103
+
z-index: 2147483647;
104
+
pointer-events: auto;
105
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
106
+
animation: popover-in 0.15s forwards;
107
+
}
108
+
.selection-btn {
109
+
display: flex;
110
+
align-items: center;
111
+
gap: 6px;
112
+
padding: 6px 12px;
113
+
background: transparent;
114
+
border: none;
115
+
border-radius: 6px;
116
+
color: #e4e4e7;
117
+
font-size: 12px;
118
+
font-weight: 500;
119
+
cursor: pointer;
120
+
transition: background 0.15s;
121
+
}
122
+
.selection-btn:hover {
123
+
background: #27272a;
124
+
}
125
+
.selection-btn svg {
126
+
width: 14px;
127
+
height: 14px;
128
+
}
129
+
.inline-compose-modal {
130
+
position: fixed;
131
+
width: 340px;
132
+
max-width: calc(100vw - 40px);
133
+
background: #09090b;
134
+
border: 1px solid #27272a;
135
+
border-radius: 12px;
136
+
padding: 16px;
137
+
box-sizing: border-box;
138
+
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5);
139
+
z-index: 2147483647;
140
+
pointer-events: auto;
141
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
142
+
color: #e4e4e7;
143
+
animation: popover-in 0.15s forwards;
144
+
overflow: hidden;
145
+
}
146
+
.inline-compose-modal * {
147
+
box-sizing: border-box;
148
+
}
149
+
.inline-compose-quote {
150
+
padding: 8px 12px;
151
+
background: #18181b;
152
+
border-left: 3px solid #6366f1;
153
+
border-radius: 4px;
154
+
font-size: 12px;
155
+
color: #a1a1aa;
156
+
font-style: italic;
157
+
margin-bottom: 12px;
158
+
max-height: 60px;
159
+
overflow: hidden;
160
+
word-break: break-word;
161
+
}
162
+
.inline-compose-textarea {
163
+
width: 100%;
164
+
min-height: 80px;
165
+
padding: 10px 12px;
166
+
background: #18181b;
167
+
border: 1px solid #27272a;
168
+
border-radius: 8px;
169
+
color: #e4e4e7;
170
+
font-family: inherit;
171
+
font-size: 13px;
172
+
resize: vertical;
173
+
margin-bottom: 12px;
174
+
box-sizing: border-box;
175
+
}
176
+
.inline-compose-textarea:focus {
177
+
outline: none;
178
+
border-color: #6366f1;
179
+
}
180
+
.inline-compose-actions {
181
+
display: flex;
182
+
justify-content: flex-end;
183
+
gap: 8px;
184
+
}
185
+
.btn-cancel {
186
+
padding: 8px 16px;
187
+
background: transparent;
188
+
border: 1px solid #27272a;
189
+
border-radius: 6px;
190
+
color: #a1a1aa;
191
+
font-size: 13px;
192
+
cursor: pointer;
193
+
}
194
+
.btn-cancel:hover {
195
+
background: #27272a;
196
+
color: #e4e4e7;
197
+
}
198
+
.btn-submit {
199
+
padding: 8px 16px;
200
+
background: #6366f1;
201
+
border: none;
202
+
border-radius: 6px;
203
+
color: white;
204
+
font-size: 13px;
205
+
font-weight: 500;
206
+
cursor: pointer;
207
+
}
208
+
.btn-submit:hover {
209
+
background: #4f46e5;
210
+
}
211
+
.btn-submit:disabled {
212
+
opacity: 0.5;
213
+
cursor: not-allowed;
214
+
}
215
+
.reply-section {
216
+
border-top: 1px solid #27272a;
217
+
padding: 12px 16px;
218
+
background: #0f0f12;
219
+
border-radius: 0 0 12px 12px;
220
+
}
221
+
.reply-textarea {
222
+
width: 100%;
223
+
min-height: 60px;
224
+
padding: 8px 10px;
225
+
background: #18181b;
226
+
border: 1px solid #27272a;
227
+
border-radius: 6px;
228
+
color: #e4e4e7;
229
+
font-family: inherit;
230
+
font-size: 12px;
231
+
resize: none;
232
+
margin-bottom: 8px;
233
+
}
234
+
.reply-textarea:focus {
235
+
outline: none;
236
+
border-color: #6366f1;
237
+
}
238
+
.reply-submit {
239
+
padding: 6px 12px;
240
+
background: #6366f1;
241
+
border: none;
242
+
border-radius: 4px;
243
+
color: white;
244
+
font-size: 11px;
245
+
font-weight: 500;
246
+
cursor: pointer;
247
+
float: right;
248
+
}
249
+
.reply-submit:disabled {
250
+
opacity: 0.5;
251
+
}
252
+
.reply-item {
253
+
padding: 8px 0;
254
+
border-top: 1px solid #27272a;
255
+
}
256
+
.reply-item:first-child {
257
+
border-top: none;
258
+
}
259
+
.reply-author {
260
+
font-size: 11px;
261
+
font-weight: 600;
262
+
color: #a1a1aa;
263
+
margin-bottom: 4px;
264
+
}
265
+
.reply-text {
266
+
font-size: 12px;
267
+
color: #e4e4e7;
268
+
line-height: 1.4;
269
+
}
152
270
`;
153
271
154
272
class DOMTextMatcher {
···
203
321
let matchIndex = this.corpus.indexOf(searchText);
204
322
205
323
if (matchIndex === -1) {
206
-
const cleaned = searchText.replace(/\s+/g, " ");
207
-
return null;
324
+
const normalizedSearch = searchText.replace(/\s+/g, " ").trim();
325
+
matchIndex = this.corpus.indexOf(normalizedSearch);
326
+
327
+
if (matchIndex === -1) {
328
+
const fuzzyMatch = this.fuzzyFindInCorpus(searchText);
329
+
if (fuzzyMatch) {
330
+
const start = this.mapIndexToPoint(fuzzyMatch.start);
331
+
const end = this.mapIndexToPoint(fuzzyMatch.end);
332
+
if (start && end) {
333
+
const range = document.createRange();
334
+
range.setStart(start.node, start.offset);
335
+
range.setEnd(end.node, end.offset);
336
+
return range;
337
+
}
338
+
}
339
+
return null;
340
+
}
208
341
}
209
342
210
343
const start = this.mapIndexToPoint(matchIndex);
···
219
352
return null;
220
353
}
221
354
355
+
fuzzyFindInCorpus(searchText) {
356
+
const searchWords = searchText
357
+
.trim()
358
+
.split(/\s+/)
359
+
.filter((w) => w.length > 0);
360
+
if (searchWords.length === 0) return null;
361
+
362
+
const corpusLower = this.corpus.toLowerCase();
363
+
364
+
const firstWord = searchWords[0].toLowerCase();
365
+
let searchStart = 0;
366
+
367
+
while (searchStart < corpusLower.length) {
368
+
const wordStart = corpusLower.indexOf(firstWord, searchStart);
369
+
if (wordStart === -1) break;
370
+
371
+
let corpusPos = wordStart;
372
+
let matched = true;
373
+
let lastMatchEnd = wordStart;
374
+
375
+
for (const word of searchWords) {
376
+
const wordLower = word.toLowerCase();
377
+
while (
378
+
corpusPos < corpusLower.length &&
379
+
/\s/.test(this.corpus[corpusPos])
380
+
) {
381
+
corpusPos++;
382
+
}
383
+
const corpusSlice = corpusLower.slice(
384
+
corpusPos,
385
+
corpusPos + wordLower.length,
386
+
);
387
+
if (corpusSlice !== wordLower) {
388
+
matched = false;
389
+
break;
390
+
}
391
+
392
+
corpusPos += wordLower.length;
393
+
lastMatchEnd = corpusPos;
394
+
}
395
+
396
+
if (matched) {
397
+
return { start: wordStart, end: lastMatchEnd };
398
+
}
399
+
400
+
searchStart = wordStart + 1;
401
+
}
402
+
403
+
return null;
404
+
}
405
+
222
406
mapIndexToPoint(corpusIndex) {
223
407
for (const info of this.indices) {
224
408
if (
···
265
449
container.id = "margin-overlay-container";
266
450
sidebarShadow.appendChild(container);
267
451
268
-
createTooltip(container);
269
-
270
452
const observer = new ResizeObserver(() => {
271
453
sidebarHost.style.height = `${getScrollHeight()}px`;
272
454
});
273
455
if (document.body) observer.observe(document.body);
274
456
if (document.documentElement) observer.observe(document.documentElement);
275
457
276
-
fetchAnnotations();
458
+
if (typeof chrome !== "undefined" && chrome.storage) {
459
+
chrome.storage.local.get(["showOverlay"], (result) => {
460
+
if (result.showOverlay === false) {
461
+
sidebarHost.style.display = "none";
462
+
} else {
463
+
fetchAnnotations();
464
+
}
465
+
});
466
+
} else {
467
+
fetchAnnotations();
468
+
}
277
469
278
470
document.addEventListener("mousemove", handleMouseMove);
279
-
document.addEventListener("click", handleDocumentClick);
471
+
document.addEventListener("click", handleDocumentClick, true);
280
472
}
281
473
282
-
function createTooltip(container) {
283
-
tooltipEl = document.createElement("div");
284
-
tooltipEl.className = "margin-badge";
285
-
tooltipEl.style.opacity = "0";
286
-
tooltipEl.style.transition = "opacity 0.1s, transform 0.1s";
287
-
tooltipEl.style.pointerEvents = "auto";
474
+
function showInlineComposeModal() {
475
+
if (!sidebarShadow || !currentSelection) return;
476
+
477
+
const container = sidebarShadow.getElementById("margin-overlay-container");
478
+
if (!container) return;
479
+
480
+
const existingModal = container.querySelector(".inline-compose-modal");
481
+
if (existingModal) existingModal.remove();
482
+
483
+
const modal = document.createElement("div");
484
+
modal.className = "inline-compose-modal";
485
+
486
+
modal.style.left = `${Math.max(20, (window.innerWidth - 340) / 2)}px`;
487
+
modal.style.top = `${Math.min(200, window.innerHeight / 4)}px`;
488
+
489
+
const truncatedQuote =
490
+
currentSelection.text.length > 100
491
+
? currentSelection.text.substring(0, 100) + "..."
492
+
: currentSelection.text;
493
+
494
+
modal.innerHTML = `
495
+
<div class="inline-compose-quote">"${truncatedQuote}"</div>
496
+
<textarea class="inline-compose-textarea" placeholder="Add your annotation..." autofocus></textarea>
497
+
<div class="inline-compose-actions">
498
+
<button class="btn-cancel">Cancel</button>
499
+
<button class="btn-submit">Post Annotation</button>
500
+
</div>
501
+
`;
502
+
503
+
const textarea = modal.querySelector("textarea");
504
+
const submitBtn = modal.querySelector(".btn-submit");
505
+
const cancelBtn = modal.querySelector(".btn-cancel");
288
506
289
-
tooltipEl.addEventListener("click", (e) => {
290
-
e.stopPropagation();
291
-
if (hoveredItems.length > 0) {
292
-
const firstItem = hoveredItems[0];
293
-
const rect = activeItems
294
-
.find((x) => x.item === firstItem)
295
-
?.range.getBoundingClientRect();
296
-
if (rect) {
297
-
const top = rect.top + window.scrollY;
298
-
const left = rect.left + window.scrollX;
299
-
showPopover(hoveredItems, top, left);
300
-
}
301
-
}
507
+
cancelBtn.addEventListener("click", () => {
508
+
modal.remove();
302
509
});
303
-
container.appendChild(tooltipEl);
510
+
511
+
submitBtn.addEventListener("click", async () => {
512
+
const text = textarea.value.trim();
513
+
if (!text) return;
514
+
515
+
submitBtn.disabled = true;
516
+
submitBtn.textContent = "Posting...";
517
+
518
+
chrome.runtime.sendMessage(
519
+
{
520
+
type: "CREATE_ANNOTATION",
521
+
data: {
522
+
url: currentSelection.url || window.location.href,
523
+
title: currentSelection.title || document.title,
524
+
text: text,
525
+
selector: currentSelection.selector,
526
+
},
527
+
},
528
+
(res) => {
529
+
if (res && res.success) {
530
+
modal.remove();
531
+
fetchAnnotations();
532
+
} else {
533
+
submitBtn.disabled = false;
534
+
submitBtn.textContent = "Post Annotation";
535
+
alert(
536
+
"Failed to create annotation: " + (res?.error || "Unknown error"),
537
+
);
538
+
}
539
+
},
540
+
);
541
+
});
542
+
543
+
container.appendChild(modal);
544
+
textarea.focus();
545
+
546
+
const handleEscape = (e) => {
547
+
if (e.key === "Escape") {
548
+
modal.remove();
549
+
document.removeEventListener("keydown", handleEscape);
550
+
}
551
+
};
552
+
document.addEventListener("keydown", handleEscape);
304
553
}
305
554
555
+
let hoverIndicator = null;
556
+
306
557
function handleMouseMove(e) {
307
558
const x = e.clientX;
308
559
const y = e.clientY;
309
-
310
560
let foundItems = [];
311
-
561
+
let firstRange = null;
312
562
for (const { range, item } of activeItems) {
313
563
const rects = range.getClientRects();
314
564
for (const rect of rects) {
315
-
const padding = 5;
316
565
if (
317
-
x >= rect.left - padding &&
318
-
x <= rect.right + padding &&
319
-
y >= rect.top - padding &&
320
-
y <= rect.bottom + padding
566
+
x >= rect.left &&
567
+
x <= rect.right &&
568
+
y >= rect.top &&
569
+
y <= rect.bottom
321
570
) {
322
-
if (!foundItems.includes(item)) {
323
-
foundItems.push(item);
571
+
if (!firstRange) firstRange = range;
572
+
if (!foundItems.some((f) => f.item === item)) {
573
+
foundItems.push({ range, item, rect });
324
574
}
325
575
break;
326
576
}
327
577
}
328
578
}
329
579
330
-
let isOverTooltip = false;
331
-
if (tooltipEl && tooltipEl.style.opacity === "1") {
332
-
const rect = tooltipEl.getBoundingClientRect();
333
-
if (
334
-
x >= rect.left &&
335
-
x <= rect.right &&
336
-
y >= rect.top &&
337
-
y <= rect.bottom
338
-
) {
339
-
isOverTooltip = true;
340
-
}
341
-
}
580
+
if (foundItems.length > 0) {
581
+
document.body.style.cursor = "pointer";
342
582
343
-
if (foundItems.length > 0 || isOverTooltip) {
344
-
if (hideTimer) {
345
-
clearTimeout(hideTimer);
346
-
hideTimer = null;
347
-
}
348
-
if (foundItems.length > 0) {
349
-
const currentIds = hoveredItems
350
-
.map((i) => i.id || i.cid)
351
-
.sort()
352
-
.join(",");
353
-
const newIds = foundItems
354
-
.map((i) => i.id || i.cid)
355
-
.sort()
356
-
.join(",");
357
-
358
-
if (currentIds !== newIds) {
359
-
hoveredItems = foundItems;
360
-
updateTooltip();
583
+
if (!hoverIndicator && sidebarShadow) {
584
+
const container = sidebarShadow.getElementById(
585
+
"margin-overlay-container",
586
+
);
587
+
if (container) {
588
+
hoverIndicator = document.createElement("div");
589
+
hoverIndicator.className = "margin-hover-indicator";
590
+
hoverIndicator.style.cssText = `
591
+
position: fixed;
592
+
display: flex;
593
+
align-items: center;
594
+
pointer-events: none;
595
+
z-index: 2147483647;
596
+
opacity: 0;
597
+
transition: opacity 0.15s, transform 0.15s;
598
+
transform: scale(0.8);
599
+
`;
600
+
container.appendChild(hoverIndicator);
361
601
}
362
602
}
363
-
} else {
364
-
if (!hideTimer && hoveredItems.length > 0) {
365
-
hideTimer = setTimeout(() => {
366
-
hoveredItems = [];
367
-
updateTooltip();
368
-
hideTimer = null;
369
-
}, 300);
370
-
}
371
-
}
372
-
}
373
603
374
-
function updateTooltip() {
375
-
if (!tooltipEl) return;
604
+
if (hoverIndicator) {
605
+
const authorsMap = new Map();
606
+
foundItems.forEach(({ item }) => {
607
+
const author = item.author || item.creator || {};
608
+
const id = author.did || author.handle || "unknown";
609
+
if (!authorsMap.has(id)) {
610
+
authorsMap.set(id, author);
611
+
}
612
+
});
613
+
const uniqueAuthors = Array.from(authorsMap.values());
376
614
377
-
if (hoveredItems.length === 0) {
378
-
tooltipEl.style.opacity = "0";
379
-
tooltipEl.style.transform = "translateY(-105%) scale(0.9)";
380
-
tooltipEl.style.pointerEvents = "none";
381
-
return;
382
-
}
615
+
const maxShow = 3;
616
+
const displayAuthors = uniqueAuthors.slice(0, maxShow);
617
+
const overflow = uniqueAuthors.length - maxShow;
383
618
384
-
tooltipEl.style.pointerEvents = "auto";
619
+
let html = displayAuthors
620
+
.map((author, i) => {
621
+
const avatar = author.avatar;
622
+
const handle = author.handle || "U";
623
+
const marginLeft = i === 0 ? "0" : "-8px";
385
624
386
-
const authorsMap = new Map();
387
-
hoveredItems.forEach((item) => {
388
-
const author = item.author || item.creator || {};
389
-
const id = author.did || author.handle;
390
-
if (id && !authorsMap.has(id)) {
391
-
authorsMap.set(id, author);
392
-
}
393
-
});
625
+
if (avatar) {
626
+
return `<img src="${avatar}" style="width: 24px; height: 24px; border-radius: 50%; object-fit: cover; border: 2px solid #09090b; margin-left: ${marginLeft};">`;
627
+
} else {
628
+
return `<div style="width: 24px; height: 24px; border-radius: 50%; background: #6366f1; color: white; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 600; font-family: -apple-system, sans-serif; border: 2px solid #09090b; margin-left: ${marginLeft};">${handle[0]?.toUpperCase() || "U"}</div>`;
629
+
}
630
+
})
631
+
.join("");
394
632
395
-
const uniqueAuthors = Array.from(authorsMap.values());
396
-
let contentHtml = "";
633
+
if (overflow > 0) {
634
+
html += `<div style="width: 24px; height: 24px; border-radius: 50%; background: #27272a; color: #a1a1aa; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 600; font-family: -apple-system, sans-serif; border: 2px solid #09090b; margin-left: -8px;">+${overflow}</div>`;
635
+
}
397
636
398
-
if (uniqueAuthors.length === 1) {
399
-
const author = uniqueAuthors[0] || {};
400
-
const handle = author.handle || "User";
401
-
const avatar = author.avatar;
402
-
const count = hoveredItems.length;
637
+
hoverIndicator.innerHTML = html;
638
+
639
+
const firstRect = firstRange.getClientRects()[0];
640
+
const totalWidth =
641
+
Math.min(uniqueAuthors.length, maxShow + (overflow > 0 ? 1 : 0)) *
642
+
18 +
643
+
8;
644
+
const leftPos = firstRect.left - totalWidth;
645
+
const topPos = firstRect.top + firstRect.height / 2 - 12;
403
646
404
-
let avatarHtml = `<div class="margin-badge-avatar">${handle[0]?.toUpperCase() || "U"}</div>`;
405
-
if (avatar) {
406
-
avatarHtml = `<img src="${avatar}" class="margin-badge-avatar">`;
647
+
hoverIndicator.style.left = `${leftPos}px`;
648
+
hoverIndicator.style.top = `${topPos}px`;
649
+
hoverIndicator.style.opacity = "1";
650
+
hoverIndicator.style.transform = "scale(1)";
407
651
}
408
-
409
-
contentHtml = `${avatarHtml}<span>${handle}${count > 1 ? ` (${count})` : ""}</span>`;
410
652
} else {
411
-
let stackHtml = `<div class="margin-badge-stack">`;
412
-
const displayAuthors = uniqueAuthors.slice(0, 3);
413
-
displayAuthors.forEach((author) => {
414
-
const handle = author.handle || "U";
415
-
const avatar = author.avatar;
416
-
if (avatar) {
417
-
stackHtml += `<img src="${avatar}" class="margin-badge-avatar">`;
418
-
} else {
419
-
stackHtml += `<div class="margin-badge-avatar">${handle[0]?.toUpperCase() || "U"}</div>`;
420
-
}
421
-
});
422
-
stackHtml += `</div>`;
653
+
document.body.style.cursor = "";
654
+
if (hoverIndicator) {
655
+
hoverIndicator.style.opacity = "0";
656
+
hoverIndicator.style.transform = "scale(0.8)";
657
+
}
658
+
}
659
+
}
423
660
424
-
contentHtml = `${stackHtml}<span>${uniqueAuthors.length} people</span>`;
661
+
function handleDocumentClick(e) {
662
+
const x = e.clientX;
663
+
const y = e.clientY;
664
+
if (popoverEl && sidebarShadow) {
665
+
const rect = popoverEl.getBoundingClientRect();
666
+
if (
667
+
x >= rect.left &&
668
+
x <= rect.right &&
669
+
y >= rect.top &&
670
+
y <= rect.bottom
671
+
) {
672
+
return;
673
+
}
425
674
}
426
675
427
-
tooltipEl.innerHTML = `
428
-
${contentHtml}
429
-
<div class="margin-badge-stem"></div>
430
-
`;
431
-
432
-
const firstItem = hoveredItems[0];
433
-
const match = activeItems.find((x) => x.item === firstItem);
434
-
if (match) {
435
-
const rects = match.range.getClientRects();
436
-
if (rects && rects.length > 0) {
437
-
const rect = rects[0];
438
-
const top = rect.top + window.scrollY;
439
-
const left = rect.left + window.scrollX;
440
-
441
-
tooltipEl.style.top = `${top - 36}px`;
442
-
tooltipEl.style.left = `${left}px`;
443
-
tooltipEl.style.opacity = "1";
444
-
tooltipEl.style.transform = "translateY(0) scale(1)";
676
+
let clickedItems = [];
677
+
for (const { range, item } of activeItems) {
678
+
const rects = range.getClientRects();
679
+
for (const rect of rects) {
680
+
if (
681
+
x >= rect.left &&
682
+
x <= rect.right &&
683
+
y >= rect.top &&
684
+
y <= rect.bottom
685
+
) {
686
+
if (!clickedItems.includes(item)) {
687
+
clickedItems.push(item);
688
+
}
689
+
break;
690
+
}
445
691
}
446
692
}
447
-
}
448
693
449
-
function handleDocumentClick(e) {
450
-
if (hoveredItems.length > 0) {
694
+
if (clickedItems.length > 0) {
451
695
e.preventDefault();
452
696
e.stopPropagation();
453
697
454
-
const item = hoveredItems[0];
455
-
const match = activeItems.find((x) => x.item === item);
698
+
if (popoverEl) {
699
+
const currentIds = popoverEl.dataset.itemIds;
700
+
const newIds = clickedItems
701
+
.map((i) => i.uri || i.id)
702
+
.sort()
703
+
.join(",");
704
+
705
+
if (currentIds === newIds) {
706
+
popoverEl.remove();
707
+
popoverEl = null;
708
+
return;
709
+
}
710
+
}
711
+
712
+
const firstItem = clickedItems[0];
713
+
const match = activeItems.find((x) => x.item === firstItem);
456
714
if (match) {
457
715
const rects = match.range.getClientRects();
458
716
if (rects.length > 0) {
459
717
const rect = rects[0];
460
718
const top = rect.top + window.scrollY;
461
719
const left = rect.left + window.scrollX;
462
-
showPopover(hoveredItems, top, left);
720
+
showPopover(clickedItems, top, left);
463
721
}
464
722
}
723
+
} else {
724
+
if (popoverEl) {
725
+
popoverEl.remove();
726
+
popoverEl = null;
727
+
}
465
728
}
466
729
}
467
-
468
-
function refreshPositions() {}
469
730
470
731
function renderBadges(annotations) {
471
732
if (!sidebarShadow) return;
···
484
745
if (range) {
485
746
activeItems.push({ range, item });
486
747
487
-
const color = item.color || "#c084fc";
748
+
const color = item.color || "#6366f1";
488
749
if (!rangesByColor[color]) rangesByColor[color] = [];
489
750
rangesByColor[color].push(range);
490
751
}
491
752
});
492
753
493
-
if (CSS.highlights) {
754
+
if (typeof CSS !== "undefined" && CSS.highlights) {
494
755
CSS.highlights.clear();
495
756
for (const [color, ranges] of Object.entries(rangesByColor)) {
496
757
const highlight = new Highlight(...ranges);
···
508
769
const style = document.createElement("style");
509
770
style.textContent = `
510
771
::highlight(${name}) {
511
-
background-color: ${color}66;
512
-
color: inherit;
772
+
text-decoration: underline;
773
+
text-decoration-color: ${color};
774
+
text-decoration-thickness: 2px;
775
+
text-underline-offset: 2px;
513
776
cursor: pointer;
514
777
}
515
778
`;
···
523
786
popoverEl = document.createElement("div");
524
787
popoverEl.className = "margin-popover";
525
788
789
+
const ids = items
790
+
.map((i) => i.uri || i.id)
791
+
.sort()
792
+
.join(",");
793
+
popoverEl.dataset.itemIds = ids;
794
+
526
795
const popWidth = 320;
527
796
const screenWidth = window.innerWidth;
528
797
let finalLeft = left;
···
531
800
popoverEl.style.top = `${top + 20}px`;
532
801
popoverEl.style.left = `${finalLeft}px`;
533
802
534
-
const title =
535
-
items.length > 1 ? `${items.length} Annotations` : "Annotation";
803
+
const hasHighlights = items.some((item) => item.type === "Highlight");
804
+
const hasAnnotations = items.some((item) => item.type !== "Highlight");
805
+
let title;
806
+
if (items.length > 1) {
807
+
if (hasHighlights && hasAnnotations) {
808
+
title = `${items.length} Items`;
809
+
} else if (hasHighlights) {
810
+
title = `${items.length} Highlights`;
811
+
} else {
812
+
title = `${items.length} Annotations`;
813
+
}
814
+
} else {
815
+
title = items[0]?.type === "Highlight" ? "Highlight" : "Annotation";
816
+
}
536
817
537
818
let contentHtml = items
538
819
.map((item) => {
···
591
872
</div>
592
873
`;
593
874
594
-
popoverEl.querySelector(".popover-close").addEventListener("click", () => {
875
+
popoverEl.querySelector(".popover-close").addEventListener("click", (e) => {
876
+
e.stopPropagation();
595
877
popoverEl.remove();
596
878
popoverEl = null;
597
879
});
598
880
599
881
const replyBtns = popoverEl.querySelectorAll(".btn-reply");
600
882
replyBtns.forEach((btn) => {
601
-
btn.addEventListener("click", () => {
883
+
btn.addEventListener("click", (e) => {
884
+
e.stopPropagation();
602
885
const id = btn.getAttribute("data-id");
603
886
if (id) {
604
887
chrome.runtime.sendMessage({
···
636
919
}, 0);
637
920
}
638
921
639
-
function closePopoverOutside(e) {
922
+
function closePopoverOutside() {
640
923
if (popoverEl) {
641
924
popoverEl.remove();
642
925
popoverEl = null;
···
644
927
}
645
928
}
646
929
647
-
function fetchAnnotations() {
930
+
function fetchAnnotations(retryCount = 0) {
648
931
if (typeof chrome !== "undefined" && chrome.runtime) {
649
932
chrome.runtime.sendMessage(
650
933
{
···
652
935
data: { url: window.location.href },
653
936
},
654
937
(res) => {
655
-
if (res && res.success) {
938
+
if (res && res.success && res.data && res.data.length > 0) {
656
939
renderBadges(res.data);
940
+
} else if (retryCount < 3) {
941
+
setTimeout(
942
+
() => fetchAnnotations(retryCount + 1),
943
+
1000 * (retryCount + 1),
944
+
);
657
945
}
658
946
},
659
947
);
···
672
960
return true;
673
961
}
674
962
963
+
if (request.type === "SHOW_INLINE_ANNOTATE") {
964
+
currentSelection = {
965
+
text: request.data.selector?.exact || "",
966
+
selector: request.data.selector,
967
+
url: request.data.url,
968
+
title: request.data.title,
969
+
};
970
+
showInlineComposeModal();
971
+
sendResponse({ success: true });
972
+
return true;
973
+
}
974
+
975
+
if (request.type === "UPDATE_OVERLAY_VISIBILITY") {
976
+
if (sidebarHost) {
977
+
sidebarHost.style.display = request.show ? "block" : "none";
978
+
}
979
+
if (request.show) {
980
+
fetchAnnotations();
981
+
} else {
982
+
if (typeof CSS !== "undefined" && CSS.highlights) {
983
+
CSS.highlights.clear();
984
+
}
985
+
}
986
+
sendResponse({ success: true });
987
+
return true;
988
+
}
989
+
675
990
if (request.type === "SCROLL_TO_TEXT") {
676
991
const selector = request.selector;
677
992
if (selector?.exact) {
···
698
1013
} else {
699
1014
initOverlay();
700
1015
}
1016
+
1017
+
window.addEventListener("load", () => {
1018
+
if (typeof chrome !== "undefined" && chrome.storage) {
1019
+
chrome.storage.local.get(["showOverlay"], (result) => {
1020
+
if (result.showOverlay !== false) {
1021
+
setTimeout(() => fetchAnnotations(), 500);
1022
+
}
1023
+
});
1024
+
} else {
1025
+
setTimeout(() => fetchAnnotations(), 500);
1026
+
}
1027
+
});
1028
+
1029
+
let lastUrl = window.location.href;
1030
+
1031
+
function checkUrlChange() {
1032
+
if (window.location.href !== lastUrl) {
1033
+
lastUrl = window.location.href;
1034
+
onUrlChange();
1035
+
}
1036
+
}
1037
+
1038
+
function onUrlChange() {
1039
+
if (typeof CSS !== "undefined" && CSS.highlights) {
1040
+
CSS.highlights.clear();
1041
+
}
1042
+
activeItems = [];
1043
+
1044
+
if (typeof chrome !== "undefined" && chrome.storage) {
1045
+
chrome.storage.local.get(["showOverlay"], (result) => {
1046
+
if (result.showOverlay !== false) {
1047
+
fetchAnnotations();
1048
+
}
1049
+
});
1050
+
} else {
1051
+
fetchAnnotations();
1052
+
}
1053
+
}
1054
+
1055
+
window.addEventListener("popstate", onUrlChange);
1056
+
1057
+
const originalPushState = history.pushState;
1058
+
const originalReplaceState = history.replaceState;
1059
+
1060
+
history.pushState = function (...args) {
1061
+
originalPushState.apply(this, args);
1062
+
checkUrlChange();
1063
+
};
1064
+
1065
+
history.replaceState = function (...args) {
1066
+
originalReplaceState.apply(this, args);
1067
+
checkUrlChange();
1068
+
};
1069
+
1070
+
setInterval(checkUrlChange, 1000);
701
1071
})();
+25
extension/eslint.config.js
+25
extension/eslint.config.js
···
1
+
import js from "@eslint/js";
2
+
import globals from "globals";
3
+
4
+
export default [
5
+
{ ignores: ["dist"] },
6
+
{
7
+
files: ["**/*.js"],
8
+
languageOptions: {
9
+
ecmaVersion: 2020,
10
+
globals: {
11
+
...globals.browser,
12
+
...globals.webextensions,
13
+
},
14
+
parserOptions: {
15
+
ecmaVersion: "latest",
16
+
sourceType: "module",
17
+
},
18
+
},
19
+
rules: {
20
+
...js.configs.recommended.rules,
21
+
"no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
22
+
"no-undef": "warn",
23
+
},
24
+
},
25
+
];
+1
-1
extension/icons/site.webmanifest
+1
-1
extension/icons/site.webmanifest
+1091
extension/package-lock.json
+1091
extension/package-lock.json
···
1
+
{
2
+
"name": "margin-extension",
3
+
"version": "0.1.0",
4
+
"lockfileVersion": 3,
5
+
"requires": true,
6
+
"packages": {
7
+
"": {
8
+
"name": "margin-extension",
9
+
"version": "0.1.0",
10
+
"devDependencies": {
11
+
"@eslint/js": "^9.39.2",
12
+
"eslint": "^9.39.2",
13
+
"globals": "^17.0.0"
14
+
}
15
+
},
16
+
"node_modules/@eslint-community/eslint-utils": {
17
+
"version": "4.9.1",
18
+
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
19
+
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
20
+
"dev": true,
21
+
"license": "MIT",
22
+
"dependencies": {
23
+
"eslint-visitor-keys": "^3.4.3"
24
+
},
25
+
"engines": {
26
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
27
+
},
28
+
"funding": {
29
+
"url": "https://opencollective.com/eslint"
30
+
},
31
+
"peerDependencies": {
32
+
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
33
+
}
34
+
},
35
+
"node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
36
+
"version": "3.4.3",
37
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
38
+
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
39
+
"dev": true,
40
+
"license": "Apache-2.0",
41
+
"engines": {
42
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
43
+
},
44
+
"funding": {
45
+
"url": "https://opencollective.com/eslint"
46
+
}
47
+
},
48
+
"node_modules/@eslint-community/regexpp": {
49
+
"version": "4.12.2",
50
+
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
51
+
"integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
52
+
"dev": true,
53
+
"license": "MIT",
54
+
"engines": {
55
+
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
56
+
}
57
+
},
58
+
"node_modules/@eslint/config-array": {
59
+
"version": "0.21.1",
60
+
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
61
+
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
62
+
"dev": true,
63
+
"license": "Apache-2.0",
64
+
"dependencies": {
65
+
"@eslint/object-schema": "^2.1.7",
66
+
"debug": "^4.3.1",
67
+
"minimatch": "^3.1.2"
68
+
},
69
+
"engines": {
70
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
71
+
}
72
+
},
73
+
"node_modules/@eslint/config-helpers": {
74
+
"version": "0.4.2",
75
+
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
76
+
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
77
+
"dev": true,
78
+
"license": "Apache-2.0",
79
+
"dependencies": {
80
+
"@eslint/core": "^0.17.0"
81
+
},
82
+
"engines": {
83
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
84
+
}
85
+
},
86
+
"node_modules/@eslint/core": {
87
+
"version": "0.17.0",
88
+
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
89
+
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
90
+
"dev": true,
91
+
"license": "Apache-2.0",
92
+
"dependencies": {
93
+
"@types/json-schema": "^7.0.15"
94
+
},
95
+
"engines": {
96
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
97
+
}
98
+
},
99
+
"node_modules/@eslint/eslintrc": {
100
+
"version": "3.3.3",
101
+
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
102
+
"integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
103
+
"dev": true,
104
+
"license": "MIT",
105
+
"dependencies": {
106
+
"ajv": "^6.12.4",
107
+
"debug": "^4.3.2",
108
+
"espree": "^10.0.1",
109
+
"globals": "^14.0.0",
110
+
"ignore": "^5.2.0",
111
+
"import-fresh": "^3.2.1",
112
+
"js-yaml": "^4.1.1",
113
+
"minimatch": "^3.1.2",
114
+
"strip-json-comments": "^3.1.1"
115
+
},
116
+
"engines": {
117
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
118
+
},
119
+
"funding": {
120
+
"url": "https://opencollective.com/eslint"
121
+
}
122
+
},
123
+
"node_modules/@eslint/eslintrc/node_modules/globals": {
124
+
"version": "14.0.0",
125
+
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
126
+
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
127
+
"dev": true,
128
+
"license": "MIT",
129
+
"engines": {
130
+
"node": ">=18"
131
+
},
132
+
"funding": {
133
+
"url": "https://github.com/sponsors/sindresorhus"
134
+
}
135
+
},
136
+
"node_modules/@eslint/js": {
137
+
"version": "9.39.2",
138
+
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
139
+
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
140
+
"dev": true,
141
+
"license": "MIT",
142
+
"engines": {
143
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
144
+
},
145
+
"funding": {
146
+
"url": "https://eslint.org/donate"
147
+
}
148
+
},
149
+
"node_modules/@eslint/object-schema": {
150
+
"version": "2.1.7",
151
+
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
152
+
"integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
153
+
"dev": true,
154
+
"license": "Apache-2.0",
155
+
"engines": {
156
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
157
+
}
158
+
},
159
+
"node_modules/@eslint/plugin-kit": {
160
+
"version": "0.4.1",
161
+
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
162
+
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
163
+
"dev": true,
164
+
"license": "Apache-2.0",
165
+
"dependencies": {
166
+
"@eslint/core": "^0.17.0",
167
+
"levn": "^0.4.1"
168
+
},
169
+
"engines": {
170
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
171
+
}
172
+
},
173
+
"node_modules/@humanfs/core": {
174
+
"version": "0.19.1",
175
+
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
176
+
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
177
+
"dev": true,
178
+
"license": "Apache-2.0",
179
+
"engines": {
180
+
"node": ">=18.18.0"
181
+
}
182
+
},
183
+
"node_modules/@humanfs/node": {
184
+
"version": "0.16.7",
185
+
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
186
+
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
187
+
"dev": true,
188
+
"license": "Apache-2.0",
189
+
"dependencies": {
190
+
"@humanfs/core": "^0.19.1",
191
+
"@humanwhocodes/retry": "^0.4.0"
192
+
},
193
+
"engines": {
194
+
"node": ">=18.18.0"
195
+
}
196
+
},
197
+
"node_modules/@humanwhocodes/module-importer": {
198
+
"version": "1.0.1",
199
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
200
+
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
201
+
"dev": true,
202
+
"license": "Apache-2.0",
203
+
"engines": {
204
+
"node": ">=12.22"
205
+
},
206
+
"funding": {
207
+
"type": "github",
208
+
"url": "https://github.com/sponsors/nzakas"
209
+
}
210
+
},
211
+
"node_modules/@humanwhocodes/retry": {
212
+
"version": "0.4.3",
213
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
214
+
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
215
+
"dev": true,
216
+
"license": "Apache-2.0",
217
+
"engines": {
218
+
"node": ">=18.18"
219
+
},
220
+
"funding": {
221
+
"type": "github",
222
+
"url": "https://github.com/sponsors/nzakas"
223
+
}
224
+
},
225
+
"node_modules/@types/estree": {
226
+
"version": "1.0.8",
227
+
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
228
+
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
229
+
"dev": true,
230
+
"license": "MIT"
231
+
},
232
+
"node_modules/@types/json-schema": {
233
+
"version": "7.0.15",
234
+
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
235
+
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
236
+
"dev": true,
237
+
"license": "MIT"
238
+
},
239
+
"node_modules/acorn": {
240
+
"version": "8.15.0",
241
+
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
242
+
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
243
+
"dev": true,
244
+
"license": "MIT",
245
+
"peer": true,
246
+
"bin": {
247
+
"acorn": "bin/acorn"
248
+
},
249
+
"engines": {
250
+
"node": ">=0.4.0"
251
+
}
252
+
},
253
+
"node_modules/acorn-jsx": {
254
+
"version": "5.3.2",
255
+
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
256
+
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
257
+
"dev": true,
258
+
"license": "MIT",
259
+
"peerDependencies": {
260
+
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
261
+
}
262
+
},
263
+
"node_modules/ajv": {
264
+
"version": "6.12.6",
265
+
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
266
+
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
267
+
"dev": true,
268
+
"license": "MIT",
269
+
"dependencies": {
270
+
"fast-deep-equal": "^3.1.1",
271
+
"fast-json-stable-stringify": "^2.0.0",
272
+
"json-schema-traverse": "^0.4.1",
273
+
"uri-js": "^4.2.2"
274
+
},
275
+
"funding": {
276
+
"type": "github",
277
+
"url": "https://github.com/sponsors/epoberezkin"
278
+
}
279
+
},
280
+
"node_modules/ansi-styles": {
281
+
"version": "4.3.0",
282
+
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
283
+
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
284
+
"dev": true,
285
+
"license": "MIT",
286
+
"dependencies": {
287
+
"color-convert": "^2.0.1"
288
+
},
289
+
"engines": {
290
+
"node": ">=8"
291
+
},
292
+
"funding": {
293
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
294
+
}
295
+
},
296
+
"node_modules/argparse": {
297
+
"version": "2.0.1",
298
+
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
299
+
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
300
+
"dev": true,
301
+
"license": "Python-2.0"
302
+
},
303
+
"node_modules/balanced-match": {
304
+
"version": "1.0.2",
305
+
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
306
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
307
+
"dev": true,
308
+
"license": "MIT"
309
+
},
310
+
"node_modules/brace-expansion": {
311
+
"version": "1.1.12",
312
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
313
+
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
314
+
"dev": true,
315
+
"license": "MIT",
316
+
"dependencies": {
317
+
"balanced-match": "^1.0.0",
318
+
"concat-map": "0.0.1"
319
+
}
320
+
},
321
+
"node_modules/callsites": {
322
+
"version": "3.1.0",
323
+
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
324
+
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
325
+
"dev": true,
326
+
"license": "MIT",
327
+
"engines": {
328
+
"node": ">=6"
329
+
}
330
+
},
331
+
"node_modules/chalk": {
332
+
"version": "4.1.2",
333
+
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
334
+
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
335
+
"dev": true,
336
+
"license": "MIT",
337
+
"dependencies": {
338
+
"ansi-styles": "^4.1.0",
339
+
"supports-color": "^7.1.0"
340
+
},
341
+
"engines": {
342
+
"node": ">=10"
343
+
},
344
+
"funding": {
345
+
"url": "https://github.com/chalk/chalk?sponsor=1"
346
+
}
347
+
},
348
+
"node_modules/color-convert": {
349
+
"version": "2.0.1",
350
+
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
351
+
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
352
+
"dev": true,
353
+
"license": "MIT",
354
+
"dependencies": {
355
+
"color-name": "~1.1.4"
356
+
},
357
+
"engines": {
358
+
"node": ">=7.0.0"
359
+
}
360
+
},
361
+
"node_modules/color-name": {
362
+
"version": "1.1.4",
363
+
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
364
+
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
365
+
"dev": true,
366
+
"license": "MIT"
367
+
},
368
+
"node_modules/concat-map": {
369
+
"version": "0.0.1",
370
+
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
371
+
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
372
+
"dev": true,
373
+
"license": "MIT"
374
+
},
375
+
"node_modules/cross-spawn": {
376
+
"version": "7.0.6",
377
+
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
378
+
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
379
+
"dev": true,
380
+
"license": "MIT",
381
+
"dependencies": {
382
+
"path-key": "^3.1.0",
383
+
"shebang-command": "^2.0.0",
384
+
"which": "^2.0.1"
385
+
},
386
+
"engines": {
387
+
"node": ">= 8"
388
+
}
389
+
},
390
+
"node_modules/debug": {
391
+
"version": "4.4.3",
392
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
393
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
394
+
"dev": true,
395
+
"license": "MIT",
396
+
"dependencies": {
397
+
"ms": "^2.1.3"
398
+
},
399
+
"engines": {
400
+
"node": ">=6.0"
401
+
},
402
+
"peerDependenciesMeta": {
403
+
"supports-color": {
404
+
"optional": true
405
+
}
406
+
}
407
+
},
408
+
"node_modules/deep-is": {
409
+
"version": "0.1.4",
410
+
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
411
+
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
412
+
"dev": true,
413
+
"license": "MIT"
414
+
},
415
+
"node_modules/escape-string-regexp": {
416
+
"version": "4.0.0",
417
+
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
418
+
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
419
+
"dev": true,
420
+
"license": "MIT",
421
+
"engines": {
422
+
"node": ">=10"
423
+
},
424
+
"funding": {
425
+
"url": "https://github.com/sponsors/sindresorhus"
426
+
}
427
+
},
428
+
"node_modules/eslint": {
429
+
"version": "9.39.2",
430
+
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
431
+
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
432
+
"dev": true,
433
+
"license": "MIT",
434
+
"peer": true,
435
+
"dependencies": {
436
+
"@eslint-community/eslint-utils": "^4.8.0",
437
+
"@eslint-community/regexpp": "^4.12.1",
438
+
"@eslint/config-array": "^0.21.1",
439
+
"@eslint/config-helpers": "^0.4.2",
440
+
"@eslint/core": "^0.17.0",
441
+
"@eslint/eslintrc": "^3.3.1",
442
+
"@eslint/js": "9.39.2",
443
+
"@eslint/plugin-kit": "^0.4.1",
444
+
"@humanfs/node": "^0.16.6",
445
+
"@humanwhocodes/module-importer": "^1.0.1",
446
+
"@humanwhocodes/retry": "^0.4.2",
447
+
"@types/estree": "^1.0.6",
448
+
"ajv": "^6.12.4",
449
+
"chalk": "^4.0.0",
450
+
"cross-spawn": "^7.0.6",
451
+
"debug": "^4.3.2",
452
+
"escape-string-regexp": "^4.0.0",
453
+
"eslint-scope": "^8.4.0",
454
+
"eslint-visitor-keys": "^4.2.1",
455
+
"espree": "^10.4.0",
456
+
"esquery": "^1.5.0",
457
+
"esutils": "^2.0.2",
458
+
"fast-deep-equal": "^3.1.3",
459
+
"file-entry-cache": "^8.0.0",
460
+
"find-up": "^5.0.0",
461
+
"glob-parent": "^6.0.2",
462
+
"ignore": "^5.2.0",
463
+
"imurmurhash": "^0.1.4",
464
+
"is-glob": "^4.0.0",
465
+
"json-stable-stringify-without-jsonify": "^1.0.1",
466
+
"lodash.merge": "^4.6.2",
467
+
"minimatch": "^3.1.2",
468
+
"natural-compare": "^1.4.0",
469
+
"optionator": "^0.9.3"
470
+
},
471
+
"bin": {
472
+
"eslint": "bin/eslint.js"
473
+
},
474
+
"engines": {
475
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
476
+
},
477
+
"funding": {
478
+
"url": "https://eslint.org/donate"
479
+
},
480
+
"peerDependencies": {
481
+
"jiti": "*"
482
+
},
483
+
"peerDependenciesMeta": {
484
+
"jiti": {
485
+
"optional": true
486
+
}
487
+
}
488
+
},
489
+
"node_modules/eslint-scope": {
490
+
"version": "8.4.0",
491
+
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
492
+
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
493
+
"dev": true,
494
+
"license": "BSD-2-Clause",
495
+
"dependencies": {
496
+
"esrecurse": "^4.3.0",
497
+
"estraverse": "^5.2.0"
498
+
},
499
+
"engines": {
500
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
501
+
},
502
+
"funding": {
503
+
"url": "https://opencollective.com/eslint"
504
+
}
505
+
},
506
+
"node_modules/eslint-visitor-keys": {
507
+
"version": "4.2.1",
508
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
509
+
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
510
+
"dev": true,
511
+
"license": "Apache-2.0",
512
+
"engines": {
513
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
514
+
},
515
+
"funding": {
516
+
"url": "https://opencollective.com/eslint"
517
+
}
518
+
},
519
+
"node_modules/espree": {
520
+
"version": "10.4.0",
521
+
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
522
+
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
523
+
"dev": true,
524
+
"license": "BSD-2-Clause",
525
+
"dependencies": {
526
+
"acorn": "^8.15.0",
527
+
"acorn-jsx": "^5.3.2",
528
+
"eslint-visitor-keys": "^4.2.1"
529
+
},
530
+
"engines": {
531
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
532
+
},
533
+
"funding": {
534
+
"url": "https://opencollective.com/eslint"
535
+
}
536
+
},
537
+
"node_modules/esquery": {
538
+
"version": "1.7.0",
539
+
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
540
+
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
541
+
"dev": true,
542
+
"license": "BSD-3-Clause",
543
+
"dependencies": {
544
+
"estraverse": "^5.1.0"
545
+
},
546
+
"engines": {
547
+
"node": ">=0.10"
548
+
}
549
+
},
550
+
"node_modules/esrecurse": {
551
+
"version": "4.3.0",
552
+
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
553
+
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
554
+
"dev": true,
555
+
"license": "BSD-2-Clause",
556
+
"dependencies": {
557
+
"estraverse": "^5.2.0"
558
+
},
559
+
"engines": {
560
+
"node": ">=4.0"
561
+
}
562
+
},
563
+
"node_modules/estraverse": {
564
+
"version": "5.3.0",
565
+
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
566
+
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
567
+
"dev": true,
568
+
"license": "BSD-2-Clause",
569
+
"engines": {
570
+
"node": ">=4.0"
571
+
}
572
+
},
573
+
"node_modules/esutils": {
574
+
"version": "2.0.3",
575
+
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
576
+
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
577
+
"dev": true,
578
+
"license": "BSD-2-Clause",
579
+
"engines": {
580
+
"node": ">=0.10.0"
581
+
}
582
+
},
583
+
"node_modules/fast-deep-equal": {
584
+
"version": "3.1.3",
585
+
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
586
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
587
+
"dev": true,
588
+
"license": "MIT"
589
+
},
590
+
"node_modules/fast-json-stable-stringify": {
591
+
"version": "2.1.0",
592
+
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
593
+
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
594
+
"dev": true,
595
+
"license": "MIT"
596
+
},
597
+
"node_modules/fast-levenshtein": {
598
+
"version": "2.0.6",
599
+
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
600
+
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
601
+
"dev": true,
602
+
"license": "MIT"
603
+
},
604
+
"node_modules/file-entry-cache": {
605
+
"version": "8.0.0",
606
+
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
607
+
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
608
+
"dev": true,
609
+
"license": "MIT",
610
+
"dependencies": {
611
+
"flat-cache": "^4.0.0"
612
+
},
613
+
"engines": {
614
+
"node": ">=16.0.0"
615
+
}
616
+
},
617
+
"node_modules/find-up": {
618
+
"version": "5.0.0",
619
+
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
620
+
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
621
+
"dev": true,
622
+
"license": "MIT",
623
+
"dependencies": {
624
+
"locate-path": "^6.0.0",
625
+
"path-exists": "^4.0.0"
626
+
},
627
+
"engines": {
628
+
"node": ">=10"
629
+
},
630
+
"funding": {
631
+
"url": "https://github.com/sponsors/sindresorhus"
632
+
}
633
+
},
634
+
"node_modules/flat-cache": {
635
+
"version": "4.0.1",
636
+
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
637
+
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
638
+
"dev": true,
639
+
"license": "MIT",
640
+
"dependencies": {
641
+
"flatted": "^3.2.9",
642
+
"keyv": "^4.5.4"
643
+
},
644
+
"engines": {
645
+
"node": ">=16"
646
+
}
647
+
},
648
+
"node_modules/flatted": {
649
+
"version": "3.3.3",
650
+
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
651
+
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
652
+
"dev": true,
653
+
"license": "ISC"
654
+
},
655
+
"node_modules/glob-parent": {
656
+
"version": "6.0.2",
657
+
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
658
+
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
659
+
"dev": true,
660
+
"license": "ISC",
661
+
"dependencies": {
662
+
"is-glob": "^4.0.3"
663
+
},
664
+
"engines": {
665
+
"node": ">=10.13.0"
666
+
}
667
+
},
668
+
"node_modules/globals": {
669
+
"version": "17.0.0",
670
+
"resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz",
671
+
"integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==",
672
+
"dev": true,
673
+
"license": "MIT",
674
+
"engines": {
675
+
"node": ">=18"
676
+
},
677
+
"funding": {
678
+
"url": "https://github.com/sponsors/sindresorhus"
679
+
}
680
+
},
681
+
"node_modules/has-flag": {
682
+
"version": "4.0.0",
683
+
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
684
+
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
685
+
"dev": true,
686
+
"license": "MIT",
687
+
"engines": {
688
+
"node": ">=8"
689
+
}
690
+
},
691
+
"node_modules/ignore": {
692
+
"version": "5.3.2",
693
+
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
694
+
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
695
+
"dev": true,
696
+
"license": "MIT",
697
+
"engines": {
698
+
"node": ">= 4"
699
+
}
700
+
},
701
+
"node_modules/import-fresh": {
702
+
"version": "3.3.1",
703
+
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
704
+
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
705
+
"dev": true,
706
+
"license": "MIT",
707
+
"dependencies": {
708
+
"parent-module": "^1.0.0",
709
+
"resolve-from": "^4.0.0"
710
+
},
711
+
"engines": {
712
+
"node": ">=6"
713
+
},
714
+
"funding": {
715
+
"url": "https://github.com/sponsors/sindresorhus"
716
+
}
717
+
},
718
+
"node_modules/imurmurhash": {
719
+
"version": "0.1.4",
720
+
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
721
+
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
722
+
"dev": true,
723
+
"license": "MIT",
724
+
"engines": {
725
+
"node": ">=0.8.19"
726
+
}
727
+
},
728
+
"node_modules/is-extglob": {
729
+
"version": "2.1.1",
730
+
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
731
+
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
732
+
"dev": true,
733
+
"license": "MIT",
734
+
"engines": {
735
+
"node": ">=0.10.0"
736
+
}
737
+
},
738
+
"node_modules/is-glob": {
739
+
"version": "4.0.3",
740
+
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
741
+
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
742
+
"dev": true,
743
+
"license": "MIT",
744
+
"dependencies": {
745
+
"is-extglob": "^2.1.1"
746
+
},
747
+
"engines": {
748
+
"node": ">=0.10.0"
749
+
}
750
+
},
751
+
"node_modules/isexe": {
752
+
"version": "2.0.0",
753
+
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
754
+
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
755
+
"dev": true,
756
+
"license": "ISC"
757
+
},
758
+
"node_modules/js-yaml": {
759
+
"version": "4.1.1",
760
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
761
+
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
762
+
"dev": true,
763
+
"license": "MIT",
764
+
"dependencies": {
765
+
"argparse": "^2.0.1"
766
+
},
767
+
"bin": {
768
+
"js-yaml": "bin/js-yaml.js"
769
+
}
770
+
},
771
+
"node_modules/json-buffer": {
772
+
"version": "3.0.1",
773
+
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
774
+
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
775
+
"dev": true,
776
+
"license": "MIT"
777
+
},
778
+
"node_modules/json-schema-traverse": {
779
+
"version": "0.4.1",
780
+
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
781
+
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
782
+
"dev": true,
783
+
"license": "MIT"
784
+
},
785
+
"node_modules/json-stable-stringify-without-jsonify": {
786
+
"version": "1.0.1",
787
+
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
788
+
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
789
+
"dev": true,
790
+
"license": "MIT"
791
+
},
792
+
"node_modules/keyv": {
793
+
"version": "4.5.4",
794
+
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
795
+
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
796
+
"dev": true,
797
+
"license": "MIT",
798
+
"dependencies": {
799
+
"json-buffer": "3.0.1"
800
+
}
801
+
},
802
+
"node_modules/levn": {
803
+
"version": "0.4.1",
804
+
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
805
+
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
806
+
"dev": true,
807
+
"license": "MIT",
808
+
"dependencies": {
809
+
"prelude-ls": "^1.2.1",
810
+
"type-check": "~0.4.0"
811
+
},
812
+
"engines": {
813
+
"node": ">= 0.8.0"
814
+
}
815
+
},
816
+
"node_modules/locate-path": {
817
+
"version": "6.0.0",
818
+
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
819
+
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
820
+
"dev": true,
821
+
"license": "MIT",
822
+
"dependencies": {
823
+
"p-locate": "^5.0.0"
824
+
},
825
+
"engines": {
826
+
"node": ">=10"
827
+
},
828
+
"funding": {
829
+
"url": "https://github.com/sponsors/sindresorhus"
830
+
}
831
+
},
832
+
"node_modules/lodash.merge": {
833
+
"version": "4.6.2",
834
+
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
835
+
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
836
+
"dev": true,
837
+
"license": "MIT"
838
+
},
839
+
"node_modules/minimatch": {
840
+
"version": "3.1.2",
841
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
842
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
843
+
"dev": true,
844
+
"license": "ISC",
845
+
"dependencies": {
846
+
"brace-expansion": "^1.1.7"
847
+
},
848
+
"engines": {
849
+
"node": "*"
850
+
}
851
+
},
852
+
"node_modules/ms": {
853
+
"version": "2.1.3",
854
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
855
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
856
+
"dev": true,
857
+
"license": "MIT"
858
+
},
859
+
"node_modules/natural-compare": {
860
+
"version": "1.4.0",
861
+
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
862
+
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
863
+
"dev": true,
864
+
"license": "MIT"
865
+
},
866
+
"node_modules/optionator": {
867
+
"version": "0.9.4",
868
+
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
869
+
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
870
+
"dev": true,
871
+
"license": "MIT",
872
+
"dependencies": {
873
+
"deep-is": "^0.1.3",
874
+
"fast-levenshtein": "^2.0.6",
875
+
"levn": "^0.4.1",
876
+
"prelude-ls": "^1.2.1",
877
+
"type-check": "^0.4.0",
878
+
"word-wrap": "^1.2.5"
879
+
},
880
+
"engines": {
881
+
"node": ">= 0.8.0"
882
+
}
883
+
},
884
+
"node_modules/p-limit": {
885
+
"version": "3.1.0",
886
+
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
887
+
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
888
+
"dev": true,
889
+
"license": "MIT",
890
+
"dependencies": {
891
+
"yocto-queue": "^0.1.0"
892
+
},
893
+
"engines": {
894
+
"node": ">=10"
895
+
},
896
+
"funding": {
897
+
"url": "https://github.com/sponsors/sindresorhus"
898
+
}
899
+
},
900
+
"node_modules/p-locate": {
901
+
"version": "5.0.0",
902
+
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
903
+
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
904
+
"dev": true,
905
+
"license": "MIT",
906
+
"dependencies": {
907
+
"p-limit": "^3.0.2"
908
+
},
909
+
"engines": {
910
+
"node": ">=10"
911
+
},
912
+
"funding": {
913
+
"url": "https://github.com/sponsors/sindresorhus"
914
+
}
915
+
},
916
+
"node_modules/parent-module": {
917
+
"version": "1.0.1",
918
+
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
919
+
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
920
+
"dev": true,
921
+
"license": "MIT",
922
+
"dependencies": {
923
+
"callsites": "^3.0.0"
924
+
},
925
+
"engines": {
926
+
"node": ">=6"
927
+
}
928
+
},
929
+
"node_modules/path-exists": {
930
+
"version": "4.0.0",
931
+
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
932
+
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
933
+
"dev": true,
934
+
"license": "MIT",
935
+
"engines": {
936
+
"node": ">=8"
937
+
}
938
+
},
939
+
"node_modules/path-key": {
940
+
"version": "3.1.1",
941
+
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
942
+
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
943
+
"dev": true,
944
+
"license": "MIT",
945
+
"engines": {
946
+
"node": ">=8"
947
+
}
948
+
},
949
+
"node_modules/prelude-ls": {
950
+
"version": "1.2.1",
951
+
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
952
+
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
953
+
"dev": true,
954
+
"license": "MIT",
955
+
"engines": {
956
+
"node": ">= 0.8.0"
957
+
}
958
+
},
959
+
"node_modules/punycode": {
960
+
"version": "2.3.1",
961
+
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
962
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
963
+
"dev": true,
964
+
"license": "MIT",
965
+
"engines": {
966
+
"node": ">=6"
967
+
}
968
+
},
969
+
"node_modules/resolve-from": {
970
+
"version": "4.0.0",
971
+
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
972
+
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
973
+
"dev": true,
974
+
"license": "MIT",
975
+
"engines": {
976
+
"node": ">=4"
977
+
}
978
+
},
979
+
"node_modules/shebang-command": {
980
+
"version": "2.0.0",
981
+
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
982
+
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
983
+
"dev": true,
984
+
"license": "MIT",
985
+
"dependencies": {
986
+
"shebang-regex": "^3.0.0"
987
+
},
988
+
"engines": {
989
+
"node": ">=8"
990
+
}
991
+
},
992
+
"node_modules/shebang-regex": {
993
+
"version": "3.0.0",
994
+
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
995
+
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
996
+
"dev": true,
997
+
"license": "MIT",
998
+
"engines": {
999
+
"node": ">=8"
1000
+
}
1001
+
},
1002
+
"node_modules/strip-json-comments": {
1003
+
"version": "3.1.1",
1004
+
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
1005
+
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
1006
+
"dev": true,
1007
+
"license": "MIT",
1008
+
"engines": {
1009
+
"node": ">=8"
1010
+
},
1011
+
"funding": {
1012
+
"url": "https://github.com/sponsors/sindresorhus"
1013
+
}
1014
+
},
1015
+
"node_modules/supports-color": {
1016
+
"version": "7.2.0",
1017
+
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
1018
+
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
1019
+
"dev": true,
1020
+
"license": "MIT",
1021
+
"dependencies": {
1022
+
"has-flag": "^4.0.0"
1023
+
},
1024
+
"engines": {
1025
+
"node": ">=8"
1026
+
}
1027
+
},
1028
+
"node_modules/type-check": {
1029
+
"version": "0.4.0",
1030
+
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
1031
+
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
1032
+
"dev": true,
1033
+
"license": "MIT",
1034
+
"dependencies": {
1035
+
"prelude-ls": "^1.2.1"
1036
+
},
1037
+
"engines": {
1038
+
"node": ">= 0.8.0"
1039
+
}
1040
+
},
1041
+
"node_modules/uri-js": {
1042
+
"version": "4.4.1",
1043
+
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
1044
+
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
1045
+
"dev": true,
1046
+
"license": "BSD-2-Clause",
1047
+
"dependencies": {
1048
+
"punycode": "^2.1.0"
1049
+
}
1050
+
},
1051
+
"node_modules/which": {
1052
+
"version": "2.0.2",
1053
+
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
1054
+
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
1055
+
"dev": true,
1056
+
"license": "ISC",
1057
+
"dependencies": {
1058
+
"isexe": "^2.0.0"
1059
+
},
1060
+
"bin": {
1061
+
"node-which": "bin/node-which"
1062
+
},
1063
+
"engines": {
1064
+
"node": ">= 8"
1065
+
}
1066
+
},
1067
+
"node_modules/word-wrap": {
1068
+
"version": "1.2.5",
1069
+
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
1070
+
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
1071
+
"dev": true,
1072
+
"license": "MIT",
1073
+
"engines": {
1074
+
"node": ">=0.10.0"
1075
+
}
1076
+
},
1077
+
"node_modules/yocto-queue": {
1078
+
"version": "0.1.0",
1079
+
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
1080
+
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
1081
+
"dev": true,
1082
+
"license": "MIT",
1083
+
"engines": {
1084
+
"node": ">=10"
1085
+
},
1086
+
"funding": {
1087
+
"url": "https://github.com/sponsors/sindresorhus"
1088
+
}
1089
+
}
1090
+
}
1091
+
}
+14
extension/package.json
+14
extension/package.json
+62
extension/popup/popup.css
+62
extension/popup/popup.css
···
648
648
gap: 8px;
649
649
margin-left: auto;
650
650
}
651
+
652
+
.toggle-switch {
653
+
position: relative;
654
+
display: inline-block;
655
+
width: 44px;
656
+
height: 24px;
657
+
flex-shrink: 0;
658
+
}
659
+
660
+
.toggle-switch input {
661
+
opacity: 0;
662
+
width: 0;
663
+
height: 0;
664
+
}
665
+
666
+
.toggle-slider {
667
+
position: absolute;
668
+
cursor: pointer;
669
+
top: 0;
670
+
left: 0;
671
+
right: 0;
672
+
bottom: 0;
673
+
background-color: var(--border);
674
+
transition: 0.2s;
675
+
border-radius: 24px;
676
+
}
677
+
678
+
.toggle-slider:before {
679
+
position: absolute;
680
+
content: "";
681
+
height: 18px;
682
+
width: 18px;
683
+
left: 3px;
684
+
bottom: 3px;
685
+
background-color: var(--text-secondary);
686
+
transition: 0.2s;
687
+
border-radius: 50%;
688
+
}
689
+
690
+
.toggle-switch input:checked + .toggle-slider {
691
+
background-color: var(--accent);
692
+
}
693
+
694
+
.toggle-switch input:checked + .toggle-slider:before {
695
+
transform: translateX(20px);
696
+
background-color: white;
697
+
}
698
+
699
+
.settings-input {
700
+
width: 100%;
701
+
padding: 10px 12px;
702
+
background: var(--bg-tertiary);
703
+
border: 1px solid var(--border);
704
+
border-radius: var(--radius-md);
705
+
color: var(--text-primary);
706
+
font-size: 13px;
707
+
}
708
+
709
+
.settings-input:focus {
710
+
outline: none;
711
+
border-color: var(--accent);
712
+
}
+20
extension/popup/popup.html
+20
extension/popup/popup.html
···
218
218
<button id="close-settings" class="btn-icon">ร</button>
219
219
</div>
220
220
<div class="setting-item">
221
+
<div
222
+
style="
223
+
display: flex;
224
+
justify-content: space-between;
225
+
align-items: center;
226
+
"
227
+
>
228
+
<div>
229
+
<label>Show page overlays</label>
230
+
<p class="setting-help" style="margin-top: 2px">
231
+
Highlights, badges, and tooltips on pages
232
+
</p>
233
+
</div>
234
+
<label class="toggle-switch">
235
+
<input type="checkbox" id="overlay-toggle" checked />
236
+
<span class="toggle-slider"></span>
237
+
</label>
238
+
</div>
239
+
</div>
240
+
<div class="setting-item">
221
241
<label for="api-url">API URL (for self-hosting)</label>
222
242
<input
223
243
type="url"
+33
-17
extension/popup/popup.js
+33
-17
extension/popup/popup.js
···
39
39
collectionList: document.getElementById("collection-list"),
40
40
collectionLoading: document.getElementById("collection-loading"),
41
41
collectionsEmpty: document.getElementById("collections-empty"),
42
+
overlayToggle: document.getElementById("overlay-toggle"),
42
43
};
43
44
44
45
let currentTab = null;
45
46
let apiUrl = "https://margin.at";
46
47
let currentUserDid = null;
47
48
let pendingSelector = null;
48
-
let activeAnnotationUriForCollection = null;
49
+
// let _activeAnnotationUriForCollection = null;
49
50
50
-
const storage = await browserAPI.storage.local.get(["apiUrl"]);
51
+
const storage = await browserAPI.storage.local.get(["apiUrl", "showOverlay"]);
51
52
if (storage.apiUrl) {
52
53
apiUrl = storage.apiUrl;
53
54
}
54
55
els.apiUrlInput.value = apiUrl;
56
+
57
+
if (els.overlayToggle) {
58
+
els.overlayToggle.checked = storage.showOverlay !== false;
59
+
}
55
60
56
61
try {
57
62
const [tab] = await browserAPI.tabs.query({
···
74
79
pendingData = sessionData.pendingAnnotation;
75
80
await browserAPI.storage.session.remove(["pendingAnnotation"]);
76
81
}
77
-
} catch (e) {}
82
+
} catch {
83
+
/* ignore */
84
+
}
78
85
}
79
86
80
87
if (!pendingData) {
···
209
216
210
217
els.saveSettings?.addEventListener("click", async () => {
211
218
const newUrl = els.apiUrlInput.value.replace(/\/$/, "");
219
+
const showOverlay = els.overlayToggle?.checked ?? true;
220
+
221
+
await browserAPI.storage.local.set({ apiUrl: newUrl, showOverlay });
212
222
if (newUrl) {
213
-
await browserAPI.storage.local.set({ apiUrl: newUrl });
214
223
apiUrl = newUrl;
215
-
await sendMessage({ type: "UPDATE_SETTINGS" });
216
-
views.settings.style.display = "none";
217
-
checkSession();
218
224
}
225
+
await sendMessage({ type: "UPDATE_SETTINGS" });
226
+
227
+
const tabs = await browserAPI.tabs.query({});
228
+
for (const tab of tabs) {
229
+
if (tab.id) {
230
+
try {
231
+
await browserAPI.tabs.sendMessage(tab.id, {
232
+
type: "UPDATE_OVERLAY_VISIBILITY",
233
+
show: showOverlay,
234
+
});
235
+
} catch {
236
+
/* ignore */
237
+
}
238
+
}
239
+
}
240
+
241
+
views.settings.style.display = "none";
242
+
checkSession();
219
243
});
220
244
221
245
els.closeCollectionSelector?.addEventListener("click", () => {
222
246
views.collectionSelector.style.display = "none";
223
-
activeAnnotationUriForCollection = null;
224
247
});
225
248
226
249
async function openCollectionSelector(annotationUri) {
···
228
251
console.error("No currentUserDid, returning early");
229
252
return;
230
253
}
231
-
activeAnnotationUriForCollection = annotationUri;
232
254
views.collectionSelector.style.display = "flex";
233
255
els.collectionList.innerHTML = "";
234
256
els.collectionLoading.style.display = "block";
···
507
529
const actions = document.createElement("div");
508
530
actions.className = "annotation-item-actions";
509
531
510
-
if (
511
-
item.author?.did === currentUserDid ||
512
-
item.creator?.did === currentUserDid
513
-
) {
532
+
if (currentUserDid) {
514
533
const folderBtn = document.createElement("button");
515
534
folderBtn.className = "btn-icon";
516
535
folderBtn.innerHTML =
···
580
599
581
600
row.appendChild(content);
582
601
583
-
if (
584
-
item.author?.did === currentUserDid ||
585
-
item.creator?.did === currentUserDid
586
-
) {
602
+
if (currentUserDid) {
587
603
const folderBtn = document.createElement("button");
588
604
folderBtn.className = "btn-icon";
589
605
folderBtn.innerHTML =
+47
extension/sidepanel/sidepanel.css
+47
extension/sidepanel/sidepanel.css
···
882
882
gap: 8px;
883
883
margin-left: auto;
884
884
}
885
+
886
+
.toggle-switch {
887
+
position: relative;
888
+
display: inline-block;
889
+
width: 44px;
890
+
height: 24px;
891
+
flex-shrink: 0;
892
+
}
893
+
894
+
.toggle-switch input {
895
+
opacity: 0;
896
+
width: 0;
897
+
height: 0;
898
+
}
899
+
900
+
.toggle-slider {
901
+
position: absolute;
902
+
cursor: pointer;
903
+
top: 0;
904
+
left: 0;
905
+
right: 0;
906
+
bottom: 0;
907
+
background-color: var(--border);
908
+
transition: 0.2s;
909
+
border-radius: 24px;
910
+
}
911
+
912
+
.toggle-slider:before {
913
+
position: absolute;
914
+
content: "";
915
+
height: 18px;
916
+
width: 18px;
917
+
left: 3px;
918
+
bottom: 3px;
919
+
background-color: var(--text-secondary);
920
+
transition: 0.2s;
921
+
border-radius: 50%;
922
+
}
923
+
924
+
.toggle-switch input:checked + .toggle-slider {
925
+
background-color: var(--accent);
926
+
}
927
+
928
+
.toggle-switch input:checked + .toggle-slider:before {
929
+
transform: translateX(20px);
930
+
background-color: white;
931
+
}
+20
extension/sidepanel/sidepanel.html
+20
extension/sidepanel/sidepanel.html
···
250
250
<button id="close-settings" class="btn-icon">ร</button>
251
251
</div>
252
252
<div class="setting-item">
253
+
<div
254
+
style="
255
+
display: flex;
256
+
justify-content: space-between;
257
+
align-items: center;
258
+
"
259
+
>
260
+
<div>
261
+
<label>Show page overlays</label>
262
+
<p class="setting-help" style="margin-top: 2px">
263
+
Display highlights, badges, and tooltips on pages
264
+
</p>
265
+
</div>
266
+
<label class="toggle-switch">
267
+
<input type="checkbox" id="overlay-toggle" checked />
268
+
<span class="toggle-slider"></span>
269
+
</label>
270
+
</div>
271
+
</div>
272
+
<div class="setting-item">
253
273
<label for="api-url">API URL</label>
254
274
<input
255
275
type="url"
+36
-21
extension/sidepanel/sidepanel.js
+36
-21
extension/sidepanel/sidepanel.js
···
37
37
collectionList: document.getElementById("collection-list"),
38
38
collectionLoading: document.getElementById("collection-loading"),
39
39
collectionsEmpty: document.getElementById("collections-empty"),
40
+
overlayToggle: document.getElementById("overlay-toggle"),
40
41
};
41
42
42
43
let currentTab = null;
43
44
let apiUrl = "";
44
45
let currentUserDid = null;
45
46
let pendingSelector = null;
46
-
let activeAnnotationUriForCollection = null;
47
47
48
48
const storage = await chrome.storage.local.get(["apiUrl"]);
49
49
if (storage.apiUrl) {
···
51
51
}
52
52
53
53
els.apiUrlInput.value = apiUrl;
54
+
55
+
const overlayStorage = await chrome.storage.local.get(["showOverlay"]);
56
+
if (els.overlayToggle) {
57
+
els.overlayToggle.checked = overlayStorage.showOverlay !== false;
58
+
}
54
59
55
60
chrome.storage.onChanged.addListener((changes, area) => {
56
61
if (area === "local" && changes.apiUrl) {
···
253
258
254
259
els.closeCollectionSelector?.addEventListener("click", () => {
255
260
views.collectionSelector.style.display = "none";
256
-
activeAnnotationUriForCollection = null;
257
261
});
258
262
259
263
els.saveSettings?.addEventListener("click", async () => {
260
264
const newUrl = els.apiUrlInput.value.replace(/\/$/, "");
265
+
const showOverlay = els.overlayToggle?.checked ?? true;
266
+
267
+
await chrome.storage.local.set({ apiUrl: newUrl, showOverlay });
261
268
if (newUrl) {
262
-
await chrome.storage.local.set({ apiUrl: newUrl });
263
269
apiUrl = newUrl;
264
-
await sendMessage({ type: "UPDATE_SETTINGS" });
265
-
views.settings.style.display = "none";
266
-
checkSession();
270
+
}
271
+
await sendMessage({ type: "UPDATE_SETTINGS" });
272
+
273
+
const tabs = await chrome.tabs.query({});
274
+
for (const tab of tabs) {
275
+
if (tab.id) {
276
+
try {
277
+
await chrome.tabs.sendMessage(tab.id, {
278
+
type: "UPDATE_OVERLAY_VISIBILITY",
279
+
show: showOverlay,
280
+
});
281
+
} catch {
282
+
/* ignore */
283
+
}
284
+
}
267
285
}
286
+
287
+
views.settings.style.display = "none";
288
+
checkSession();
268
289
});
269
290
270
291
els.signOutBtn?.addEventListener("click", async () => {
···
367
388
console.error("No currentUserDid, returning early");
368
389
return;
369
390
}
370
-
activeAnnotationUriForCollection = annotationUri;
371
391
views.collectionSelector.style.display = "flex";
372
392
els.collectionList.innerHTML = "";
373
393
els.collectionLoading.style.display = "block";
···
561
581
header.appendChild(badge);
562
582
}
563
583
564
-
if (
565
-
item.author?.did === currentUserDid ||
566
-
item.creator?.did === currentUserDid
567
-
) {
584
+
if (currentUserDid) {
568
585
const actions = document.createElement("div");
569
586
actions.className = "annotation-item-actions";
570
587
···
635
652
let hostname = item.source;
636
653
try {
637
654
hostname = new URL(item.source).hostname;
638
-
} catch {}
655
+
} catch {
656
+
/* ignore */
657
+
}
639
658
640
659
const row = document.createElement("div");
641
660
row.style.display = "flex";
···
658
677
659
678
row.appendChild(content);
660
679
661
-
if (
662
-
item.author?.did === currentUserDid ||
663
-
item.creator?.did === currentUserDid
664
-
) {
680
+
if (currentUserDid) {
665
681
const folderBtn = document.createElement("button");
666
682
folderBtn.className = "btn-icon";
667
683
folderBtn.innerHTML =
···
701
717
let hostname = url;
702
718
try {
703
719
hostname = new URL(url).hostname;
704
-
} catch {}
720
+
} catch {
721
+
/* ignore */
722
+
}
705
723
706
724
const header = document.createElement("div");
707
725
header.className = "annotation-item-header";
···
721
739
722
740
header.appendChild(meta);
723
741
724
-
if (
725
-
item.author?.did === currentUserDid ||
726
-
item.creator?.did === currentUserDid
727
-
) {
742
+
if (currentUserDid) {
728
743
const actions = document.createElement("div");
729
744
actions.className = "annotation-item-actions";
730
745
+40
web/eslint.config.js
+40
web/eslint.config.js
···
1
+
import js from "@eslint/js";
2
+
import globals from "globals";
3
+
import react from "eslint-plugin-react";
4
+
import reactHooks from "eslint-plugin-react-hooks";
5
+
import reactRefresh from "eslint-plugin-react-refresh";
6
+
7
+
export default [
8
+
{ ignores: ["dist"] },
9
+
{
10
+
files: ["**/*.{js,jsx}"],
11
+
languageOptions: {
12
+
ecmaVersion: 2020,
13
+
globals: globals.browser,
14
+
parserOptions: {
15
+
ecmaVersion: "latest",
16
+
ecmaFeatures: { jsx: true },
17
+
sourceType: "module",
18
+
},
19
+
},
20
+
settings: { react: { version: "18.3" } },
21
+
plugins: {
22
+
react,
23
+
"react-hooks": reactHooks,
24
+
"react-refresh": reactRefresh,
25
+
},
26
+
rules: {
27
+
...js.configs.recommended.rules,
28
+
...react.configs.recommended.rules,
29
+
...react.configs["jsx-runtime"].rules,
30
+
...reactHooks.configs.recommended.rules,
31
+
"react/jsx-no-target-blank": "off",
32
+
"react-refresh/only-export-components": [
33
+
"warn",
34
+
{ allowConstantExport: true },
35
+
],
36
+
"no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
37
+
"react/prop-types": "off",
38
+
},
39
+
},
40
+
];
+3051
-12
web/package-lock.json
+3051
-12
web/package-lock.json
···
15
15
"react-router-dom": "^6.28.0"
16
16
},
17
17
"devDependencies": {
18
+
"@eslint/js": "^9.39.2",
18
19
"@types/react": "^18.3.12",
19
20
"@types/react-dom": "^18.3.1",
20
21
"@vitejs/plugin-react": "^4.3.3",
22
+
"eslint": "^9.39.2",
23
+
"eslint-plugin-react": "^7.37.5",
24
+
"eslint-plugin-react-hooks": "^7.0.1",
25
+
"eslint-plugin-react-refresh": "^0.4.26",
26
+
"globals": "^17.0.0",
21
27
"vite": "^6.0.3"
22
28
}
23
29
},
···
746
752
"node": ">=18"
747
753
}
748
754
},
755
+
"node_modules/@eslint-community/eslint-utils": {
756
+
"version": "4.9.1",
757
+
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
758
+
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
759
+
"dev": true,
760
+
"license": "MIT",
761
+
"dependencies": {
762
+
"eslint-visitor-keys": "^3.4.3"
763
+
},
764
+
"engines": {
765
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
766
+
},
767
+
"funding": {
768
+
"url": "https://opencollective.com/eslint"
769
+
},
770
+
"peerDependencies": {
771
+
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
772
+
}
773
+
},
774
+
"node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
775
+
"version": "3.4.3",
776
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
777
+
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
778
+
"dev": true,
779
+
"license": "Apache-2.0",
780
+
"engines": {
781
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
782
+
},
783
+
"funding": {
784
+
"url": "https://opencollective.com/eslint"
785
+
}
786
+
},
787
+
"node_modules/@eslint-community/regexpp": {
788
+
"version": "4.12.2",
789
+
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
790
+
"integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
791
+
"dev": true,
792
+
"license": "MIT",
793
+
"engines": {
794
+
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
795
+
}
796
+
},
797
+
"node_modules/@eslint/config-array": {
798
+
"version": "0.21.1",
799
+
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
800
+
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
801
+
"dev": true,
802
+
"license": "Apache-2.0",
803
+
"dependencies": {
804
+
"@eslint/object-schema": "^2.1.7",
805
+
"debug": "^4.3.1",
806
+
"minimatch": "^3.1.2"
807
+
},
808
+
"engines": {
809
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
810
+
}
811
+
},
812
+
"node_modules/@eslint/config-helpers": {
813
+
"version": "0.4.2",
814
+
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
815
+
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
816
+
"dev": true,
817
+
"license": "Apache-2.0",
818
+
"dependencies": {
819
+
"@eslint/core": "^0.17.0"
820
+
},
821
+
"engines": {
822
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
823
+
}
824
+
},
825
+
"node_modules/@eslint/core": {
826
+
"version": "0.17.0",
827
+
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
828
+
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
829
+
"dev": true,
830
+
"license": "Apache-2.0",
831
+
"dependencies": {
832
+
"@types/json-schema": "^7.0.15"
833
+
},
834
+
"engines": {
835
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
836
+
}
837
+
},
838
+
"node_modules/@eslint/eslintrc": {
839
+
"version": "3.3.3",
840
+
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
841
+
"integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
842
+
"dev": true,
843
+
"license": "MIT",
844
+
"dependencies": {
845
+
"ajv": "^6.12.4",
846
+
"debug": "^4.3.2",
847
+
"espree": "^10.0.1",
848
+
"globals": "^14.0.0",
849
+
"ignore": "^5.2.0",
850
+
"import-fresh": "^3.2.1",
851
+
"js-yaml": "^4.1.1",
852
+
"minimatch": "^3.1.2",
853
+
"strip-json-comments": "^3.1.1"
854
+
},
855
+
"engines": {
856
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
857
+
},
858
+
"funding": {
859
+
"url": "https://opencollective.com/eslint"
860
+
}
861
+
},
862
+
"node_modules/@eslint/eslintrc/node_modules/globals": {
863
+
"version": "14.0.0",
864
+
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
865
+
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
866
+
"dev": true,
867
+
"license": "MIT",
868
+
"engines": {
869
+
"node": ">=18"
870
+
},
871
+
"funding": {
872
+
"url": "https://github.com/sponsors/sindresorhus"
873
+
}
874
+
},
875
+
"node_modules/@eslint/js": {
876
+
"version": "9.39.2",
877
+
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
878
+
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
879
+
"dev": true,
880
+
"license": "MIT",
881
+
"engines": {
882
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
883
+
},
884
+
"funding": {
885
+
"url": "https://eslint.org/donate"
886
+
}
887
+
},
888
+
"node_modules/@eslint/object-schema": {
889
+
"version": "2.1.7",
890
+
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
891
+
"integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
892
+
"dev": true,
893
+
"license": "Apache-2.0",
894
+
"engines": {
895
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
896
+
}
897
+
},
898
+
"node_modules/@eslint/plugin-kit": {
899
+
"version": "0.4.1",
900
+
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
901
+
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
902
+
"dev": true,
903
+
"license": "Apache-2.0",
904
+
"dependencies": {
905
+
"@eslint/core": "^0.17.0",
906
+
"levn": "^0.4.1"
907
+
},
908
+
"engines": {
909
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
910
+
}
911
+
},
912
+
"node_modules/@humanfs/core": {
913
+
"version": "0.19.1",
914
+
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
915
+
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
916
+
"dev": true,
917
+
"license": "Apache-2.0",
918
+
"engines": {
919
+
"node": ">=18.18.0"
920
+
}
921
+
},
922
+
"node_modules/@humanfs/node": {
923
+
"version": "0.16.7",
924
+
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
925
+
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
926
+
"dev": true,
927
+
"license": "Apache-2.0",
928
+
"dependencies": {
929
+
"@humanfs/core": "^0.19.1",
930
+
"@humanwhocodes/retry": "^0.4.0"
931
+
},
932
+
"engines": {
933
+
"node": ">=18.18.0"
934
+
}
935
+
},
936
+
"node_modules/@humanwhocodes/module-importer": {
937
+
"version": "1.0.1",
938
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
939
+
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
940
+
"dev": true,
941
+
"license": "Apache-2.0",
942
+
"engines": {
943
+
"node": ">=12.22"
944
+
},
945
+
"funding": {
946
+
"type": "github",
947
+
"url": "https://github.com/sponsors/nzakas"
948
+
}
949
+
},
950
+
"node_modules/@humanwhocodes/retry": {
951
+
"version": "0.4.3",
952
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
953
+
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
954
+
"dev": true,
955
+
"license": "Apache-2.0",
956
+
"engines": {
957
+
"node": ">=18.18"
958
+
},
959
+
"funding": {
960
+
"type": "github",
961
+
"url": "https://github.com/sponsors/nzakas"
962
+
}
963
+
},
749
964
"node_modules/@jridgewell/gen-mapping": {
750
965
"version": "0.3.13",
751
966
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
···
797
1012
}
798
1013
},
799
1014
"node_modules/@remix-run/router": {
800
-
"version": "1.23.1",
801
-
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.1.tgz",
802
-
"integrity": "sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==",
1015
+
"version": "1.23.2",
1016
+
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
1017
+
"integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
803
1018
"license": "MIT",
804
1019
"engines": {
805
1020
"node": ">=14.0.0"
···
1172
1387
"dev": true,
1173
1388
"license": "MIT"
1174
1389
},
1390
+
"node_modules/@types/json-schema": {
1391
+
"version": "7.0.15",
1392
+
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
1393
+
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
1394
+
"dev": true,
1395
+
"license": "MIT"
1396
+
},
1175
1397
"node_modules/@types/prop-types": {
1176
1398
"version": "15.7.15",
1177
1399
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
···
1222
1444
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1223
1445
}
1224
1446
},
1447
+
"node_modules/acorn": {
1448
+
"version": "8.15.0",
1449
+
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
1450
+
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
1451
+
"dev": true,
1452
+
"license": "MIT",
1453
+
"peer": true,
1454
+
"bin": {
1455
+
"acorn": "bin/acorn"
1456
+
},
1457
+
"engines": {
1458
+
"node": ">=0.4.0"
1459
+
}
1460
+
},
1461
+
"node_modules/acorn-jsx": {
1462
+
"version": "5.3.2",
1463
+
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
1464
+
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
1465
+
"dev": true,
1466
+
"license": "MIT",
1467
+
"peerDependencies": {
1468
+
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
1469
+
}
1470
+
},
1471
+
"node_modules/ajv": {
1472
+
"version": "6.12.6",
1473
+
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
1474
+
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
1475
+
"dev": true,
1476
+
"license": "MIT",
1477
+
"dependencies": {
1478
+
"fast-deep-equal": "^3.1.1",
1479
+
"fast-json-stable-stringify": "^2.0.0",
1480
+
"json-schema-traverse": "^0.4.1",
1481
+
"uri-js": "^4.2.2"
1482
+
},
1483
+
"funding": {
1484
+
"type": "github",
1485
+
"url": "https://github.com/sponsors/epoberezkin"
1486
+
}
1487
+
},
1488
+
"node_modules/ansi-styles": {
1489
+
"version": "4.3.0",
1490
+
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
1491
+
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
1492
+
"dev": true,
1493
+
"license": "MIT",
1494
+
"dependencies": {
1495
+
"color-convert": "^2.0.1"
1496
+
},
1497
+
"engines": {
1498
+
"node": ">=8"
1499
+
},
1500
+
"funding": {
1501
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
1502
+
}
1503
+
},
1504
+
"node_modules/argparse": {
1505
+
"version": "2.0.1",
1506
+
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
1507
+
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
1508
+
"dev": true,
1509
+
"license": "Python-2.0"
1510
+
},
1511
+
"node_modules/array-buffer-byte-length": {
1512
+
"version": "1.0.2",
1513
+
"resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
1514
+
"integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
1515
+
"dev": true,
1516
+
"license": "MIT",
1517
+
"dependencies": {
1518
+
"call-bound": "^1.0.3",
1519
+
"is-array-buffer": "^3.0.5"
1520
+
},
1521
+
"engines": {
1522
+
"node": ">= 0.4"
1523
+
},
1524
+
"funding": {
1525
+
"url": "https://github.com/sponsors/ljharb"
1526
+
}
1527
+
},
1528
+
"node_modules/array-includes": {
1529
+
"version": "3.1.9",
1530
+
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
1531
+
"integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
1532
+
"dev": true,
1533
+
"license": "MIT",
1534
+
"dependencies": {
1535
+
"call-bind": "^1.0.8",
1536
+
"call-bound": "^1.0.4",
1537
+
"define-properties": "^1.2.1",
1538
+
"es-abstract": "^1.24.0",
1539
+
"es-object-atoms": "^1.1.1",
1540
+
"get-intrinsic": "^1.3.0",
1541
+
"is-string": "^1.1.1",
1542
+
"math-intrinsics": "^1.1.0"
1543
+
},
1544
+
"engines": {
1545
+
"node": ">= 0.4"
1546
+
},
1547
+
"funding": {
1548
+
"url": "https://github.com/sponsors/ljharb"
1549
+
}
1550
+
},
1551
+
"node_modules/array.prototype.findlast": {
1552
+
"version": "1.2.5",
1553
+
"resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
1554
+
"integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
1555
+
"dev": true,
1556
+
"license": "MIT",
1557
+
"dependencies": {
1558
+
"call-bind": "^1.0.7",
1559
+
"define-properties": "^1.2.1",
1560
+
"es-abstract": "^1.23.2",
1561
+
"es-errors": "^1.3.0",
1562
+
"es-object-atoms": "^1.0.0",
1563
+
"es-shim-unscopables": "^1.0.2"
1564
+
},
1565
+
"engines": {
1566
+
"node": ">= 0.4"
1567
+
},
1568
+
"funding": {
1569
+
"url": "https://github.com/sponsors/ljharb"
1570
+
}
1571
+
},
1572
+
"node_modules/array.prototype.flat": {
1573
+
"version": "1.3.3",
1574
+
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
1575
+
"integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
1576
+
"dev": true,
1577
+
"license": "MIT",
1578
+
"dependencies": {
1579
+
"call-bind": "^1.0.8",
1580
+
"define-properties": "^1.2.1",
1581
+
"es-abstract": "^1.23.5",
1582
+
"es-shim-unscopables": "^1.0.2"
1583
+
},
1584
+
"engines": {
1585
+
"node": ">= 0.4"
1586
+
},
1587
+
"funding": {
1588
+
"url": "https://github.com/sponsors/ljharb"
1589
+
}
1590
+
},
1591
+
"node_modules/array.prototype.flatmap": {
1592
+
"version": "1.3.3",
1593
+
"resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
1594
+
"integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
1595
+
"dev": true,
1596
+
"license": "MIT",
1597
+
"dependencies": {
1598
+
"call-bind": "^1.0.8",
1599
+
"define-properties": "^1.2.1",
1600
+
"es-abstract": "^1.23.5",
1601
+
"es-shim-unscopables": "^1.0.2"
1602
+
},
1603
+
"engines": {
1604
+
"node": ">= 0.4"
1605
+
},
1606
+
"funding": {
1607
+
"url": "https://github.com/sponsors/ljharb"
1608
+
}
1609
+
},
1610
+
"node_modules/array.prototype.tosorted": {
1611
+
"version": "1.1.4",
1612
+
"resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
1613
+
"integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
1614
+
"dev": true,
1615
+
"license": "MIT",
1616
+
"dependencies": {
1617
+
"call-bind": "^1.0.7",
1618
+
"define-properties": "^1.2.1",
1619
+
"es-abstract": "^1.23.3",
1620
+
"es-errors": "^1.3.0",
1621
+
"es-shim-unscopables": "^1.0.2"
1622
+
},
1623
+
"engines": {
1624
+
"node": ">= 0.4"
1625
+
}
1626
+
},
1627
+
"node_modules/arraybuffer.prototype.slice": {
1628
+
"version": "1.0.4",
1629
+
"resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
1630
+
"integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
1631
+
"dev": true,
1632
+
"license": "MIT",
1633
+
"dependencies": {
1634
+
"array-buffer-byte-length": "^1.0.1",
1635
+
"call-bind": "^1.0.8",
1636
+
"define-properties": "^1.2.1",
1637
+
"es-abstract": "^1.23.5",
1638
+
"es-errors": "^1.3.0",
1639
+
"get-intrinsic": "^1.2.6",
1640
+
"is-array-buffer": "^3.0.4"
1641
+
},
1642
+
"engines": {
1643
+
"node": ">= 0.4"
1644
+
},
1645
+
"funding": {
1646
+
"url": "https://github.com/sponsors/ljharb"
1647
+
}
1648
+
},
1649
+
"node_modules/async-function": {
1650
+
"version": "1.0.0",
1651
+
"resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
1652
+
"integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
1653
+
"dev": true,
1654
+
"license": "MIT",
1655
+
"engines": {
1656
+
"node": ">= 0.4"
1657
+
}
1658
+
},
1659
+
"node_modules/available-typed-arrays": {
1660
+
"version": "1.0.7",
1661
+
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
1662
+
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
1663
+
"dev": true,
1664
+
"license": "MIT",
1665
+
"dependencies": {
1666
+
"possible-typed-array-names": "^1.0.0"
1667
+
},
1668
+
"engines": {
1669
+
"node": ">= 0.4"
1670
+
},
1671
+
"funding": {
1672
+
"url": "https://github.com/sponsors/ljharb"
1673
+
}
1674
+
},
1675
+
"node_modules/balanced-match": {
1676
+
"version": "1.0.2",
1677
+
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
1678
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
1679
+
"dev": true,
1680
+
"license": "MIT"
1681
+
},
1225
1682
"node_modules/baseline-browser-mapping": {
1226
1683
"version": "2.9.11",
1227
1684
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
···
1230
1687
"license": "Apache-2.0",
1231
1688
"bin": {
1232
1689
"baseline-browser-mapping": "dist/cli.js"
1690
+
}
1691
+
},
1692
+
"node_modules/brace-expansion": {
1693
+
"version": "1.1.12",
1694
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
1695
+
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
1696
+
"dev": true,
1697
+
"license": "MIT",
1698
+
"dependencies": {
1699
+
"balanced-match": "^1.0.0",
1700
+
"concat-map": "0.0.1"
1233
1701
}
1234
1702
},
1235
1703
"node_modules/browserslist": {
···
1267
1735
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1268
1736
}
1269
1737
},
1738
+
"node_modules/call-bind": {
1739
+
"version": "1.0.8",
1740
+
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
1741
+
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
1742
+
"dev": true,
1743
+
"license": "MIT",
1744
+
"dependencies": {
1745
+
"call-bind-apply-helpers": "^1.0.0",
1746
+
"es-define-property": "^1.0.0",
1747
+
"get-intrinsic": "^1.2.4",
1748
+
"set-function-length": "^1.2.2"
1749
+
},
1750
+
"engines": {
1751
+
"node": ">= 0.4"
1752
+
},
1753
+
"funding": {
1754
+
"url": "https://github.com/sponsors/ljharb"
1755
+
}
1756
+
},
1757
+
"node_modules/call-bind-apply-helpers": {
1758
+
"version": "1.0.2",
1759
+
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
1760
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
1761
+
"dev": true,
1762
+
"license": "MIT",
1763
+
"dependencies": {
1764
+
"es-errors": "^1.3.0",
1765
+
"function-bind": "^1.1.2"
1766
+
},
1767
+
"engines": {
1768
+
"node": ">= 0.4"
1769
+
}
1770
+
},
1771
+
"node_modules/call-bound": {
1772
+
"version": "1.0.4",
1773
+
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
1774
+
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
1775
+
"dev": true,
1776
+
"license": "MIT",
1777
+
"dependencies": {
1778
+
"call-bind-apply-helpers": "^1.0.2",
1779
+
"get-intrinsic": "^1.3.0"
1780
+
},
1781
+
"engines": {
1782
+
"node": ">= 0.4"
1783
+
},
1784
+
"funding": {
1785
+
"url": "https://github.com/sponsors/ljharb"
1786
+
}
1787
+
},
1788
+
"node_modules/callsites": {
1789
+
"version": "3.1.0",
1790
+
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
1791
+
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
1792
+
"dev": true,
1793
+
"license": "MIT",
1794
+
"engines": {
1795
+
"node": ">=6"
1796
+
}
1797
+
},
1270
1798
"node_modules/caniuse-lite": {
1271
1799
"version": "1.0.30001762",
1272
1800
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz",
···
1288
1816
],
1289
1817
"license": "CC-BY-4.0"
1290
1818
},
1819
+
"node_modules/chalk": {
1820
+
"version": "4.1.2",
1821
+
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
1822
+
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
1823
+
"dev": true,
1824
+
"license": "MIT",
1825
+
"dependencies": {
1826
+
"ansi-styles": "^4.1.0",
1827
+
"supports-color": "^7.1.0"
1828
+
},
1829
+
"engines": {
1830
+
"node": ">=10"
1831
+
},
1832
+
"funding": {
1833
+
"url": "https://github.com/chalk/chalk?sponsor=1"
1834
+
}
1835
+
},
1836
+
"node_modules/color-convert": {
1837
+
"version": "2.0.1",
1838
+
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
1839
+
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
1840
+
"dev": true,
1841
+
"license": "MIT",
1842
+
"dependencies": {
1843
+
"color-name": "~1.1.4"
1844
+
},
1845
+
"engines": {
1846
+
"node": ">=7.0.0"
1847
+
}
1848
+
},
1849
+
"node_modules/color-name": {
1850
+
"version": "1.1.4",
1851
+
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
1852
+
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
1853
+
"dev": true,
1854
+
"license": "MIT"
1855
+
},
1856
+
"node_modules/concat-map": {
1857
+
"version": "0.0.1",
1858
+
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
1859
+
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
1860
+
"dev": true,
1861
+
"license": "MIT"
1862
+
},
1291
1863
"node_modules/convert-source-map": {
1292
1864
"version": "2.0.0",
1293
1865
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
···
1295
1867
"dev": true,
1296
1868
"license": "MIT"
1297
1869
},
1870
+
"node_modules/cross-spawn": {
1871
+
"version": "7.0.6",
1872
+
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
1873
+
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
1874
+
"dev": true,
1875
+
"license": "MIT",
1876
+
"dependencies": {
1877
+
"path-key": "^3.1.0",
1878
+
"shebang-command": "^2.0.0",
1879
+
"which": "^2.0.1"
1880
+
},
1881
+
"engines": {
1882
+
"node": ">= 8"
1883
+
}
1884
+
},
1298
1885
"node_modules/csstype": {
1299
1886
"version": "3.2.3",
1300
1887
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
···
1302
1889
"dev": true,
1303
1890
"license": "MIT"
1304
1891
},
1892
+
"node_modules/data-view-buffer": {
1893
+
"version": "1.0.2",
1894
+
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
1895
+
"integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
1896
+
"dev": true,
1897
+
"license": "MIT",
1898
+
"dependencies": {
1899
+
"call-bound": "^1.0.3",
1900
+
"es-errors": "^1.3.0",
1901
+
"is-data-view": "^1.0.2"
1902
+
},
1903
+
"engines": {
1904
+
"node": ">= 0.4"
1905
+
},
1906
+
"funding": {
1907
+
"url": "https://github.com/sponsors/ljharb"
1908
+
}
1909
+
},
1910
+
"node_modules/data-view-byte-length": {
1911
+
"version": "1.0.2",
1912
+
"resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
1913
+
"integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
1914
+
"dev": true,
1915
+
"license": "MIT",
1916
+
"dependencies": {
1917
+
"call-bound": "^1.0.3",
1918
+
"es-errors": "^1.3.0",
1919
+
"is-data-view": "^1.0.2"
1920
+
},
1921
+
"engines": {
1922
+
"node": ">= 0.4"
1923
+
},
1924
+
"funding": {
1925
+
"url": "https://github.com/sponsors/inspect-js"
1926
+
}
1927
+
},
1928
+
"node_modules/data-view-byte-offset": {
1929
+
"version": "1.0.1",
1930
+
"resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
1931
+
"integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
1932
+
"dev": true,
1933
+
"license": "MIT",
1934
+
"dependencies": {
1935
+
"call-bound": "^1.0.2",
1936
+
"es-errors": "^1.3.0",
1937
+
"is-data-view": "^1.0.1"
1938
+
},
1939
+
"engines": {
1940
+
"node": ">= 0.4"
1941
+
},
1942
+
"funding": {
1943
+
"url": "https://github.com/sponsors/ljharb"
1944
+
}
1945
+
},
1305
1946
"node_modules/debug": {
1306
1947
"version": "4.4.3",
1307
1948
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
···
1320
1961
}
1321
1962
}
1322
1963
},
1964
+
"node_modules/deep-is": {
1965
+
"version": "0.1.4",
1966
+
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
1967
+
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
1968
+
"dev": true,
1969
+
"license": "MIT"
1970
+
},
1971
+
"node_modules/define-data-property": {
1972
+
"version": "1.1.4",
1973
+
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
1974
+
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
1975
+
"dev": true,
1976
+
"license": "MIT",
1977
+
"dependencies": {
1978
+
"es-define-property": "^1.0.0",
1979
+
"es-errors": "^1.3.0",
1980
+
"gopd": "^1.0.1"
1981
+
},
1982
+
"engines": {
1983
+
"node": ">= 0.4"
1984
+
},
1985
+
"funding": {
1986
+
"url": "https://github.com/sponsors/ljharb"
1987
+
}
1988
+
},
1989
+
"node_modules/define-properties": {
1990
+
"version": "1.2.1",
1991
+
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
1992
+
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
1993
+
"dev": true,
1994
+
"license": "MIT",
1995
+
"dependencies": {
1996
+
"define-data-property": "^1.0.1",
1997
+
"has-property-descriptors": "^1.0.0",
1998
+
"object-keys": "^1.1.1"
1999
+
},
2000
+
"engines": {
2001
+
"node": ">= 0.4"
2002
+
},
2003
+
"funding": {
2004
+
"url": "https://github.com/sponsors/ljharb"
2005
+
}
2006
+
},
2007
+
"node_modules/doctrine": {
2008
+
"version": "2.1.0",
2009
+
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
2010
+
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
2011
+
"dev": true,
2012
+
"license": "Apache-2.0",
2013
+
"dependencies": {
2014
+
"esutils": "^2.0.2"
2015
+
},
2016
+
"engines": {
2017
+
"node": ">=0.10.0"
2018
+
}
2019
+
},
2020
+
"node_modules/dunder-proto": {
2021
+
"version": "1.0.1",
2022
+
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
2023
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
2024
+
"dev": true,
2025
+
"license": "MIT",
2026
+
"dependencies": {
2027
+
"call-bind-apply-helpers": "^1.0.1",
2028
+
"es-errors": "^1.3.0",
2029
+
"gopd": "^1.2.0"
2030
+
},
2031
+
"engines": {
2032
+
"node": ">= 0.4"
2033
+
}
2034
+
},
1323
2035
"node_modules/electron-to-chromium": {
1324
2036
"version": "1.5.267",
1325
2037
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
···
1327
2039
"dev": true,
1328
2040
"license": "ISC"
1329
2041
},
2042
+
"node_modules/es-abstract": {
2043
+
"version": "1.24.1",
2044
+
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
2045
+
"integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
2046
+
"dev": true,
2047
+
"license": "MIT",
2048
+
"dependencies": {
2049
+
"array-buffer-byte-length": "^1.0.2",
2050
+
"arraybuffer.prototype.slice": "^1.0.4",
2051
+
"available-typed-arrays": "^1.0.7",
2052
+
"call-bind": "^1.0.8",
2053
+
"call-bound": "^1.0.4",
2054
+
"data-view-buffer": "^1.0.2",
2055
+
"data-view-byte-length": "^1.0.2",
2056
+
"data-view-byte-offset": "^1.0.1",
2057
+
"es-define-property": "^1.0.1",
2058
+
"es-errors": "^1.3.0",
2059
+
"es-object-atoms": "^1.1.1",
2060
+
"es-set-tostringtag": "^2.1.0",
2061
+
"es-to-primitive": "^1.3.0",
2062
+
"function.prototype.name": "^1.1.8",
2063
+
"get-intrinsic": "^1.3.0",
2064
+
"get-proto": "^1.0.1",
2065
+
"get-symbol-description": "^1.1.0",
2066
+
"globalthis": "^1.0.4",
2067
+
"gopd": "^1.2.0",
2068
+
"has-property-descriptors": "^1.0.2",
2069
+
"has-proto": "^1.2.0",
2070
+
"has-symbols": "^1.1.0",
2071
+
"hasown": "^2.0.2",
2072
+
"internal-slot": "^1.1.0",
2073
+
"is-array-buffer": "^3.0.5",
2074
+
"is-callable": "^1.2.7",
2075
+
"is-data-view": "^1.0.2",
2076
+
"is-negative-zero": "^2.0.3",
2077
+
"is-regex": "^1.2.1",
2078
+
"is-set": "^2.0.3",
2079
+
"is-shared-array-buffer": "^1.0.4",
2080
+
"is-string": "^1.1.1",
2081
+
"is-typed-array": "^1.1.15",
2082
+
"is-weakref": "^1.1.1",
2083
+
"math-intrinsics": "^1.1.0",
2084
+
"object-inspect": "^1.13.4",
2085
+
"object-keys": "^1.1.1",
2086
+
"object.assign": "^4.1.7",
2087
+
"own-keys": "^1.0.1",
2088
+
"regexp.prototype.flags": "^1.5.4",
2089
+
"safe-array-concat": "^1.1.3",
2090
+
"safe-push-apply": "^1.0.0",
2091
+
"safe-regex-test": "^1.1.0",
2092
+
"set-proto": "^1.0.0",
2093
+
"stop-iteration-iterator": "^1.1.0",
2094
+
"string.prototype.trim": "^1.2.10",
2095
+
"string.prototype.trimend": "^1.0.9",
2096
+
"string.prototype.trimstart": "^1.0.8",
2097
+
"typed-array-buffer": "^1.0.3",
2098
+
"typed-array-byte-length": "^1.0.3",
2099
+
"typed-array-byte-offset": "^1.0.4",
2100
+
"typed-array-length": "^1.0.7",
2101
+
"unbox-primitive": "^1.1.0",
2102
+
"which-typed-array": "^1.1.19"
2103
+
},
2104
+
"engines": {
2105
+
"node": ">= 0.4"
2106
+
},
2107
+
"funding": {
2108
+
"url": "https://github.com/sponsors/ljharb"
2109
+
}
2110
+
},
2111
+
"node_modules/es-define-property": {
2112
+
"version": "1.0.1",
2113
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
2114
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
2115
+
"dev": true,
2116
+
"license": "MIT",
2117
+
"engines": {
2118
+
"node": ">= 0.4"
2119
+
}
2120
+
},
2121
+
"node_modules/es-errors": {
2122
+
"version": "1.3.0",
2123
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
2124
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
2125
+
"dev": true,
2126
+
"license": "MIT",
2127
+
"engines": {
2128
+
"node": ">= 0.4"
2129
+
}
2130
+
},
2131
+
"node_modules/es-iterator-helpers": {
2132
+
"version": "1.2.2",
2133
+
"resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz",
2134
+
"integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==",
2135
+
"dev": true,
2136
+
"license": "MIT",
2137
+
"dependencies": {
2138
+
"call-bind": "^1.0.8",
2139
+
"call-bound": "^1.0.4",
2140
+
"define-properties": "^1.2.1",
2141
+
"es-abstract": "^1.24.1",
2142
+
"es-errors": "^1.3.0",
2143
+
"es-set-tostringtag": "^2.1.0",
2144
+
"function-bind": "^1.1.2",
2145
+
"get-intrinsic": "^1.3.0",
2146
+
"globalthis": "^1.0.4",
2147
+
"gopd": "^1.2.0",
2148
+
"has-property-descriptors": "^1.0.2",
2149
+
"has-proto": "^1.2.0",
2150
+
"has-symbols": "^1.1.0",
2151
+
"internal-slot": "^1.1.0",
2152
+
"iterator.prototype": "^1.1.5",
2153
+
"safe-array-concat": "^1.1.3"
2154
+
},
2155
+
"engines": {
2156
+
"node": ">= 0.4"
2157
+
}
2158
+
},
2159
+
"node_modules/es-object-atoms": {
2160
+
"version": "1.1.1",
2161
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
2162
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
2163
+
"dev": true,
2164
+
"license": "MIT",
2165
+
"dependencies": {
2166
+
"es-errors": "^1.3.0"
2167
+
},
2168
+
"engines": {
2169
+
"node": ">= 0.4"
2170
+
}
2171
+
},
2172
+
"node_modules/es-set-tostringtag": {
2173
+
"version": "2.1.0",
2174
+
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
2175
+
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
2176
+
"dev": true,
2177
+
"license": "MIT",
2178
+
"dependencies": {
2179
+
"es-errors": "^1.3.0",
2180
+
"get-intrinsic": "^1.2.6",
2181
+
"has-tostringtag": "^1.0.2",
2182
+
"hasown": "^2.0.2"
2183
+
},
2184
+
"engines": {
2185
+
"node": ">= 0.4"
2186
+
}
2187
+
},
2188
+
"node_modules/es-shim-unscopables": {
2189
+
"version": "1.1.0",
2190
+
"resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
2191
+
"integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
2192
+
"dev": true,
2193
+
"license": "MIT",
2194
+
"dependencies": {
2195
+
"hasown": "^2.0.2"
2196
+
},
2197
+
"engines": {
2198
+
"node": ">= 0.4"
2199
+
}
2200
+
},
2201
+
"node_modules/es-to-primitive": {
2202
+
"version": "1.3.0",
2203
+
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
2204
+
"integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
2205
+
"dev": true,
2206
+
"license": "MIT",
2207
+
"dependencies": {
2208
+
"is-callable": "^1.2.7",
2209
+
"is-date-object": "^1.0.5",
2210
+
"is-symbol": "^1.0.4"
2211
+
},
2212
+
"engines": {
2213
+
"node": ">= 0.4"
2214
+
},
2215
+
"funding": {
2216
+
"url": "https://github.com/sponsors/ljharb"
2217
+
}
2218
+
},
1330
2219
"node_modules/esbuild": {
1331
2220
"version": "0.25.12",
1332
2221
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
···
1379
2268
"node": ">=6"
1380
2269
}
1381
2270
},
2271
+
"node_modules/escape-string-regexp": {
2272
+
"version": "4.0.0",
2273
+
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
2274
+
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
2275
+
"dev": true,
2276
+
"license": "MIT",
2277
+
"engines": {
2278
+
"node": ">=10"
2279
+
},
2280
+
"funding": {
2281
+
"url": "https://github.com/sponsors/sindresorhus"
2282
+
}
2283
+
},
2284
+
"node_modules/eslint": {
2285
+
"version": "9.39.2",
2286
+
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
2287
+
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
2288
+
"dev": true,
2289
+
"license": "MIT",
2290
+
"peer": true,
2291
+
"dependencies": {
2292
+
"@eslint-community/eslint-utils": "^4.8.0",
2293
+
"@eslint-community/regexpp": "^4.12.1",
2294
+
"@eslint/config-array": "^0.21.1",
2295
+
"@eslint/config-helpers": "^0.4.2",
2296
+
"@eslint/core": "^0.17.0",
2297
+
"@eslint/eslintrc": "^3.3.1",
2298
+
"@eslint/js": "9.39.2",
2299
+
"@eslint/plugin-kit": "^0.4.1",
2300
+
"@humanfs/node": "^0.16.6",
2301
+
"@humanwhocodes/module-importer": "^1.0.1",
2302
+
"@humanwhocodes/retry": "^0.4.2",
2303
+
"@types/estree": "^1.0.6",
2304
+
"ajv": "^6.12.4",
2305
+
"chalk": "^4.0.0",
2306
+
"cross-spawn": "^7.0.6",
2307
+
"debug": "^4.3.2",
2308
+
"escape-string-regexp": "^4.0.0",
2309
+
"eslint-scope": "^8.4.0",
2310
+
"eslint-visitor-keys": "^4.2.1",
2311
+
"espree": "^10.4.0",
2312
+
"esquery": "^1.5.0",
2313
+
"esutils": "^2.0.2",
2314
+
"fast-deep-equal": "^3.1.3",
2315
+
"file-entry-cache": "^8.0.0",
2316
+
"find-up": "^5.0.0",
2317
+
"glob-parent": "^6.0.2",
2318
+
"ignore": "^5.2.0",
2319
+
"imurmurhash": "^0.1.4",
2320
+
"is-glob": "^4.0.0",
2321
+
"json-stable-stringify-without-jsonify": "^1.0.1",
2322
+
"lodash.merge": "^4.6.2",
2323
+
"minimatch": "^3.1.2",
2324
+
"natural-compare": "^1.4.0",
2325
+
"optionator": "^0.9.3"
2326
+
},
2327
+
"bin": {
2328
+
"eslint": "bin/eslint.js"
2329
+
},
2330
+
"engines": {
2331
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2332
+
},
2333
+
"funding": {
2334
+
"url": "https://eslint.org/donate"
2335
+
},
2336
+
"peerDependencies": {
2337
+
"jiti": "*"
2338
+
},
2339
+
"peerDependenciesMeta": {
2340
+
"jiti": {
2341
+
"optional": true
2342
+
}
2343
+
}
2344
+
},
2345
+
"node_modules/eslint-plugin-react": {
2346
+
"version": "7.37.5",
2347
+
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
2348
+
"integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
2349
+
"dev": true,
2350
+
"license": "MIT",
2351
+
"dependencies": {
2352
+
"array-includes": "^3.1.8",
2353
+
"array.prototype.findlast": "^1.2.5",
2354
+
"array.prototype.flatmap": "^1.3.3",
2355
+
"array.prototype.tosorted": "^1.1.4",
2356
+
"doctrine": "^2.1.0",
2357
+
"es-iterator-helpers": "^1.2.1",
2358
+
"estraverse": "^5.3.0",
2359
+
"hasown": "^2.0.2",
2360
+
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
2361
+
"minimatch": "^3.1.2",
2362
+
"object.entries": "^1.1.9",
2363
+
"object.fromentries": "^2.0.8",
2364
+
"object.values": "^1.2.1",
2365
+
"prop-types": "^15.8.1",
2366
+
"resolve": "^2.0.0-next.5",
2367
+
"semver": "^6.3.1",
2368
+
"string.prototype.matchall": "^4.0.12",
2369
+
"string.prototype.repeat": "^1.0.0"
2370
+
},
2371
+
"engines": {
2372
+
"node": ">=4"
2373
+
},
2374
+
"peerDependencies": {
2375
+
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
2376
+
}
2377
+
},
2378
+
"node_modules/eslint-plugin-react-hooks": {
2379
+
"version": "7.0.1",
2380
+
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
2381
+
"integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
2382
+
"dev": true,
2383
+
"license": "MIT",
2384
+
"dependencies": {
2385
+
"@babel/core": "^7.24.4",
2386
+
"@babel/parser": "^7.24.4",
2387
+
"hermes-parser": "^0.25.1",
2388
+
"zod": "^3.25.0 || ^4.0.0",
2389
+
"zod-validation-error": "^3.5.0 || ^4.0.0"
2390
+
},
2391
+
"engines": {
2392
+
"node": ">=18"
2393
+
},
2394
+
"peerDependencies": {
2395
+
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
2396
+
}
2397
+
},
2398
+
"node_modules/eslint-plugin-react-refresh": {
2399
+
"version": "0.4.26",
2400
+
"resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz",
2401
+
"integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==",
2402
+
"dev": true,
2403
+
"license": "MIT",
2404
+
"peerDependencies": {
2405
+
"eslint": ">=8.40"
2406
+
}
2407
+
},
2408
+
"node_modules/eslint-scope": {
2409
+
"version": "8.4.0",
2410
+
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
2411
+
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
2412
+
"dev": true,
2413
+
"license": "BSD-2-Clause",
2414
+
"dependencies": {
2415
+
"esrecurse": "^4.3.0",
2416
+
"estraverse": "^5.2.0"
2417
+
},
2418
+
"engines": {
2419
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2420
+
},
2421
+
"funding": {
2422
+
"url": "https://opencollective.com/eslint"
2423
+
}
2424
+
},
2425
+
"node_modules/eslint-visitor-keys": {
2426
+
"version": "4.2.1",
2427
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
2428
+
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
2429
+
"dev": true,
2430
+
"license": "Apache-2.0",
2431
+
"engines": {
2432
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2433
+
},
2434
+
"funding": {
2435
+
"url": "https://opencollective.com/eslint"
2436
+
}
2437
+
},
2438
+
"node_modules/espree": {
2439
+
"version": "10.4.0",
2440
+
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
2441
+
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
2442
+
"dev": true,
2443
+
"license": "BSD-2-Clause",
2444
+
"dependencies": {
2445
+
"acorn": "^8.15.0",
2446
+
"acorn-jsx": "^5.3.2",
2447
+
"eslint-visitor-keys": "^4.2.1"
2448
+
},
2449
+
"engines": {
2450
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2451
+
},
2452
+
"funding": {
2453
+
"url": "https://opencollective.com/eslint"
2454
+
}
2455
+
},
2456
+
"node_modules/esquery": {
2457
+
"version": "1.7.0",
2458
+
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
2459
+
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
2460
+
"dev": true,
2461
+
"license": "BSD-3-Clause",
2462
+
"dependencies": {
2463
+
"estraverse": "^5.1.0"
2464
+
},
2465
+
"engines": {
2466
+
"node": ">=0.10"
2467
+
}
2468
+
},
2469
+
"node_modules/esrecurse": {
2470
+
"version": "4.3.0",
2471
+
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
2472
+
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
2473
+
"dev": true,
2474
+
"license": "BSD-2-Clause",
2475
+
"dependencies": {
2476
+
"estraverse": "^5.2.0"
2477
+
},
2478
+
"engines": {
2479
+
"node": ">=4.0"
2480
+
}
2481
+
},
2482
+
"node_modules/estraverse": {
2483
+
"version": "5.3.0",
2484
+
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
2485
+
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
2486
+
"dev": true,
2487
+
"license": "BSD-2-Clause",
2488
+
"engines": {
2489
+
"node": ">=4.0"
2490
+
}
2491
+
},
2492
+
"node_modules/esutils": {
2493
+
"version": "2.0.3",
2494
+
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
2495
+
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
2496
+
"dev": true,
2497
+
"license": "BSD-2-Clause",
2498
+
"engines": {
2499
+
"node": ">=0.10.0"
2500
+
}
2501
+
},
2502
+
"node_modules/fast-deep-equal": {
2503
+
"version": "3.1.3",
2504
+
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
2505
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
2506
+
"dev": true,
2507
+
"license": "MIT"
2508
+
},
2509
+
"node_modules/fast-json-stable-stringify": {
2510
+
"version": "2.1.0",
2511
+
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
2512
+
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
2513
+
"dev": true,
2514
+
"license": "MIT"
2515
+
},
2516
+
"node_modules/fast-levenshtein": {
2517
+
"version": "2.0.6",
2518
+
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
2519
+
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
2520
+
"dev": true,
2521
+
"license": "MIT"
2522
+
},
1382
2523
"node_modules/fdir": {
1383
2524
"version": "6.5.0",
1384
2525
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
···
1397
2538
}
1398
2539
}
1399
2540
},
2541
+
"node_modules/file-entry-cache": {
2542
+
"version": "8.0.0",
2543
+
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
2544
+
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
2545
+
"dev": true,
2546
+
"license": "MIT",
2547
+
"dependencies": {
2548
+
"flat-cache": "^4.0.0"
2549
+
},
2550
+
"engines": {
2551
+
"node": ">=16.0.0"
2552
+
}
2553
+
},
2554
+
"node_modules/find-up": {
2555
+
"version": "5.0.0",
2556
+
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
2557
+
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
2558
+
"dev": true,
2559
+
"license": "MIT",
2560
+
"dependencies": {
2561
+
"locate-path": "^6.0.0",
2562
+
"path-exists": "^4.0.0"
2563
+
},
2564
+
"engines": {
2565
+
"node": ">=10"
2566
+
},
2567
+
"funding": {
2568
+
"url": "https://github.com/sponsors/sindresorhus"
2569
+
}
2570
+
},
2571
+
"node_modules/flat-cache": {
2572
+
"version": "4.0.1",
2573
+
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
2574
+
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
2575
+
"dev": true,
2576
+
"license": "MIT",
2577
+
"dependencies": {
2578
+
"flatted": "^3.2.9",
2579
+
"keyv": "^4.5.4"
2580
+
},
2581
+
"engines": {
2582
+
"node": ">=16"
2583
+
}
2584
+
},
2585
+
"node_modules/flatted": {
2586
+
"version": "3.3.3",
2587
+
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
2588
+
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
2589
+
"dev": true,
2590
+
"license": "ISC"
2591
+
},
2592
+
"node_modules/for-each": {
2593
+
"version": "0.3.5",
2594
+
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
2595
+
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
2596
+
"dev": true,
2597
+
"license": "MIT",
2598
+
"dependencies": {
2599
+
"is-callable": "^1.2.7"
2600
+
},
2601
+
"engines": {
2602
+
"node": ">= 0.4"
2603
+
},
2604
+
"funding": {
2605
+
"url": "https://github.com/sponsors/ljharb"
2606
+
}
2607
+
},
1400
2608
"node_modules/fsevents": {
1401
2609
"version": "2.3.3",
1402
2610
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
···
1412
2620
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1413
2621
}
1414
2622
},
2623
+
"node_modules/function-bind": {
2624
+
"version": "1.1.2",
2625
+
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
2626
+
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
2627
+
"dev": true,
2628
+
"license": "MIT",
2629
+
"funding": {
2630
+
"url": "https://github.com/sponsors/ljharb"
2631
+
}
2632
+
},
2633
+
"node_modules/function.prototype.name": {
2634
+
"version": "1.1.8",
2635
+
"resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
2636
+
"integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
2637
+
"dev": true,
2638
+
"license": "MIT",
2639
+
"dependencies": {
2640
+
"call-bind": "^1.0.8",
2641
+
"call-bound": "^1.0.3",
2642
+
"define-properties": "^1.2.1",
2643
+
"functions-have-names": "^1.2.3",
2644
+
"hasown": "^2.0.2",
2645
+
"is-callable": "^1.2.7"
2646
+
},
2647
+
"engines": {
2648
+
"node": ">= 0.4"
2649
+
},
2650
+
"funding": {
2651
+
"url": "https://github.com/sponsors/ljharb"
2652
+
}
2653
+
},
2654
+
"node_modules/functions-have-names": {
2655
+
"version": "1.2.3",
2656
+
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
2657
+
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
2658
+
"dev": true,
2659
+
"license": "MIT",
2660
+
"funding": {
2661
+
"url": "https://github.com/sponsors/ljharb"
2662
+
}
2663
+
},
2664
+
"node_modules/generator-function": {
2665
+
"version": "2.0.1",
2666
+
"resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
2667
+
"integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
2668
+
"dev": true,
2669
+
"license": "MIT",
2670
+
"engines": {
2671
+
"node": ">= 0.4"
2672
+
}
2673
+
},
1415
2674
"node_modules/gensync": {
1416
2675
"version": "1.0.0-beta.2",
1417
2676
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
···
1422
2681
"node": ">=6.9.0"
1423
2682
}
1424
2683
},
2684
+
"node_modules/get-intrinsic": {
2685
+
"version": "1.3.0",
2686
+
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
2687
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
2688
+
"dev": true,
2689
+
"license": "MIT",
2690
+
"dependencies": {
2691
+
"call-bind-apply-helpers": "^1.0.2",
2692
+
"es-define-property": "^1.0.1",
2693
+
"es-errors": "^1.3.0",
2694
+
"es-object-atoms": "^1.1.1",
2695
+
"function-bind": "^1.1.2",
2696
+
"get-proto": "^1.0.1",
2697
+
"gopd": "^1.2.0",
2698
+
"has-symbols": "^1.1.0",
2699
+
"hasown": "^2.0.2",
2700
+
"math-intrinsics": "^1.1.0"
2701
+
},
2702
+
"engines": {
2703
+
"node": ">= 0.4"
2704
+
},
2705
+
"funding": {
2706
+
"url": "https://github.com/sponsors/ljharb"
2707
+
}
2708
+
},
2709
+
"node_modules/get-proto": {
2710
+
"version": "1.0.1",
2711
+
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
2712
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
2713
+
"dev": true,
2714
+
"license": "MIT",
2715
+
"dependencies": {
2716
+
"dunder-proto": "^1.0.1",
2717
+
"es-object-atoms": "^1.0.0"
2718
+
},
2719
+
"engines": {
2720
+
"node": ">= 0.4"
2721
+
}
2722
+
},
2723
+
"node_modules/get-symbol-description": {
2724
+
"version": "1.1.0",
2725
+
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
2726
+
"integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
2727
+
"dev": true,
2728
+
"license": "MIT",
2729
+
"dependencies": {
2730
+
"call-bound": "^1.0.3",
2731
+
"es-errors": "^1.3.0",
2732
+
"get-intrinsic": "^1.2.6"
2733
+
},
2734
+
"engines": {
2735
+
"node": ">= 0.4"
2736
+
},
2737
+
"funding": {
2738
+
"url": "https://github.com/sponsors/ljharb"
2739
+
}
2740
+
},
2741
+
"node_modules/glob-parent": {
2742
+
"version": "6.0.2",
2743
+
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
2744
+
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
2745
+
"dev": true,
2746
+
"license": "ISC",
2747
+
"dependencies": {
2748
+
"is-glob": "^4.0.3"
2749
+
},
2750
+
"engines": {
2751
+
"node": ">=10.13.0"
2752
+
}
2753
+
},
2754
+
"node_modules/globals": {
2755
+
"version": "17.0.0",
2756
+
"resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz",
2757
+
"integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==",
2758
+
"dev": true,
2759
+
"license": "MIT",
2760
+
"engines": {
2761
+
"node": ">=18"
2762
+
},
2763
+
"funding": {
2764
+
"url": "https://github.com/sponsors/sindresorhus"
2765
+
}
2766
+
},
2767
+
"node_modules/globalthis": {
2768
+
"version": "1.0.4",
2769
+
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
2770
+
"integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
2771
+
"dev": true,
2772
+
"license": "MIT",
2773
+
"dependencies": {
2774
+
"define-properties": "^1.2.1",
2775
+
"gopd": "^1.0.1"
2776
+
},
2777
+
"engines": {
2778
+
"node": ">= 0.4"
2779
+
},
2780
+
"funding": {
2781
+
"url": "https://github.com/sponsors/ljharb"
2782
+
}
2783
+
},
2784
+
"node_modules/gopd": {
2785
+
"version": "1.2.0",
2786
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
2787
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
2788
+
"dev": true,
2789
+
"license": "MIT",
2790
+
"engines": {
2791
+
"node": ">= 0.4"
2792
+
},
2793
+
"funding": {
2794
+
"url": "https://github.com/sponsors/ljharb"
2795
+
}
2796
+
},
2797
+
"node_modules/has-bigints": {
2798
+
"version": "1.1.0",
2799
+
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
2800
+
"integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
2801
+
"dev": true,
2802
+
"license": "MIT",
2803
+
"engines": {
2804
+
"node": ">= 0.4"
2805
+
},
2806
+
"funding": {
2807
+
"url": "https://github.com/sponsors/ljharb"
2808
+
}
2809
+
},
2810
+
"node_modules/has-flag": {
2811
+
"version": "4.0.0",
2812
+
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
2813
+
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
2814
+
"dev": true,
2815
+
"license": "MIT",
2816
+
"engines": {
2817
+
"node": ">=8"
2818
+
}
2819
+
},
2820
+
"node_modules/has-property-descriptors": {
2821
+
"version": "1.0.2",
2822
+
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
2823
+
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
2824
+
"dev": true,
2825
+
"license": "MIT",
2826
+
"dependencies": {
2827
+
"es-define-property": "^1.0.0"
2828
+
},
2829
+
"funding": {
2830
+
"url": "https://github.com/sponsors/ljharb"
2831
+
}
2832
+
},
2833
+
"node_modules/has-proto": {
2834
+
"version": "1.2.0",
2835
+
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
2836
+
"integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
2837
+
"dev": true,
2838
+
"license": "MIT",
2839
+
"dependencies": {
2840
+
"dunder-proto": "^1.0.0"
2841
+
},
2842
+
"engines": {
2843
+
"node": ">= 0.4"
2844
+
},
2845
+
"funding": {
2846
+
"url": "https://github.com/sponsors/ljharb"
2847
+
}
2848
+
},
2849
+
"node_modules/has-symbols": {
2850
+
"version": "1.1.0",
2851
+
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
2852
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
2853
+
"dev": true,
2854
+
"license": "MIT",
2855
+
"engines": {
2856
+
"node": ">= 0.4"
2857
+
},
2858
+
"funding": {
2859
+
"url": "https://github.com/sponsors/ljharb"
2860
+
}
2861
+
},
2862
+
"node_modules/has-tostringtag": {
2863
+
"version": "1.0.2",
2864
+
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
2865
+
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
2866
+
"dev": true,
2867
+
"license": "MIT",
2868
+
"dependencies": {
2869
+
"has-symbols": "^1.0.3"
2870
+
},
2871
+
"engines": {
2872
+
"node": ">= 0.4"
2873
+
},
2874
+
"funding": {
2875
+
"url": "https://github.com/sponsors/ljharb"
2876
+
}
2877
+
},
2878
+
"node_modules/hasown": {
2879
+
"version": "2.0.2",
2880
+
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
2881
+
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
2882
+
"dev": true,
2883
+
"license": "MIT",
2884
+
"dependencies": {
2885
+
"function-bind": "^1.1.2"
2886
+
},
2887
+
"engines": {
2888
+
"node": ">= 0.4"
2889
+
}
2890
+
},
2891
+
"node_modules/hermes-estree": {
2892
+
"version": "0.25.1",
2893
+
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
2894
+
"integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
2895
+
"dev": true,
2896
+
"license": "MIT"
2897
+
},
2898
+
"node_modules/hermes-parser": {
2899
+
"version": "0.25.1",
2900
+
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
2901
+
"integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
2902
+
"dev": true,
2903
+
"license": "MIT",
2904
+
"dependencies": {
2905
+
"hermes-estree": "0.25.1"
2906
+
}
2907
+
},
2908
+
"node_modules/ignore": {
2909
+
"version": "5.3.2",
2910
+
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
2911
+
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
2912
+
"dev": true,
2913
+
"license": "MIT",
2914
+
"engines": {
2915
+
"node": ">= 4"
2916
+
}
2917
+
},
2918
+
"node_modules/import-fresh": {
2919
+
"version": "3.3.1",
2920
+
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
2921
+
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
2922
+
"dev": true,
2923
+
"license": "MIT",
2924
+
"dependencies": {
2925
+
"parent-module": "^1.0.0",
2926
+
"resolve-from": "^4.0.0"
2927
+
},
2928
+
"engines": {
2929
+
"node": ">=6"
2930
+
},
2931
+
"funding": {
2932
+
"url": "https://github.com/sponsors/sindresorhus"
2933
+
}
2934
+
},
2935
+
"node_modules/imurmurhash": {
2936
+
"version": "0.1.4",
2937
+
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
2938
+
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
2939
+
"dev": true,
2940
+
"license": "MIT",
2941
+
"engines": {
2942
+
"node": ">=0.8.19"
2943
+
}
2944
+
},
2945
+
"node_modules/internal-slot": {
2946
+
"version": "1.1.0",
2947
+
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
2948
+
"integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
2949
+
"dev": true,
2950
+
"license": "MIT",
2951
+
"dependencies": {
2952
+
"es-errors": "^1.3.0",
2953
+
"hasown": "^2.0.2",
2954
+
"side-channel": "^1.1.0"
2955
+
},
2956
+
"engines": {
2957
+
"node": ">= 0.4"
2958
+
}
2959
+
},
2960
+
"node_modules/is-array-buffer": {
2961
+
"version": "3.0.5",
2962
+
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
2963
+
"integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
2964
+
"dev": true,
2965
+
"license": "MIT",
2966
+
"dependencies": {
2967
+
"call-bind": "^1.0.8",
2968
+
"call-bound": "^1.0.3",
2969
+
"get-intrinsic": "^1.2.6"
2970
+
},
2971
+
"engines": {
2972
+
"node": ">= 0.4"
2973
+
},
2974
+
"funding": {
2975
+
"url": "https://github.com/sponsors/ljharb"
2976
+
}
2977
+
},
2978
+
"node_modules/is-async-function": {
2979
+
"version": "2.1.1",
2980
+
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
2981
+
"integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
2982
+
"dev": true,
2983
+
"license": "MIT",
2984
+
"dependencies": {
2985
+
"async-function": "^1.0.0",
2986
+
"call-bound": "^1.0.3",
2987
+
"get-proto": "^1.0.1",
2988
+
"has-tostringtag": "^1.0.2",
2989
+
"safe-regex-test": "^1.1.0"
2990
+
},
2991
+
"engines": {
2992
+
"node": ">= 0.4"
2993
+
},
2994
+
"funding": {
2995
+
"url": "https://github.com/sponsors/ljharb"
2996
+
}
2997
+
},
2998
+
"node_modules/is-bigint": {
2999
+
"version": "1.1.0",
3000
+
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
3001
+
"integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
3002
+
"dev": true,
3003
+
"license": "MIT",
3004
+
"dependencies": {
3005
+
"has-bigints": "^1.0.2"
3006
+
},
3007
+
"engines": {
3008
+
"node": ">= 0.4"
3009
+
},
3010
+
"funding": {
3011
+
"url": "https://github.com/sponsors/ljharb"
3012
+
}
3013
+
},
3014
+
"node_modules/is-boolean-object": {
3015
+
"version": "1.2.2",
3016
+
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
3017
+
"integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
3018
+
"dev": true,
3019
+
"license": "MIT",
3020
+
"dependencies": {
3021
+
"call-bound": "^1.0.3",
3022
+
"has-tostringtag": "^1.0.2"
3023
+
},
3024
+
"engines": {
3025
+
"node": ">= 0.4"
3026
+
},
3027
+
"funding": {
3028
+
"url": "https://github.com/sponsors/ljharb"
3029
+
}
3030
+
},
3031
+
"node_modules/is-callable": {
3032
+
"version": "1.2.7",
3033
+
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
3034
+
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
3035
+
"dev": true,
3036
+
"license": "MIT",
3037
+
"engines": {
3038
+
"node": ">= 0.4"
3039
+
},
3040
+
"funding": {
3041
+
"url": "https://github.com/sponsors/ljharb"
3042
+
}
3043
+
},
3044
+
"node_modules/is-core-module": {
3045
+
"version": "2.16.1",
3046
+
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
3047
+
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
3048
+
"dev": true,
3049
+
"license": "MIT",
3050
+
"dependencies": {
3051
+
"hasown": "^2.0.2"
3052
+
},
3053
+
"engines": {
3054
+
"node": ">= 0.4"
3055
+
},
3056
+
"funding": {
3057
+
"url": "https://github.com/sponsors/ljharb"
3058
+
}
3059
+
},
3060
+
"node_modules/is-data-view": {
3061
+
"version": "1.0.2",
3062
+
"resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
3063
+
"integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
3064
+
"dev": true,
3065
+
"license": "MIT",
3066
+
"dependencies": {
3067
+
"call-bound": "^1.0.2",
3068
+
"get-intrinsic": "^1.2.6",
3069
+
"is-typed-array": "^1.1.13"
3070
+
},
3071
+
"engines": {
3072
+
"node": ">= 0.4"
3073
+
},
3074
+
"funding": {
3075
+
"url": "https://github.com/sponsors/ljharb"
3076
+
}
3077
+
},
3078
+
"node_modules/is-date-object": {
3079
+
"version": "1.1.0",
3080
+
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
3081
+
"integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
3082
+
"dev": true,
3083
+
"license": "MIT",
3084
+
"dependencies": {
3085
+
"call-bound": "^1.0.2",
3086
+
"has-tostringtag": "^1.0.2"
3087
+
},
3088
+
"engines": {
3089
+
"node": ">= 0.4"
3090
+
},
3091
+
"funding": {
3092
+
"url": "https://github.com/sponsors/ljharb"
3093
+
}
3094
+
},
3095
+
"node_modules/is-extglob": {
3096
+
"version": "2.1.1",
3097
+
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
3098
+
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
3099
+
"dev": true,
3100
+
"license": "MIT",
3101
+
"engines": {
3102
+
"node": ">=0.10.0"
3103
+
}
3104
+
},
3105
+
"node_modules/is-finalizationregistry": {
3106
+
"version": "1.1.1",
3107
+
"resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
3108
+
"integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
3109
+
"dev": true,
3110
+
"license": "MIT",
3111
+
"dependencies": {
3112
+
"call-bound": "^1.0.3"
3113
+
},
3114
+
"engines": {
3115
+
"node": ">= 0.4"
3116
+
},
3117
+
"funding": {
3118
+
"url": "https://github.com/sponsors/ljharb"
3119
+
}
3120
+
},
3121
+
"node_modules/is-generator-function": {
3122
+
"version": "1.1.2",
3123
+
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
3124
+
"integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
3125
+
"dev": true,
3126
+
"license": "MIT",
3127
+
"dependencies": {
3128
+
"call-bound": "^1.0.4",
3129
+
"generator-function": "^2.0.0",
3130
+
"get-proto": "^1.0.1",
3131
+
"has-tostringtag": "^1.0.2",
3132
+
"safe-regex-test": "^1.1.0"
3133
+
},
3134
+
"engines": {
3135
+
"node": ">= 0.4"
3136
+
},
3137
+
"funding": {
3138
+
"url": "https://github.com/sponsors/ljharb"
3139
+
}
3140
+
},
3141
+
"node_modules/is-glob": {
3142
+
"version": "4.0.3",
3143
+
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
3144
+
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
3145
+
"dev": true,
3146
+
"license": "MIT",
3147
+
"dependencies": {
3148
+
"is-extglob": "^2.1.1"
3149
+
},
3150
+
"engines": {
3151
+
"node": ">=0.10.0"
3152
+
}
3153
+
},
3154
+
"node_modules/is-map": {
3155
+
"version": "2.0.3",
3156
+
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
3157
+
"integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
3158
+
"dev": true,
3159
+
"license": "MIT",
3160
+
"engines": {
3161
+
"node": ">= 0.4"
3162
+
},
3163
+
"funding": {
3164
+
"url": "https://github.com/sponsors/ljharb"
3165
+
}
3166
+
},
3167
+
"node_modules/is-negative-zero": {
3168
+
"version": "2.0.3",
3169
+
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
3170
+
"integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
3171
+
"dev": true,
3172
+
"license": "MIT",
3173
+
"engines": {
3174
+
"node": ">= 0.4"
3175
+
},
3176
+
"funding": {
3177
+
"url": "https://github.com/sponsors/ljharb"
3178
+
}
3179
+
},
3180
+
"node_modules/is-number-object": {
3181
+
"version": "1.1.1",
3182
+
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
3183
+
"integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
3184
+
"dev": true,
3185
+
"license": "MIT",
3186
+
"dependencies": {
3187
+
"call-bound": "^1.0.3",
3188
+
"has-tostringtag": "^1.0.2"
3189
+
},
3190
+
"engines": {
3191
+
"node": ">= 0.4"
3192
+
},
3193
+
"funding": {
3194
+
"url": "https://github.com/sponsors/ljharb"
3195
+
}
3196
+
},
3197
+
"node_modules/is-regex": {
3198
+
"version": "1.2.1",
3199
+
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
3200
+
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
3201
+
"dev": true,
3202
+
"license": "MIT",
3203
+
"dependencies": {
3204
+
"call-bound": "^1.0.2",
3205
+
"gopd": "^1.2.0",
3206
+
"has-tostringtag": "^1.0.2",
3207
+
"hasown": "^2.0.2"
3208
+
},
3209
+
"engines": {
3210
+
"node": ">= 0.4"
3211
+
},
3212
+
"funding": {
3213
+
"url": "https://github.com/sponsors/ljharb"
3214
+
}
3215
+
},
3216
+
"node_modules/is-set": {
3217
+
"version": "2.0.3",
3218
+
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
3219
+
"integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
3220
+
"dev": true,
3221
+
"license": "MIT",
3222
+
"engines": {
3223
+
"node": ">= 0.4"
3224
+
},
3225
+
"funding": {
3226
+
"url": "https://github.com/sponsors/ljharb"
3227
+
}
3228
+
},
3229
+
"node_modules/is-shared-array-buffer": {
3230
+
"version": "1.0.4",
3231
+
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
3232
+
"integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
3233
+
"dev": true,
3234
+
"license": "MIT",
3235
+
"dependencies": {
3236
+
"call-bound": "^1.0.3"
3237
+
},
3238
+
"engines": {
3239
+
"node": ">= 0.4"
3240
+
},
3241
+
"funding": {
3242
+
"url": "https://github.com/sponsors/ljharb"
3243
+
}
3244
+
},
3245
+
"node_modules/is-string": {
3246
+
"version": "1.1.1",
3247
+
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
3248
+
"integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
3249
+
"dev": true,
3250
+
"license": "MIT",
3251
+
"dependencies": {
3252
+
"call-bound": "^1.0.3",
3253
+
"has-tostringtag": "^1.0.2"
3254
+
},
3255
+
"engines": {
3256
+
"node": ">= 0.4"
3257
+
},
3258
+
"funding": {
3259
+
"url": "https://github.com/sponsors/ljharb"
3260
+
}
3261
+
},
3262
+
"node_modules/is-symbol": {
3263
+
"version": "1.1.1",
3264
+
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
3265
+
"integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
3266
+
"dev": true,
3267
+
"license": "MIT",
3268
+
"dependencies": {
3269
+
"call-bound": "^1.0.2",
3270
+
"has-symbols": "^1.1.0",
3271
+
"safe-regex-test": "^1.1.0"
3272
+
},
3273
+
"engines": {
3274
+
"node": ">= 0.4"
3275
+
},
3276
+
"funding": {
3277
+
"url": "https://github.com/sponsors/ljharb"
3278
+
}
3279
+
},
3280
+
"node_modules/is-typed-array": {
3281
+
"version": "1.1.15",
3282
+
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
3283
+
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
3284
+
"dev": true,
3285
+
"license": "MIT",
3286
+
"dependencies": {
3287
+
"which-typed-array": "^1.1.16"
3288
+
},
3289
+
"engines": {
3290
+
"node": ">= 0.4"
3291
+
},
3292
+
"funding": {
3293
+
"url": "https://github.com/sponsors/ljharb"
3294
+
}
3295
+
},
3296
+
"node_modules/is-weakmap": {
3297
+
"version": "2.0.2",
3298
+
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
3299
+
"integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
3300
+
"dev": true,
3301
+
"license": "MIT",
3302
+
"engines": {
3303
+
"node": ">= 0.4"
3304
+
},
3305
+
"funding": {
3306
+
"url": "https://github.com/sponsors/ljharb"
3307
+
}
3308
+
},
3309
+
"node_modules/is-weakref": {
3310
+
"version": "1.1.1",
3311
+
"resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
3312
+
"integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
3313
+
"dev": true,
3314
+
"license": "MIT",
3315
+
"dependencies": {
3316
+
"call-bound": "^1.0.3"
3317
+
},
3318
+
"engines": {
3319
+
"node": ">= 0.4"
3320
+
},
3321
+
"funding": {
3322
+
"url": "https://github.com/sponsors/ljharb"
3323
+
}
3324
+
},
3325
+
"node_modules/is-weakset": {
3326
+
"version": "2.0.4",
3327
+
"resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
3328
+
"integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
3329
+
"dev": true,
3330
+
"license": "MIT",
3331
+
"dependencies": {
3332
+
"call-bound": "^1.0.3",
3333
+
"get-intrinsic": "^1.2.6"
3334
+
},
3335
+
"engines": {
3336
+
"node": ">= 0.4"
3337
+
},
3338
+
"funding": {
3339
+
"url": "https://github.com/sponsors/ljharb"
3340
+
}
3341
+
},
3342
+
"node_modules/isarray": {
3343
+
"version": "2.0.5",
3344
+
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
3345
+
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
3346
+
"dev": true,
3347
+
"license": "MIT"
3348
+
},
3349
+
"node_modules/isexe": {
3350
+
"version": "2.0.0",
3351
+
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
3352
+
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
3353
+
"dev": true,
3354
+
"license": "ISC"
3355
+
},
3356
+
"node_modules/iterator.prototype": {
3357
+
"version": "1.1.5",
3358
+
"resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
3359
+
"integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
3360
+
"dev": true,
3361
+
"license": "MIT",
3362
+
"dependencies": {
3363
+
"define-data-property": "^1.1.4",
3364
+
"es-object-atoms": "^1.0.0",
3365
+
"get-intrinsic": "^1.2.6",
3366
+
"get-proto": "^1.0.0",
3367
+
"has-symbols": "^1.1.0",
3368
+
"set-function-name": "^2.0.2"
3369
+
},
3370
+
"engines": {
3371
+
"node": ">= 0.4"
3372
+
}
3373
+
},
1425
3374
"node_modules/js-tokens": {
1426
3375
"version": "4.0.0",
1427
3376
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1428
3377
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1429
3378
"license": "MIT"
1430
3379
},
3380
+
"node_modules/js-yaml": {
3381
+
"version": "4.1.1",
3382
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
3383
+
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
3384
+
"dev": true,
3385
+
"license": "MIT",
3386
+
"dependencies": {
3387
+
"argparse": "^2.0.1"
3388
+
},
3389
+
"bin": {
3390
+
"js-yaml": "bin/js-yaml.js"
3391
+
}
3392
+
},
1431
3393
"node_modules/jsesc": {
1432
3394
"version": "3.1.0",
1433
3395
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
···
1441
3403
"node": ">=6"
1442
3404
}
1443
3405
},
3406
+
"node_modules/json-buffer": {
3407
+
"version": "3.0.1",
3408
+
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
3409
+
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
3410
+
"dev": true,
3411
+
"license": "MIT"
3412
+
},
3413
+
"node_modules/json-schema-traverse": {
3414
+
"version": "0.4.1",
3415
+
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
3416
+
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
3417
+
"dev": true,
3418
+
"license": "MIT"
3419
+
},
3420
+
"node_modules/json-stable-stringify-without-jsonify": {
3421
+
"version": "1.0.1",
3422
+
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
3423
+
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
3424
+
"dev": true,
3425
+
"license": "MIT"
3426
+
},
1444
3427
"node_modules/json5": {
1445
3428
"version": "2.2.3",
1446
3429
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
···
1454
3437
"node": ">=6"
1455
3438
}
1456
3439
},
3440
+
"node_modules/jsx-ast-utils": {
3441
+
"version": "3.3.5",
3442
+
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
3443
+
"integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
3444
+
"dev": true,
3445
+
"license": "MIT",
3446
+
"dependencies": {
3447
+
"array-includes": "^3.1.6",
3448
+
"array.prototype.flat": "^1.3.1",
3449
+
"object.assign": "^4.1.4",
3450
+
"object.values": "^1.1.6"
3451
+
},
3452
+
"engines": {
3453
+
"node": ">=4.0"
3454
+
}
3455
+
},
3456
+
"node_modules/keyv": {
3457
+
"version": "4.5.4",
3458
+
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
3459
+
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
3460
+
"dev": true,
3461
+
"license": "MIT",
3462
+
"dependencies": {
3463
+
"json-buffer": "3.0.1"
3464
+
}
3465
+
},
3466
+
"node_modules/levn": {
3467
+
"version": "0.4.1",
3468
+
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
3469
+
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
3470
+
"dev": true,
3471
+
"license": "MIT",
3472
+
"dependencies": {
3473
+
"prelude-ls": "^1.2.1",
3474
+
"type-check": "~0.4.0"
3475
+
},
3476
+
"engines": {
3477
+
"node": ">= 0.8.0"
3478
+
}
3479
+
},
3480
+
"node_modules/locate-path": {
3481
+
"version": "6.0.0",
3482
+
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
3483
+
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
3484
+
"dev": true,
3485
+
"license": "MIT",
3486
+
"dependencies": {
3487
+
"p-locate": "^5.0.0"
3488
+
},
3489
+
"engines": {
3490
+
"node": ">=10"
3491
+
},
3492
+
"funding": {
3493
+
"url": "https://github.com/sponsors/sindresorhus"
3494
+
}
3495
+
},
3496
+
"node_modules/lodash.merge": {
3497
+
"version": "4.6.2",
3498
+
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
3499
+
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
3500
+
"dev": true,
3501
+
"license": "MIT"
3502
+
},
1457
3503
"node_modules/loose-envify": {
1458
3504
"version": "1.4.0",
1459
3505
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
···
1485
3531
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
1486
3532
}
1487
3533
},
3534
+
"node_modules/math-intrinsics": {
3535
+
"version": "1.1.0",
3536
+
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
3537
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
3538
+
"dev": true,
3539
+
"license": "MIT",
3540
+
"engines": {
3541
+
"node": ">= 0.4"
3542
+
}
3543
+
},
3544
+
"node_modules/minimatch": {
3545
+
"version": "3.1.2",
3546
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
3547
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
3548
+
"dev": true,
3549
+
"license": "ISC",
3550
+
"dependencies": {
3551
+
"brace-expansion": "^1.1.7"
3552
+
},
3553
+
"engines": {
3554
+
"node": "*"
3555
+
}
3556
+
},
1488
3557
"node_modules/ms": {
1489
3558
"version": "2.1.3",
1490
3559
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
···
1511
3580
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1512
3581
}
1513
3582
},
3583
+
"node_modules/natural-compare": {
3584
+
"version": "1.4.0",
3585
+
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
3586
+
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
3587
+
"dev": true,
3588
+
"license": "MIT"
3589
+
},
1514
3590
"node_modules/node-releases": {
1515
3591
"version": "2.0.27",
1516
3592
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
1517
3593
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
3594
+
"dev": true,
3595
+
"license": "MIT"
3596
+
},
3597
+
"node_modules/object-assign": {
3598
+
"version": "4.1.1",
3599
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
3600
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
3601
+
"dev": true,
3602
+
"license": "MIT",
3603
+
"engines": {
3604
+
"node": ">=0.10.0"
3605
+
}
3606
+
},
3607
+
"node_modules/object-inspect": {
3608
+
"version": "1.13.4",
3609
+
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
3610
+
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
3611
+
"dev": true,
3612
+
"license": "MIT",
3613
+
"engines": {
3614
+
"node": ">= 0.4"
3615
+
},
3616
+
"funding": {
3617
+
"url": "https://github.com/sponsors/ljharb"
3618
+
}
3619
+
},
3620
+
"node_modules/object-keys": {
3621
+
"version": "1.1.1",
3622
+
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
3623
+
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
3624
+
"dev": true,
3625
+
"license": "MIT",
3626
+
"engines": {
3627
+
"node": ">= 0.4"
3628
+
}
3629
+
},
3630
+
"node_modules/object.assign": {
3631
+
"version": "4.1.7",
3632
+
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
3633
+
"integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
3634
+
"dev": true,
3635
+
"license": "MIT",
3636
+
"dependencies": {
3637
+
"call-bind": "^1.0.8",
3638
+
"call-bound": "^1.0.3",
3639
+
"define-properties": "^1.2.1",
3640
+
"es-object-atoms": "^1.0.0",
3641
+
"has-symbols": "^1.1.0",
3642
+
"object-keys": "^1.1.1"
3643
+
},
3644
+
"engines": {
3645
+
"node": ">= 0.4"
3646
+
},
3647
+
"funding": {
3648
+
"url": "https://github.com/sponsors/ljharb"
3649
+
}
3650
+
},
3651
+
"node_modules/object.entries": {
3652
+
"version": "1.1.9",
3653
+
"resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
3654
+
"integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
3655
+
"dev": true,
3656
+
"license": "MIT",
3657
+
"dependencies": {
3658
+
"call-bind": "^1.0.8",
3659
+
"call-bound": "^1.0.4",
3660
+
"define-properties": "^1.2.1",
3661
+
"es-object-atoms": "^1.1.1"
3662
+
},
3663
+
"engines": {
3664
+
"node": ">= 0.4"
3665
+
}
3666
+
},
3667
+
"node_modules/object.fromentries": {
3668
+
"version": "2.0.8",
3669
+
"resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
3670
+
"integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
3671
+
"dev": true,
3672
+
"license": "MIT",
3673
+
"dependencies": {
3674
+
"call-bind": "^1.0.7",
3675
+
"define-properties": "^1.2.1",
3676
+
"es-abstract": "^1.23.2",
3677
+
"es-object-atoms": "^1.0.0"
3678
+
},
3679
+
"engines": {
3680
+
"node": ">= 0.4"
3681
+
},
3682
+
"funding": {
3683
+
"url": "https://github.com/sponsors/ljharb"
3684
+
}
3685
+
},
3686
+
"node_modules/object.values": {
3687
+
"version": "1.2.1",
3688
+
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
3689
+
"integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
3690
+
"dev": true,
3691
+
"license": "MIT",
3692
+
"dependencies": {
3693
+
"call-bind": "^1.0.8",
3694
+
"call-bound": "^1.0.3",
3695
+
"define-properties": "^1.2.1",
3696
+
"es-object-atoms": "^1.0.0"
3697
+
},
3698
+
"engines": {
3699
+
"node": ">= 0.4"
3700
+
},
3701
+
"funding": {
3702
+
"url": "https://github.com/sponsors/ljharb"
3703
+
}
3704
+
},
3705
+
"node_modules/optionator": {
3706
+
"version": "0.9.4",
3707
+
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
3708
+
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
3709
+
"dev": true,
3710
+
"license": "MIT",
3711
+
"dependencies": {
3712
+
"deep-is": "^0.1.3",
3713
+
"fast-levenshtein": "^2.0.6",
3714
+
"levn": "^0.4.1",
3715
+
"prelude-ls": "^1.2.1",
3716
+
"type-check": "^0.4.0",
3717
+
"word-wrap": "^1.2.5"
3718
+
},
3719
+
"engines": {
3720
+
"node": ">= 0.8.0"
3721
+
}
3722
+
},
3723
+
"node_modules/own-keys": {
3724
+
"version": "1.0.1",
3725
+
"resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
3726
+
"integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
3727
+
"dev": true,
3728
+
"license": "MIT",
3729
+
"dependencies": {
3730
+
"get-intrinsic": "^1.2.6",
3731
+
"object-keys": "^1.1.1",
3732
+
"safe-push-apply": "^1.0.0"
3733
+
},
3734
+
"engines": {
3735
+
"node": ">= 0.4"
3736
+
},
3737
+
"funding": {
3738
+
"url": "https://github.com/sponsors/ljharb"
3739
+
}
3740
+
},
3741
+
"node_modules/p-limit": {
3742
+
"version": "3.1.0",
3743
+
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
3744
+
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
3745
+
"dev": true,
3746
+
"license": "MIT",
3747
+
"dependencies": {
3748
+
"yocto-queue": "^0.1.0"
3749
+
},
3750
+
"engines": {
3751
+
"node": ">=10"
3752
+
},
3753
+
"funding": {
3754
+
"url": "https://github.com/sponsors/sindresorhus"
3755
+
}
3756
+
},
3757
+
"node_modules/p-locate": {
3758
+
"version": "5.0.0",
3759
+
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
3760
+
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
3761
+
"dev": true,
3762
+
"license": "MIT",
3763
+
"dependencies": {
3764
+
"p-limit": "^3.0.2"
3765
+
},
3766
+
"engines": {
3767
+
"node": ">=10"
3768
+
},
3769
+
"funding": {
3770
+
"url": "https://github.com/sponsors/sindresorhus"
3771
+
}
3772
+
},
3773
+
"node_modules/parent-module": {
3774
+
"version": "1.0.1",
3775
+
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
3776
+
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
3777
+
"dev": true,
3778
+
"license": "MIT",
3779
+
"dependencies": {
3780
+
"callsites": "^3.0.0"
3781
+
},
3782
+
"engines": {
3783
+
"node": ">=6"
3784
+
}
3785
+
},
3786
+
"node_modules/path-exists": {
3787
+
"version": "4.0.0",
3788
+
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
3789
+
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
3790
+
"dev": true,
3791
+
"license": "MIT",
3792
+
"engines": {
3793
+
"node": ">=8"
3794
+
}
3795
+
},
3796
+
"node_modules/path-key": {
3797
+
"version": "3.1.1",
3798
+
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
3799
+
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
3800
+
"dev": true,
3801
+
"license": "MIT",
3802
+
"engines": {
3803
+
"node": ">=8"
3804
+
}
3805
+
},
3806
+
"node_modules/path-parse": {
3807
+
"version": "1.0.7",
3808
+
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
3809
+
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1518
3810
"dev": true,
1519
3811
"license": "MIT"
1520
3812
},
···
1539
3831
"url": "https://github.com/sponsors/jonschlinkert"
1540
3832
}
1541
3833
},
3834
+
"node_modules/possible-typed-array-names": {
3835
+
"version": "1.1.0",
3836
+
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
3837
+
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
3838
+
"dev": true,
3839
+
"license": "MIT",
3840
+
"engines": {
3841
+
"node": ">= 0.4"
3842
+
}
3843
+
},
1542
3844
"node_modules/postcss": {
1543
3845
"version": "8.5.6",
1544
3846
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
···
1568
3870
"node": "^10 || ^12 || >=14"
1569
3871
}
1570
3872
},
3873
+
"node_modules/prelude-ls": {
3874
+
"version": "1.2.1",
3875
+
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
3876
+
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
3877
+
"dev": true,
3878
+
"license": "MIT",
3879
+
"engines": {
3880
+
"node": ">= 0.8.0"
3881
+
}
3882
+
},
3883
+
"node_modules/prop-types": {
3884
+
"version": "15.8.1",
3885
+
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
3886
+
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
3887
+
"dev": true,
3888
+
"license": "MIT",
3889
+
"dependencies": {
3890
+
"loose-envify": "^1.4.0",
3891
+
"object-assign": "^4.1.1",
3892
+
"react-is": "^16.13.1"
3893
+
}
3894
+
},
3895
+
"node_modules/punycode": {
3896
+
"version": "2.3.1",
3897
+
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
3898
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
3899
+
"dev": true,
3900
+
"license": "MIT",
3901
+
"engines": {
3902
+
"node": ">=6"
3903
+
}
3904
+
},
1571
3905
"node_modules/react": {
1572
3906
"version": "18.3.1",
1573
3907
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
···
1604
3938
"react": "*"
1605
3939
}
1606
3940
},
3941
+
"node_modules/react-is": {
3942
+
"version": "16.13.1",
3943
+
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
3944
+
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
3945
+
"dev": true,
3946
+
"license": "MIT"
3947
+
},
1607
3948
"node_modules/react-refresh": {
1608
3949
"version": "0.17.0",
1609
3950
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
···
1615
3956
}
1616
3957
},
1617
3958
"node_modules/react-router": {
1618
-
"version": "6.30.2",
1619
-
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.2.tgz",
1620
-
"integrity": "sha512-H2Bm38Zu1bm8KUE5NVWRMzuIyAV8p/JrOaBJAwVmp37AXG72+CZJlEBw6pdn9i5TBgLMhNDgijS4ZlblpHyWTA==",
3959
+
"version": "6.30.3",
3960
+
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
3961
+
"integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
1621
3962
"license": "MIT",
1622
3963
"dependencies": {
1623
-
"@remix-run/router": "1.23.1"
3964
+
"@remix-run/router": "1.23.2"
1624
3965
},
1625
3966
"engines": {
1626
3967
"node": ">=14.0.0"
···
1630
3971
}
1631
3972
},
1632
3973
"node_modules/react-router-dom": {
1633
-
"version": "6.30.2",
1634
-
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.2.tgz",
1635
-
"integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==",
3974
+
"version": "6.30.3",
3975
+
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
3976
+
"integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
1636
3977
"license": "MIT",
1637
3978
"dependencies": {
1638
-
"@remix-run/router": "1.23.1",
1639
-
"react-router": "6.30.2"
3979
+
"@remix-run/router": "1.23.2",
3980
+
"react-router": "6.30.3"
1640
3981
},
1641
3982
"engines": {
1642
3983
"node": ">=14.0.0"
···
1646
3987
"react-dom": ">=16.8"
1647
3988
}
1648
3989
},
3990
+
"node_modules/reflect.getprototypeof": {
3991
+
"version": "1.0.10",
3992
+
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
3993
+
"integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
3994
+
"dev": true,
3995
+
"license": "MIT",
3996
+
"dependencies": {
3997
+
"call-bind": "^1.0.8",
3998
+
"define-properties": "^1.2.1",
3999
+
"es-abstract": "^1.23.9",
4000
+
"es-errors": "^1.3.0",
4001
+
"es-object-atoms": "^1.0.0",
4002
+
"get-intrinsic": "^1.2.7",
4003
+
"get-proto": "^1.0.1",
4004
+
"which-builtin-type": "^1.2.1"
4005
+
},
4006
+
"engines": {
4007
+
"node": ">= 0.4"
4008
+
},
4009
+
"funding": {
4010
+
"url": "https://github.com/sponsors/ljharb"
4011
+
}
4012
+
},
4013
+
"node_modules/regexp.prototype.flags": {
4014
+
"version": "1.5.4",
4015
+
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
4016
+
"integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
4017
+
"dev": true,
4018
+
"license": "MIT",
4019
+
"dependencies": {
4020
+
"call-bind": "^1.0.8",
4021
+
"define-properties": "^1.2.1",
4022
+
"es-errors": "^1.3.0",
4023
+
"get-proto": "^1.0.1",
4024
+
"gopd": "^1.2.0",
4025
+
"set-function-name": "^2.0.2"
4026
+
},
4027
+
"engines": {
4028
+
"node": ">= 0.4"
4029
+
},
4030
+
"funding": {
4031
+
"url": "https://github.com/sponsors/ljharb"
4032
+
}
4033
+
},
4034
+
"node_modules/resolve": {
4035
+
"version": "2.0.0-next.5",
4036
+
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
4037
+
"integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
4038
+
"dev": true,
4039
+
"license": "MIT",
4040
+
"dependencies": {
4041
+
"is-core-module": "^2.13.0",
4042
+
"path-parse": "^1.0.7",
4043
+
"supports-preserve-symlinks-flag": "^1.0.0"
4044
+
},
4045
+
"bin": {
4046
+
"resolve": "bin/resolve"
4047
+
},
4048
+
"funding": {
4049
+
"url": "https://github.com/sponsors/ljharb"
4050
+
}
4051
+
},
4052
+
"node_modules/resolve-from": {
4053
+
"version": "4.0.0",
4054
+
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
4055
+
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
4056
+
"dev": true,
4057
+
"license": "MIT",
4058
+
"engines": {
4059
+
"node": ">=4"
4060
+
}
4061
+
},
1649
4062
"node_modules/rollup": {
1650
4063
"version": "4.54.0",
1651
4064
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
···
1688
4101
"fsevents": "~2.3.2"
1689
4102
}
1690
4103
},
4104
+
"node_modules/safe-array-concat": {
4105
+
"version": "1.1.3",
4106
+
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
4107
+
"integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
4108
+
"dev": true,
4109
+
"license": "MIT",
4110
+
"dependencies": {
4111
+
"call-bind": "^1.0.8",
4112
+
"call-bound": "^1.0.2",
4113
+
"get-intrinsic": "^1.2.6",
4114
+
"has-symbols": "^1.1.0",
4115
+
"isarray": "^2.0.5"
4116
+
},
4117
+
"engines": {
4118
+
"node": ">=0.4"
4119
+
},
4120
+
"funding": {
4121
+
"url": "https://github.com/sponsors/ljharb"
4122
+
}
4123
+
},
4124
+
"node_modules/safe-push-apply": {
4125
+
"version": "1.0.0",
4126
+
"resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
4127
+
"integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
4128
+
"dev": true,
4129
+
"license": "MIT",
4130
+
"dependencies": {
4131
+
"es-errors": "^1.3.0",
4132
+
"isarray": "^2.0.5"
4133
+
},
4134
+
"engines": {
4135
+
"node": ">= 0.4"
4136
+
},
4137
+
"funding": {
4138
+
"url": "https://github.com/sponsors/ljharb"
4139
+
}
4140
+
},
4141
+
"node_modules/safe-regex-test": {
4142
+
"version": "1.1.0",
4143
+
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
4144
+
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
4145
+
"dev": true,
4146
+
"license": "MIT",
4147
+
"dependencies": {
4148
+
"call-bound": "^1.0.2",
4149
+
"es-errors": "^1.3.0",
4150
+
"is-regex": "^1.2.1"
4151
+
},
4152
+
"engines": {
4153
+
"node": ">= 0.4"
4154
+
},
4155
+
"funding": {
4156
+
"url": "https://github.com/sponsors/ljharb"
4157
+
}
4158
+
},
1691
4159
"node_modules/scheduler": {
1692
4160
"version": "0.23.2",
1693
4161
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
···
1707
4175
"semver": "bin/semver.js"
1708
4176
}
1709
4177
},
4178
+
"node_modules/set-function-length": {
4179
+
"version": "1.2.2",
4180
+
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
4181
+
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
4182
+
"dev": true,
4183
+
"license": "MIT",
4184
+
"dependencies": {
4185
+
"define-data-property": "^1.1.4",
4186
+
"es-errors": "^1.3.0",
4187
+
"function-bind": "^1.1.2",
4188
+
"get-intrinsic": "^1.2.4",
4189
+
"gopd": "^1.0.1",
4190
+
"has-property-descriptors": "^1.0.2"
4191
+
},
4192
+
"engines": {
4193
+
"node": ">= 0.4"
4194
+
}
4195
+
},
4196
+
"node_modules/set-function-name": {
4197
+
"version": "2.0.2",
4198
+
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
4199
+
"integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
4200
+
"dev": true,
4201
+
"license": "MIT",
4202
+
"dependencies": {
4203
+
"define-data-property": "^1.1.4",
4204
+
"es-errors": "^1.3.0",
4205
+
"functions-have-names": "^1.2.3",
4206
+
"has-property-descriptors": "^1.0.2"
4207
+
},
4208
+
"engines": {
4209
+
"node": ">= 0.4"
4210
+
}
4211
+
},
4212
+
"node_modules/set-proto": {
4213
+
"version": "1.0.0",
4214
+
"resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
4215
+
"integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
4216
+
"dev": true,
4217
+
"license": "MIT",
4218
+
"dependencies": {
4219
+
"dunder-proto": "^1.0.1",
4220
+
"es-errors": "^1.3.0",
4221
+
"es-object-atoms": "^1.0.0"
4222
+
},
4223
+
"engines": {
4224
+
"node": ">= 0.4"
4225
+
}
4226
+
},
4227
+
"node_modules/shebang-command": {
4228
+
"version": "2.0.0",
4229
+
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
4230
+
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
4231
+
"dev": true,
4232
+
"license": "MIT",
4233
+
"dependencies": {
4234
+
"shebang-regex": "^3.0.0"
4235
+
},
4236
+
"engines": {
4237
+
"node": ">=8"
4238
+
}
4239
+
},
4240
+
"node_modules/shebang-regex": {
4241
+
"version": "3.0.0",
4242
+
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
4243
+
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
4244
+
"dev": true,
4245
+
"license": "MIT",
4246
+
"engines": {
4247
+
"node": ">=8"
4248
+
}
4249
+
},
4250
+
"node_modules/side-channel": {
4251
+
"version": "1.1.0",
4252
+
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
4253
+
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
4254
+
"dev": true,
4255
+
"license": "MIT",
4256
+
"dependencies": {
4257
+
"es-errors": "^1.3.0",
4258
+
"object-inspect": "^1.13.3",
4259
+
"side-channel-list": "^1.0.0",
4260
+
"side-channel-map": "^1.0.1",
4261
+
"side-channel-weakmap": "^1.0.2"
4262
+
},
4263
+
"engines": {
4264
+
"node": ">= 0.4"
4265
+
},
4266
+
"funding": {
4267
+
"url": "https://github.com/sponsors/ljharb"
4268
+
}
4269
+
},
4270
+
"node_modules/side-channel-list": {
4271
+
"version": "1.0.0",
4272
+
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
4273
+
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
4274
+
"dev": true,
4275
+
"license": "MIT",
4276
+
"dependencies": {
4277
+
"es-errors": "^1.3.0",
4278
+
"object-inspect": "^1.13.3"
4279
+
},
4280
+
"engines": {
4281
+
"node": ">= 0.4"
4282
+
},
4283
+
"funding": {
4284
+
"url": "https://github.com/sponsors/ljharb"
4285
+
}
4286
+
},
4287
+
"node_modules/side-channel-map": {
4288
+
"version": "1.0.1",
4289
+
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
4290
+
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
4291
+
"dev": true,
4292
+
"license": "MIT",
4293
+
"dependencies": {
4294
+
"call-bound": "^1.0.2",
4295
+
"es-errors": "^1.3.0",
4296
+
"get-intrinsic": "^1.2.5",
4297
+
"object-inspect": "^1.13.3"
4298
+
},
4299
+
"engines": {
4300
+
"node": ">= 0.4"
4301
+
},
4302
+
"funding": {
4303
+
"url": "https://github.com/sponsors/ljharb"
4304
+
}
4305
+
},
4306
+
"node_modules/side-channel-weakmap": {
4307
+
"version": "1.0.2",
4308
+
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
4309
+
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
4310
+
"dev": true,
4311
+
"license": "MIT",
4312
+
"dependencies": {
4313
+
"call-bound": "^1.0.2",
4314
+
"es-errors": "^1.3.0",
4315
+
"get-intrinsic": "^1.2.5",
4316
+
"object-inspect": "^1.13.3",
4317
+
"side-channel-map": "^1.0.1"
4318
+
},
4319
+
"engines": {
4320
+
"node": ">= 0.4"
4321
+
},
4322
+
"funding": {
4323
+
"url": "https://github.com/sponsors/ljharb"
4324
+
}
4325
+
},
1710
4326
"node_modules/source-map-js": {
1711
4327
"version": "1.2.1",
1712
4328
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
···
1717
4333
"node": ">=0.10.0"
1718
4334
}
1719
4335
},
4336
+
"node_modules/stop-iteration-iterator": {
4337
+
"version": "1.1.0",
4338
+
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
4339
+
"integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
4340
+
"dev": true,
4341
+
"license": "MIT",
4342
+
"dependencies": {
4343
+
"es-errors": "^1.3.0",
4344
+
"internal-slot": "^1.1.0"
4345
+
},
4346
+
"engines": {
4347
+
"node": ">= 0.4"
4348
+
}
4349
+
},
4350
+
"node_modules/string.prototype.matchall": {
4351
+
"version": "4.0.12",
4352
+
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
4353
+
"integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
4354
+
"dev": true,
4355
+
"license": "MIT",
4356
+
"dependencies": {
4357
+
"call-bind": "^1.0.8",
4358
+
"call-bound": "^1.0.3",
4359
+
"define-properties": "^1.2.1",
4360
+
"es-abstract": "^1.23.6",
4361
+
"es-errors": "^1.3.0",
4362
+
"es-object-atoms": "^1.0.0",
4363
+
"get-intrinsic": "^1.2.6",
4364
+
"gopd": "^1.2.0",
4365
+
"has-symbols": "^1.1.0",
4366
+
"internal-slot": "^1.1.0",
4367
+
"regexp.prototype.flags": "^1.5.3",
4368
+
"set-function-name": "^2.0.2",
4369
+
"side-channel": "^1.1.0"
4370
+
},
4371
+
"engines": {
4372
+
"node": ">= 0.4"
4373
+
},
4374
+
"funding": {
4375
+
"url": "https://github.com/sponsors/ljharb"
4376
+
}
4377
+
},
4378
+
"node_modules/string.prototype.repeat": {
4379
+
"version": "1.0.0",
4380
+
"resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
4381
+
"integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
4382
+
"dev": true,
4383
+
"license": "MIT",
4384
+
"dependencies": {
4385
+
"define-properties": "^1.1.3",
4386
+
"es-abstract": "^1.17.5"
4387
+
}
4388
+
},
4389
+
"node_modules/string.prototype.trim": {
4390
+
"version": "1.2.10",
4391
+
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
4392
+
"integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
4393
+
"dev": true,
4394
+
"license": "MIT",
4395
+
"dependencies": {
4396
+
"call-bind": "^1.0.8",
4397
+
"call-bound": "^1.0.2",
4398
+
"define-data-property": "^1.1.4",
4399
+
"define-properties": "^1.2.1",
4400
+
"es-abstract": "^1.23.5",
4401
+
"es-object-atoms": "^1.0.0",
4402
+
"has-property-descriptors": "^1.0.2"
4403
+
},
4404
+
"engines": {
4405
+
"node": ">= 0.4"
4406
+
},
4407
+
"funding": {
4408
+
"url": "https://github.com/sponsors/ljharb"
4409
+
}
4410
+
},
4411
+
"node_modules/string.prototype.trimend": {
4412
+
"version": "1.0.9",
4413
+
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
4414
+
"integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
4415
+
"dev": true,
4416
+
"license": "MIT",
4417
+
"dependencies": {
4418
+
"call-bind": "^1.0.8",
4419
+
"call-bound": "^1.0.2",
4420
+
"define-properties": "^1.2.1",
4421
+
"es-object-atoms": "^1.0.0"
4422
+
},
4423
+
"engines": {
4424
+
"node": ">= 0.4"
4425
+
},
4426
+
"funding": {
4427
+
"url": "https://github.com/sponsors/ljharb"
4428
+
}
4429
+
},
4430
+
"node_modules/string.prototype.trimstart": {
4431
+
"version": "1.0.8",
4432
+
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
4433
+
"integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
4434
+
"dev": true,
4435
+
"license": "MIT",
4436
+
"dependencies": {
4437
+
"call-bind": "^1.0.7",
4438
+
"define-properties": "^1.2.1",
4439
+
"es-object-atoms": "^1.0.0"
4440
+
},
4441
+
"engines": {
4442
+
"node": ">= 0.4"
4443
+
},
4444
+
"funding": {
4445
+
"url": "https://github.com/sponsors/ljharb"
4446
+
}
4447
+
},
4448
+
"node_modules/strip-json-comments": {
4449
+
"version": "3.1.1",
4450
+
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
4451
+
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
4452
+
"dev": true,
4453
+
"license": "MIT",
4454
+
"engines": {
4455
+
"node": ">=8"
4456
+
},
4457
+
"funding": {
4458
+
"url": "https://github.com/sponsors/sindresorhus"
4459
+
}
4460
+
},
4461
+
"node_modules/supports-color": {
4462
+
"version": "7.2.0",
4463
+
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
4464
+
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
4465
+
"dev": true,
4466
+
"license": "MIT",
4467
+
"dependencies": {
4468
+
"has-flag": "^4.0.0"
4469
+
},
4470
+
"engines": {
4471
+
"node": ">=8"
4472
+
}
4473
+
},
4474
+
"node_modules/supports-preserve-symlinks-flag": {
4475
+
"version": "1.0.0",
4476
+
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
4477
+
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
4478
+
"dev": true,
4479
+
"license": "MIT",
4480
+
"engines": {
4481
+
"node": ">= 0.4"
4482
+
},
4483
+
"funding": {
4484
+
"url": "https://github.com/sponsors/ljharb"
4485
+
}
4486
+
},
1720
4487
"node_modules/tinyglobby": {
1721
4488
"version": "0.2.15",
1722
4489
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
···
1734
4501
"url": "https://github.com/sponsors/SuperchupuDev"
1735
4502
}
1736
4503
},
4504
+
"node_modules/type-check": {
4505
+
"version": "0.4.0",
4506
+
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
4507
+
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
4508
+
"dev": true,
4509
+
"license": "MIT",
4510
+
"dependencies": {
4511
+
"prelude-ls": "^1.2.1"
4512
+
},
4513
+
"engines": {
4514
+
"node": ">= 0.8.0"
4515
+
}
4516
+
},
4517
+
"node_modules/typed-array-buffer": {
4518
+
"version": "1.0.3",
4519
+
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
4520
+
"integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
4521
+
"dev": true,
4522
+
"license": "MIT",
4523
+
"dependencies": {
4524
+
"call-bound": "^1.0.3",
4525
+
"es-errors": "^1.3.0",
4526
+
"is-typed-array": "^1.1.14"
4527
+
},
4528
+
"engines": {
4529
+
"node": ">= 0.4"
4530
+
}
4531
+
},
4532
+
"node_modules/typed-array-byte-length": {
4533
+
"version": "1.0.3",
4534
+
"resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
4535
+
"integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
4536
+
"dev": true,
4537
+
"license": "MIT",
4538
+
"dependencies": {
4539
+
"call-bind": "^1.0.8",
4540
+
"for-each": "^0.3.3",
4541
+
"gopd": "^1.2.0",
4542
+
"has-proto": "^1.2.0",
4543
+
"is-typed-array": "^1.1.14"
4544
+
},
4545
+
"engines": {
4546
+
"node": ">= 0.4"
4547
+
},
4548
+
"funding": {
4549
+
"url": "https://github.com/sponsors/ljharb"
4550
+
}
4551
+
},
4552
+
"node_modules/typed-array-byte-offset": {
4553
+
"version": "1.0.4",
4554
+
"resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
4555
+
"integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
4556
+
"dev": true,
4557
+
"license": "MIT",
4558
+
"dependencies": {
4559
+
"available-typed-arrays": "^1.0.7",
4560
+
"call-bind": "^1.0.8",
4561
+
"for-each": "^0.3.3",
4562
+
"gopd": "^1.2.0",
4563
+
"has-proto": "^1.2.0",
4564
+
"is-typed-array": "^1.1.15",
4565
+
"reflect.getprototypeof": "^1.0.9"
4566
+
},
4567
+
"engines": {
4568
+
"node": ">= 0.4"
4569
+
},
4570
+
"funding": {
4571
+
"url": "https://github.com/sponsors/ljharb"
4572
+
}
4573
+
},
4574
+
"node_modules/typed-array-length": {
4575
+
"version": "1.0.7",
4576
+
"resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
4577
+
"integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
4578
+
"dev": true,
4579
+
"license": "MIT",
4580
+
"dependencies": {
4581
+
"call-bind": "^1.0.7",
4582
+
"for-each": "^0.3.3",
4583
+
"gopd": "^1.0.1",
4584
+
"is-typed-array": "^1.1.13",
4585
+
"possible-typed-array-names": "^1.0.0",
4586
+
"reflect.getprototypeof": "^1.0.6"
4587
+
},
4588
+
"engines": {
4589
+
"node": ">= 0.4"
4590
+
},
4591
+
"funding": {
4592
+
"url": "https://github.com/sponsors/ljharb"
4593
+
}
4594
+
},
4595
+
"node_modules/unbox-primitive": {
4596
+
"version": "1.1.0",
4597
+
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
4598
+
"integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
4599
+
"dev": true,
4600
+
"license": "MIT",
4601
+
"dependencies": {
4602
+
"call-bound": "^1.0.3",
4603
+
"has-bigints": "^1.0.2",
4604
+
"has-symbols": "^1.1.0",
4605
+
"which-boxed-primitive": "^1.1.1"
4606
+
},
4607
+
"engines": {
4608
+
"node": ">= 0.4"
4609
+
},
4610
+
"funding": {
4611
+
"url": "https://github.com/sponsors/ljharb"
4612
+
}
4613
+
},
1737
4614
"node_modules/update-browserslist-db": {
1738
4615
"version": "1.2.3",
1739
4616
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
···
1763
4640
},
1764
4641
"peerDependencies": {
1765
4642
"browserslist": ">= 4.21.0"
4643
+
}
4644
+
},
4645
+
"node_modules/uri-js": {
4646
+
"version": "4.4.1",
4647
+
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
4648
+
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
4649
+
"dev": true,
4650
+
"license": "BSD-2-Clause",
4651
+
"dependencies": {
4652
+
"punycode": "^2.1.0"
1766
4653
}
1767
4654
},
1768
4655
"node_modules/vite": {
···
1841
4728
}
1842
4729
}
1843
4730
},
4731
+
"node_modules/which": {
4732
+
"version": "2.0.2",
4733
+
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
4734
+
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
4735
+
"dev": true,
4736
+
"license": "ISC",
4737
+
"dependencies": {
4738
+
"isexe": "^2.0.0"
4739
+
},
4740
+
"bin": {
4741
+
"node-which": "bin/node-which"
4742
+
},
4743
+
"engines": {
4744
+
"node": ">= 8"
4745
+
}
4746
+
},
4747
+
"node_modules/which-boxed-primitive": {
4748
+
"version": "1.1.1",
4749
+
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
4750
+
"integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
4751
+
"dev": true,
4752
+
"license": "MIT",
4753
+
"dependencies": {
4754
+
"is-bigint": "^1.1.0",
4755
+
"is-boolean-object": "^1.2.1",
4756
+
"is-number-object": "^1.1.1",
4757
+
"is-string": "^1.1.1",
4758
+
"is-symbol": "^1.1.1"
4759
+
},
4760
+
"engines": {
4761
+
"node": ">= 0.4"
4762
+
},
4763
+
"funding": {
4764
+
"url": "https://github.com/sponsors/ljharb"
4765
+
}
4766
+
},
4767
+
"node_modules/which-builtin-type": {
4768
+
"version": "1.2.1",
4769
+
"resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
4770
+
"integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
4771
+
"dev": true,
4772
+
"license": "MIT",
4773
+
"dependencies": {
4774
+
"call-bound": "^1.0.2",
4775
+
"function.prototype.name": "^1.1.6",
4776
+
"has-tostringtag": "^1.0.2",
4777
+
"is-async-function": "^2.0.0",
4778
+
"is-date-object": "^1.1.0",
4779
+
"is-finalizationregistry": "^1.1.0",
4780
+
"is-generator-function": "^1.0.10",
4781
+
"is-regex": "^1.2.1",
4782
+
"is-weakref": "^1.0.2",
4783
+
"isarray": "^2.0.5",
4784
+
"which-boxed-primitive": "^1.1.0",
4785
+
"which-collection": "^1.0.2",
4786
+
"which-typed-array": "^1.1.16"
4787
+
},
4788
+
"engines": {
4789
+
"node": ">= 0.4"
4790
+
},
4791
+
"funding": {
4792
+
"url": "https://github.com/sponsors/ljharb"
4793
+
}
4794
+
},
4795
+
"node_modules/which-collection": {
4796
+
"version": "1.0.2",
4797
+
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
4798
+
"integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
4799
+
"dev": true,
4800
+
"license": "MIT",
4801
+
"dependencies": {
4802
+
"is-map": "^2.0.3",
4803
+
"is-set": "^2.0.3",
4804
+
"is-weakmap": "^2.0.2",
4805
+
"is-weakset": "^2.0.3"
4806
+
},
4807
+
"engines": {
4808
+
"node": ">= 0.4"
4809
+
},
4810
+
"funding": {
4811
+
"url": "https://github.com/sponsors/ljharb"
4812
+
}
4813
+
},
4814
+
"node_modules/which-typed-array": {
4815
+
"version": "1.1.20",
4816
+
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
4817
+
"integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
4818
+
"dev": true,
4819
+
"license": "MIT",
4820
+
"dependencies": {
4821
+
"available-typed-arrays": "^1.0.7",
4822
+
"call-bind": "^1.0.8",
4823
+
"call-bound": "^1.0.4",
4824
+
"for-each": "^0.3.5",
4825
+
"get-proto": "^1.0.1",
4826
+
"gopd": "^1.2.0",
4827
+
"has-tostringtag": "^1.0.2"
4828
+
},
4829
+
"engines": {
4830
+
"node": ">= 0.4"
4831
+
},
4832
+
"funding": {
4833
+
"url": "https://github.com/sponsors/ljharb"
4834
+
}
4835
+
},
4836
+
"node_modules/word-wrap": {
4837
+
"version": "1.2.5",
4838
+
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
4839
+
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
4840
+
"dev": true,
4841
+
"license": "MIT",
4842
+
"engines": {
4843
+
"node": ">=0.10.0"
4844
+
}
4845
+
},
1844
4846
"node_modules/yallist": {
1845
4847
"version": "3.1.1",
1846
4848
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
1847
4849
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
1848
4850
"dev": true,
1849
4851
"license": "ISC"
4852
+
},
4853
+
"node_modules/yocto-queue": {
4854
+
"version": "0.1.0",
4855
+
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
4856
+
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
4857
+
"dev": true,
4858
+
"license": "MIT",
4859
+
"engines": {
4860
+
"node": ">=10"
4861
+
},
4862
+
"funding": {
4863
+
"url": "https://github.com/sponsors/sindresorhus"
4864
+
}
4865
+
},
4866
+
"node_modules/zod": {
4867
+
"version": "4.3.5",
4868
+
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
4869
+
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
4870
+
"dev": true,
4871
+
"license": "MIT",
4872
+
"peer": true,
4873
+
"funding": {
4874
+
"url": "https://github.com/sponsors/colinhacks"
4875
+
}
4876
+
},
4877
+
"node_modules/zod-validation-error": {
4878
+
"version": "4.0.2",
4879
+
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
4880
+
"integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
4881
+
"dev": true,
4882
+
"license": "MIT",
4883
+
"engines": {
4884
+
"node": ">=18.0.0"
4885
+
},
4886
+
"peerDependencies": {
4887
+
"zod": "^3.25.0 || ^4.0.0"
4888
+
}
1850
4889
}
1851
4890
}
1852
4891
}
+7
web/package.json
+7
web/package.json
···
6
6
"scripts": {
7
7
"dev": "vite",
8
8
"build": "vite build",
9
+
"lint": "eslint .",
9
10
"preview": "vite preview"
10
11
},
11
12
"dependencies": {
···
16
17
"react-router-dom": "^6.28.0"
17
18
},
18
19
"devDependencies": {
20
+
"@eslint/js": "^9.39.2",
19
21
"@types/react": "^18.3.12",
20
22
"@types/react-dom": "^18.3.1",
21
23
"@vitejs/plugin-react": "^4.3.3",
24
+
"eslint": "^9.39.2",
25
+
"eslint-plugin-react": "^7.37.5",
26
+
"eslint-plugin-react-hooks": "^7.0.1",
27
+
"eslint-plugin-react-refresh": "^0.4.26",
28
+
"globals": "^17.0.0",
22
29
"vite": "^6.0.3"
23
30
}
24
31
}
+2
-1
web/src/App.jsx
+2
-1
web/src/App.jsx
···
15
15
import Collections from "./pages/Collections";
16
16
import CollectionDetail from "./pages/CollectionDetail";
17
17
import Privacy from "./pages/Privacy";
18
-
19
18
import Terms from "./pages/Terms";
19
+
import ScrollToTop from "./components/ScrollToTop";
20
20
21
21
function AppContent() {
22
22
return (
23
23
<div className="layout">
24
+
<ScrollToTop />
24
25
<Sidebar />
25
26
<div className="main-layout">
26
27
<main className="main-content-wrapper">
+15
web/src/api/client.js
+15
web/src/api/client.js
···
430
430
export async function getTrendingTags(limit = 10) {
431
431
return request(`${API_BASE}/tags/trending?limit=${limit}`);
432
432
}
433
+
434
+
export async function getAPIKeys() {
435
+
return request(`${API_BASE}/keys`);
436
+
}
437
+
438
+
export async function createAPIKey(name) {
439
+
return request(`${API_BASE}/keys`, {
440
+
method: "POST",
441
+
body: JSON.stringify({ name }),
442
+
});
443
+
}
444
+
445
+
export async function deleteAPIKey(id) {
446
+
return request(`${API_BASE}/keys/${id}`, { method: "DELETE" });
447
+
}
+4
-4
web/src/components/AddToCollectionModal.jsx
+4
-4
web/src/components/AddToCollectionModal.jsx
···
1
-
import { useState, useEffect } from "react";
1
+
import { useState, useEffect, useCallback } from "react";
2
2
import { X, Plus, Check, Folder } from "lucide-react";
3
3
import {
4
4
getCollections,
···
30
30
loadCollections();
31
31
setError(null);
32
32
}
33
-
}, [isOpen, user, annotationUri]);
33
+
}, [isOpen, user, annotationUri, loadCollections]);
34
34
35
-
const loadCollections = async () => {
35
+
const loadCollections = useCallback(async () => {
36
36
try {
37
37
setLoading(true);
38
38
const [data, existingURIs] = await Promise.all([
···
49
49
} finally {
50
50
setLoading(false);
51
51
}
52
-
};
52
+
}, [user?.did, annotationUri]);
53
53
54
54
const handleAdd = async (collectionUri) => {
55
55
if (addedTo.has(collectionUri)) return;
+37
-49
web/src/components/AnnotationCard.jsx
+37
-49
web/src/components/AnnotationCard.jsx
···
5
5
import {
6
6
normalizeAnnotation,
7
7
normalizeHighlight,
8
-
normalizeBookmark,
9
-
deleteAnnotation,
10
8
likeAnnotation,
11
9
unlikeAnnotation,
12
10
getReplies,
13
11
createReply,
14
12
deleteReply,
15
-
getLikeCount,
16
13
updateAnnotation,
17
14
updateHighlight,
18
-
updateBookmark,
19
15
getEditHistory,
16
+
deleteAnnotation,
20
17
} from "../api/client";
21
18
import {
22
-
HeartIcon,
23
-
MessageIcon,
24
-
TrashIcon,
25
-
ExternalLinkIcon,
26
-
HighlightIcon,
27
-
BookmarkIcon,
28
-
} from "./Icons";
29
-
import { Folder, Edit2, Save, X, Clock } from "lucide-react";
19
+
MessageSquare,
20
+
Heart,
21
+
Trash2,
22
+
Folder,
23
+
Edit2,
24
+
Save,
25
+
X,
26
+
Clock,
27
+
} from "lucide-react";
28
+
import { HighlightIcon, TrashIcon } from "./Icons";
30
29
import ShareMenu from "./ShareMenu";
31
30
32
31
function buildTextFragmentUrl(baseUrl, selector) {
···
90
89
91
90
const [hasEditHistory, setHasEditHistory] = useState(false);
92
91
93
-
useEffect(() => {}, []);
92
+
useEffect(() => {
93
+
if (data.uri && !data.color && !data.description) {
94
+
getEditHistory(data.uri)
95
+
.then((history) => {
96
+
if (history && history.length > 0) {
97
+
setHasEditHistory(true);
98
+
}
99
+
})
100
+
.catch(() => {});
101
+
}
102
+
}, [data.uri, data.color, data.description]);
94
103
95
104
const fetchHistory = async () => {
96
105
if (showHistory) {
···
216
225
}
217
226
};
218
227
219
-
const handleShare = async () => {
220
-
const uriParts = data.uri.split("/");
221
-
const did = uriParts[2];
222
-
const rkey = uriParts[uriParts.length - 1];
223
-
const shareUrl = `${window.location.origin}/at/${did}/${rkey}`;
224
-
225
-
if (navigator.share) {
226
-
try {
227
-
await navigator.share({
228
-
title: "Margin Annotation",
229
-
text: data.text?.substring(0, 100),
230
-
url: shareUrl,
231
-
});
232
-
} catch (err) {}
233
-
} else {
234
-
try {
235
-
await navigator.clipboard.writeText(shareUrl);
236
-
alert("Link copied!");
237
-
} catch {
238
-
prompt("Copy this link:", shareUrl);
239
-
}
240
-
}
241
-
};
242
-
243
228
const handleDelete = async () => {
244
229
if (!confirm("Delete this annotation? This cannot be undone.")) return;
245
230
try {
···
324
309
disabled={deleting}
325
310
title="Delete"
326
311
>
327
-
<TrashIcon size={16} />
312
+
<Trash2 size={16} />
328
313
</button>
329
314
</>
330
315
)}
···
386
371
borderLeftColor: data.color || "var(--accent)",
387
372
}}
388
373
>
389
-
<mark>"{highlightedText}"</mark>
374
+
<mark>"{highlightedText}"</mark>
390
375
</a>
391
376
)}
392
377
···
454
439
className={`annotation-action ${isLiked ? "liked" : ""}`}
455
440
onClick={handleLike}
456
441
>
457
-
<HeartIcon filled={isLiked} size={16} />
442
+
<Heart filled={isLiked} size={16} />
458
443
{likeCount > 0 && <span>{likeCount}</span>}
459
444
</button>
460
445
<button
···
471
456
setShowReplies(!showReplies);
472
457
}}
473
458
>
474
-
<MessageIcon size={16} />
459
+
<MessageSquare size={16} />
475
460
<span>{replyCount > 0 ? `${replyCount}` : "Reply"}</span>
476
461
</button>
477
462
<ShareMenu
···
561
546
onChange={(e) => setReplyText(e.target.value)}
562
547
onFocus={(e) => {
563
548
if (!user) {
564
-
e.target.blur();
565
-
login();
549
+
e.preventDefault();
550
+
alert("Please sign in to like annotations");
566
551
}
567
552
}}
568
553
rows={2}
···
589
574
);
590
575
}
591
576
592
-
export function HighlightCard({ highlight, onDelete, onAddToCollection }) {
577
+
export function HighlightCard({
578
+
highlight,
579
+
onDelete,
580
+
onAddToCollection,
581
+
onUpdate,
582
+
}) {
593
583
const { user, login } = useAuth();
594
584
const data = normalizeHighlight(highlight);
595
585
const highlightedText =
···
609
599
610
600
await updateHighlight(data.uri, editColor, tagList);
611
601
setIsEditing(false);
612
-
613
-
if (highlight.color) highlight.color = editColor;
614
-
if (highlight.tags) highlight.tags = tagList;
615
-
else highlight.value = { ...highlight.value, tags: tagList };
602
+
if (typeof onUpdate === "function")
603
+
onUpdate({ ...highlight, color: editColor, tags: tagList });
616
604
} catch (err) {
617
605
alert("Failed to update: " + err.message);
618
606
}
···
720
708
borderLeftColor: isEditing ? editColor : data.color || "#f59e0b",
721
709
}}
722
710
>
723
-
<mark>"{highlightedText}"</mark>
711
+
<mark>"{highlightedText}"</mark>
724
712
</a>
725
713
)}
726
714
-2
web/src/components/AnnotationSkeleton.jsx
-2
web/src/components/AnnotationSkeleton.jsx
+20
-10
web/src/components/BookmarkCard.jsx
+20
-10
web/src/components/BookmarkCard.jsx
···
9
9
getLikeCount,
10
10
deleteBookmark,
11
11
} from "../api/client";
12
-
import { HeartIcon, TrashIcon, ExternalLinkIcon, BookmarkIcon } from "./Icons";
12
+
import { HeartIcon, TrashIcon, BookmarkIcon } from "./Icons";
13
13
import { Folder } from "lucide-react";
14
14
import ShareMenu from "./ShareMenu";
15
15
16
-
export default function BookmarkCard({ bookmark, onAddToCollection }) {
16
+
export default function BookmarkCard({
17
+
bookmark,
18
+
onAddToCollection,
19
+
onDelete,
20
+
}) {
17
21
const { user, login } = useAuth();
18
22
const raw = bookmark;
19
23
const data =
···
34
38
if (likeRes.count !== undefined) setLikeCount(likeRes.count);
35
39
if (likeRes.liked !== undefined) setIsLiked(likeRes.liked);
36
40
}
37
-
} catch (err) {
38
-
console.error("Failed to fetch data:", err);
41
+
} catch {
42
+
/* ignore */
39
43
}
40
44
}
41
45
if (data.uri) fetchData();
···
60
64
const cid = data.cid || "";
61
65
if (data.uri && cid) await likeAnnotation(data.uri, cid);
62
66
}
63
-
} catch (err) {
67
+
} catch {
64
68
setIsLiked(!isLiked);
65
69
setLikeCount((prev) => (isLiked ? prev + 1 : prev - 1));
66
70
}
67
71
};
68
72
69
73
const handleDelete = async () => {
74
+
if (onDelete) {
75
+
onDelete(data.uri);
76
+
return;
77
+
}
78
+
70
79
if (!confirm("Delete this bookmark?")) return;
71
80
try {
72
81
setDeleting(true);
73
82
const parts = data.uri.split("/");
74
83
const rkey = parts[parts.length - 1];
75
84
await deleteBookmark(rkey);
76
-
if (onDelete) onDelete(data.uri);
77
-
else window.location.reload();
85
+
window.location.reload();
78
86
} catch (err) {
79
87
alert("Failed to delete: " + err.message);
80
88
} finally {
···
100
108
let domain = "";
101
109
try {
102
110
if (data.url) domain = new URL(data.url).hostname.replace("www.", "");
103
-
} catch {}
111
+
} catch {
112
+
/* ignore */
113
+
}
104
114
105
115
const authorDisplayName = data.author?.displayName || data.author?.handle;
106
116
const authorHandle = data.author?.handle;
···
109
119
const marginProfileUrl = authorDid ? `/profile/${authorDid}` : null;
110
120
111
121
return (
112
-
<article className="card bookmark-card">
122
+
<article className="card annotation-card bookmark-card">
113
123
<header className="annotation-header">
114
124
<div className="annotation-header-left">
115
125
<Link to={marginProfileUrl || "#"} className="annotation-avatar-link">
···
150
160
151
161
<div className="annotation-header-right">
152
162
<div style={{ display: "flex", gap: "4px" }}>
153
-
{isOwner && (
163
+
{(isOwner || onDelete) && (
154
164
<button
155
165
className="annotation-action action-icon-only"
156
166
onClick={handleDelete}
-1
web/src/components/CollectionItemCard.jsx
-1
web/src/components/CollectionItemCard.jsx
-7
web/src/components/CollectionModal.jsx
-7
web/src/components/CollectionModal.jsx
···
12
12
Camera,
13
13
Code,
14
14
Globe,
15
-
Lock,
16
15
Flag,
17
16
Tag,
18
17
Box,
···
21
20
Image,
22
21
Video,
23
22
Mail,
24
-
Phone,
25
23
MapPin,
26
24
Calendar,
27
25
Clock,
···
31
29
Users,
32
30
Home,
33
31
Briefcase,
34
-
ShoppingBag,
35
32
Gift,
36
33
Award,
37
34
Target,
38
35
TrendingUp,
39
-
BarChart,
40
-
PieChart,
41
36
Activity,
42
37
Cpu,
43
38
Database,
···
46
41
Moon,
47
42
Flame,
48
43
Leaf,
49
-
Droplet,
50
-
Snowflake,
51
44
} from "lucide-react";
52
45
import { createCollection, updateCollection } from "../api/client";
53
46
+1
-1
web/src/components/Composer.jsx
+1
-1
web/src/components/Composer.jsx
-1
web/src/components/ReplyList.jsx
-1
web/src/components/ReplyList.jsx
+44
-10
web/src/components/RightSidebar.jsx
+44
-10
web/src/components/RightSidebar.jsx
···
1
1
import { useState, useEffect } from "react";
2
2
import { Link } from "react-router-dom";
3
-
import { Download, ExternalLink } from "lucide-react";
4
-
import { SiFirefox, SiGooglechrome, SiGithub, SiBluesky } from "react-icons/si";
3
+
import { ExternalLink } from "lucide-react";
4
+
import {
5
+
SiFirefox,
6
+
SiGooglechrome,
7
+
SiGithub,
8
+
SiBluesky,
9
+
SiApple,
10
+
SiKofi,
11
+
} from "react-icons/si";
5
12
import { FaEdge } from "react-icons/fa";
6
13
import { useAuth } from "../context/AuthContext";
7
14
import { getTrendingTags } from "../api/client";
8
-
import tangledIcon from "../assets/tangled.svg";
9
15
10
16
const isFirefox =
11
17
typeof navigator !== "undefined" && /Firefox/i.test(navigator.userAgent);
12
18
const isEdge =
13
19
typeof navigator !== "undefined" && /Edg/i.test(navigator.userAgent);
14
-
const isChrome =
20
+
const isMobileSafari =
15
21
typeof navigator !== "undefined" &&
16
-
/Chrome/i.test(navigator.userAgent) &&
17
-
!isEdge;
22
+
/iPhone|iPad|iPod/.test(navigator.userAgent) &&
23
+
/Safari/.test(navigator.userAgent) &&
24
+
!/CriOS|FxiOS|OPiOS|EdgiOS/.test(navigator.userAgent);
18
25
19
26
function getExtensionInfo() {
27
+
if (isMobileSafari) {
28
+
return {
29
+
url: "https://margin.at/soon",
30
+
icon: SiApple,
31
+
name: "iOS",
32
+
label: "Coming Soon",
33
+
};
34
+
}
20
35
if (isFirefox) {
21
36
return {
22
37
url: "https://addons.mozilla.org/en-US/firefox/addon/margin/",
23
38
icon: SiFirefox,
24
39
name: "Firefox",
40
+
label: "Install for Firefox",
25
41
};
26
42
}
27
43
if (isEdge) {
···
29
45
url: "https://microsoftedge.microsoft.com/addons/detail/margin/nfjnmllpdgcdnhmmggjihjbidmeadddn",
30
46
icon: FaEdge,
31
47
name: "Edge",
48
+
label: "Install for Edge",
32
49
};
33
50
}
34
51
return {
35
52
url: "https://chromewebstore.google.com/detail/margin/cgpmbiiagnehkikhcbnhiagfomajncpa/",
36
53
icon: SiGooglechrome,
37
54
name: "Chrome",
55
+
label: "Install for Chrome",
38
56
};
39
57
}
40
58
···
55
73
return (
56
74
<aside className="right-sidebar">
57
75
<div className="right-section">
58
-
<h3 className="right-section-title">Get the Extension</h3>
76
+
<h3 className="right-section-title">
77
+
{isMobileSafari ? "Save from Safari" : "Get the Extension"}
78
+
</h3>
59
79
<p className="right-section-desc">
60
-
Annotate, highlight, and bookmark any webpage
80
+
{isMobileSafari
81
+
? "Bookmark pages using Safari's share sheet"
82
+
: "Annotate, highlight, and bookmark any webpage"}
61
83
</p>
62
84
<a
63
85
href={ext.url}
···
66
88
className="right-extension-btn"
67
89
>
68
90
<ExtIcon size={18} />
69
-
Install for {ext.name}
91
+
{ext.label}
70
92
<ExternalLink size={14} />
71
93
</a>
72
94
</div>
···
125
147
<ExternalLink size={12} />
126
148
</a>
127
149
<a
128
-
href="https://tangled.net"
150
+
href="https://tangled.org/margin.at/margin"
129
151
target="_blank"
130
152
rel="noopener noreferrer"
131
153
className="right-link"
···
145
167
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
146
168
<SiBluesky size={16} />
147
169
Bluesky
170
+
</div>
171
+
<ExternalLink size={12} />
172
+
</a>
173
+
<a
174
+
href="https://ko-fi.com/scan"
175
+
target="_blank"
176
+
rel="noopener noreferrer"
177
+
className="right-link"
178
+
>
179
+
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
180
+
<SiKofi size={16} />
181
+
Donate
148
182
</div>
149
183
<ExternalLink size={12} />
150
184
</a>
+12
web/src/components/ScrollToTop.jsx
+12
web/src/components/ScrollToTop.jsx
+4
-1
web/src/context/AuthContext.jsx
+4
-1
web/src/context/AuthContext.jsx
···
48
48
const handleLogout = async () => {
49
49
try {
50
50
await logout();
51
-
} catch {}
51
+
} catch (e) {
52
+
console.warn("Logout failed", e);
53
+
}
52
54
setUser(null);
53
55
};
54
56
···
64
66
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
65
67
}
66
68
69
+
// eslint-disable-next-line react-refresh/only-export-components
67
70
export function useAuth() {
68
71
const context = useContext(AuthContext);
69
72
if (!context) {
+34
-1
web/src/css/annotations.css
+34
-1
web/src/css/annotations.css
···
182
182
font-weight: 400;
183
183
font-family: var(--font-serif, var(--font-sans));
184
184
display: inline;
185
+
overflow-wrap: anywhere;
186
+
word-break: break-all;
187
+
padding-right: 4px;
185
188
}
186
189
187
190
.annotation-text {
···
277
280
padding-left: 46px;
278
281
}
279
282
280
-
@media (max-width: 600px) {
283
+
.annotation-text,
284
+
.reply-text,
285
+
.history-content {
286
+
overflow-wrap: break-word;
287
+
word-break: break-word;
288
+
max-width: 100%;
289
+
}
290
+
291
+
.annotation-highlight mark {
292
+
overflow-wrap: break-word;
293
+
word-break: break-word;
294
+
display: inline;
295
+
}
296
+
297
+
.annotation-header-left,
298
+
.annotation-meta,
299
+
.reply-meta {
300
+
min-width: 0;
301
+
max-width: 100%;
302
+
}
303
+
304
+
.annotation-author-row,
305
+
.reply-author {
306
+
max-width: 100%;
307
+
}
308
+
309
+
.annotation-source {
310
+
max-width: 100%;
311
+
}
312
+
313
+
@media (max-width: 768px) {
281
314
.annotation-content,
282
315
.annotation-actions,
283
316
.inline-replies {
+3
web/src/css/base.css
+3
web/src/css/base.css
···
41
41
html {
42
42
font-size: 16px;
43
43
-webkit-text-size-adjust: 100%;
44
+
overflow-x: hidden;
44
45
}
45
46
46
47
body {
···
51
52
min-height: 100vh;
52
53
-webkit-font-smoothing: antialiased;
53
54
-moz-osx-font-smoothing: grayscale;
55
+
overflow-x: hidden;
56
+
max-width: 100vw;
54
57
}
55
58
56
59
a {
+16
web/src/css/collections.css
+16
web/src/css/collections.css
···
171
171
line-height: 1.3;
172
172
}
173
173
174
+
@media (max-width: 600px) {
175
+
.collection-detail-header {
176
+
flex-direction: column;
177
+
padding: 16px;
178
+
gap: 16px;
179
+
}
180
+
181
+
.collection-detail-actions {
182
+
position: static;
183
+
margin-top: -8px;
184
+
justify-content: flex-end;
185
+
}
186
+
}
187
+
174
188
.collection-detail-desc {
175
189
color: var(--text-secondary);
176
190
font-size: 1rem;
177
191
line-height: 1.5;
178
192
margin-bottom: 12px;
179
193
max-width: 600px;
194
+
overflow-wrap: break-word;
195
+
word-break: break-word;
180
196
}
181
197
182
198
.collection-detail-stats {
+2
web/src/css/feed.css
+2
web/src/css/feed.css
+50
web/src/css/layout.css
+50
web/src/css/layout.css
···
451
451
.main-layout {
452
452
margin-left: 0;
453
453
padding-bottom: 80px;
454
+
width: 100%;
455
+
min-width: 0;
454
456
}
455
457
456
458
.main-content-wrapper {
457
459
padding: 20px 16px;
460
+
max-width: 100%;
461
+
width: 100%;
462
+
overflow-x: hidden;
463
+
min-width: 0;
458
464
}
459
465
460
466
.mobile-nav {
461
467
display: block;
468
+
max-width: 100vw;
469
+
}
470
+
471
+
.card,
472
+
.annotation-card,
473
+
.collection-card,
474
+
.profile-header,
475
+
.api-keys-section {
476
+
overflow-x: hidden;
477
+
max-width: 100%;
478
+
}
479
+
480
+
code {
481
+
word-break: break-all;
482
+
overflow-wrap: break-word;
483
+
}
484
+
485
+
pre {
486
+
overflow-x: auto;
487
+
max-width: 100%;
488
+
}
489
+
490
+
input,
491
+
textarea {
492
+
max-width: 100%;
493
+
}
494
+
495
+
.flex-row,
496
+
[style*="display: flex"][style*="gap"] {
497
+
flex-wrap: wrap;
498
+
}
499
+
500
+
.static-page {
501
+
overflow-x: hidden;
502
+
}
503
+
504
+
.static-page ol,
505
+
.static-page ul {
506
+
padding-left: 1.25rem;
507
+
}
508
+
509
+
.static-page code {
510
+
font-size: 0.75rem;
511
+
word-break: break-all;
462
512
}
463
513
}
+20
web/src/css/login.css
+20
web/src/css/login.css
···
10
10
margin: 0 auto;
11
11
}
12
12
13
+
@media (max-width: 600px) {
14
+
.login-page {
15
+
padding: 40px 16px;
16
+
}
17
+
18
+
.login-at-logo {
19
+
font-size: 4rem;
20
+
}
21
+
22
+
.login-brand-name {
23
+
font-size: 1.25rem;
24
+
}
25
+
26
+
.login-brand-icon {
27
+
width: 40px;
28
+
height: 40px;
29
+
font-size: 1.5rem;
30
+
}
31
+
}
32
+
13
33
.login-at-logo {
14
34
font-size: 5rem;
15
35
font-weight: 800;
+2
web/src/css/notifications.css
+2
web/src/css/notifications.css
+162
-155
web/src/css/profile.css
+162
-155
web/src/css/profile.css
···
1
1
.profile-header {
2
-
display: flex;
3
-
align-items: center;
4
-
gap: 24px;
5
-
margin-bottom: 32px;
6
-
padding-bottom: 24px;
7
-
border-bottom: 1px solid var(--border);
2
+
display: flex;
3
+
align-items: center;
4
+
gap: 24px;
5
+
margin-bottom: 32px;
6
+
padding-bottom: 24px;
7
+
border-bottom: 1px solid var(--border);
8
8
}
9
9
10
10
.profile-avatar {
11
-
width: 80px;
12
-
height: 80px;
13
-
min-width: 80px;
14
-
border-radius: 50%;
15
-
background: var(--bg-tertiary);
16
-
display: flex;
17
-
align-items: center;
18
-
justify-content: center;
19
-
font-weight: 600;
20
-
font-size: 2rem;
21
-
color: var(--text-secondary);
22
-
overflow: hidden;
23
-
border: 1px solid var(--border);
11
+
width: 80px;
12
+
height: 80px;
13
+
min-width: 80px;
14
+
border-radius: 50%;
15
+
background: var(--bg-tertiary);
16
+
display: flex;
17
+
align-items: center;
18
+
justify-content: center;
19
+
font-weight: 600;
20
+
font-size: 2rem;
21
+
color: var(--text-secondary);
22
+
overflow: hidden;
23
+
border: 1px solid var(--border);
24
24
}
25
25
26
26
.profile-avatar img {
27
-
width: 100%;
28
-
height: 100%;
29
-
object-fit: cover;
27
+
width: 100%;
28
+
height: 100%;
29
+
object-fit: cover;
30
30
}
31
31
32
32
.profile-avatar-link {
33
-
text-decoration: none;
33
+
text-decoration: none;
34
34
}
35
35
36
36
.profile-info {
37
-
flex: 1;
38
-
display: flex;
39
-
flex-direction: column;
40
-
gap: 4px;
37
+
flex: 1;
38
+
display: flex;
39
+
flex-direction: column;
40
+
gap: 4px;
41
41
}
42
42
43
43
.profile-name {
44
-
font-size: 1.5rem;
45
-
font-weight: 700;
46
-
color: var(--text-primary);
47
-
line-height: 1.2;
44
+
font-size: 1.5rem;
45
+
font-weight: 700;
46
+
color: var(--text-primary);
47
+
line-height: 1.2;
48
+
overflow-wrap: break-word;
49
+
word-break: break-word;
48
50
}
49
51
50
52
.profile-handle-row {
51
-
display: flex;
52
-
align-items: center;
53
-
gap: 12px;
54
-
margin-top: 4px;
55
-
flex-wrap: wrap;
53
+
display: flex;
54
+
align-items: center;
55
+
gap: 12px;
56
+
margin-top: 4px;
57
+
flex-wrap: wrap;
56
58
}
57
59
58
60
.profile-handle-link {
59
-
color: var(--text-tertiary);
60
-
text-decoration: none;
61
-
font-size: 1rem;
62
-
transition: color 0.15s;
61
+
color: var(--text-tertiary);
62
+
text-decoration: none;
63
+
font-size: 1rem;
64
+
transition: color 0.15s;
65
+
overflow-wrap: break-word;
66
+
word-break: break-all;
63
67
}
64
68
65
69
.profile-handle-link:hover {
66
-
color: var(--text-secondary);
70
+
color: var(--text-secondary);
67
71
}
68
72
69
73
.profile-bluesky-link {
70
-
display: inline-flex;
71
-
align-items: center;
72
-
gap: 6px;
73
-
color: #3b82f6;
74
-
text-decoration: none;
75
-
font-size: 0.85rem;
76
-
font-weight: 500;
77
-
padding: 2px 8px;
78
-
border-radius: var(--radius-sm);
79
-
background: rgba(59, 130, 246, 0.1);
80
-
transition: all 0.15s ease;
74
+
display: inline-flex;
75
+
align-items: center;
76
+
gap: 6px;
77
+
color: #3b82f6;
78
+
text-decoration: none;
79
+
font-size: 0.85rem;
80
+
font-weight: 500;
81
+
padding: 2px 8px;
82
+
border-radius: var(--radius-sm);
83
+
background: rgba(59, 130, 246, 0.1);
84
+
transition: all 0.15s ease;
85
+
width: fit-content;
81
86
}
82
87
83
88
.profile-bluesky-link:hover {
84
-
background: rgba(59, 130, 246, 0.15);
89
+
background: rgba(59, 130, 246, 0.15);
85
90
}
86
91
87
92
.profile-stats {
88
-
display: flex;
89
-
gap: 24px;
90
-
margin-top: 12px;
93
+
display: flex;
94
+
gap: 24px;
95
+
margin-top: 12px;
91
96
}
92
97
93
98
.profile-stat {
94
-
color: var(--text-tertiary);
95
-
font-size: 0.9rem;
99
+
color: var(--text-tertiary);
100
+
font-size: 0.9rem;
96
101
}
97
102
98
103
.profile-stat strong {
99
-
color: var(--text-primary);
100
-
font-weight: 600;
104
+
color: var(--text-primary);
105
+
font-weight: 600;
101
106
}
102
107
103
108
.profile-tabs {
104
-
display: flex;
105
-
gap: 24px;
106
-
margin-bottom: 24px;
107
-
border-bottom: 1px solid var(--border);
109
+
display: flex;
110
+
gap: 24px;
111
+
margin-bottom: 24px;
112
+
border-bottom: 1px solid var(--border);
113
+
flex-wrap: wrap;
114
+
row-gap: 8px;
108
115
}
109
116
110
117
.profile-tab {
111
-
padding: 12px 0;
112
-
font-size: 0.95rem;
113
-
font-weight: 500;
114
-
color: var(--text-tertiary);
115
-
background: transparent;
116
-
border: none;
117
-
cursor: pointer;
118
-
transition: all 0.15s ease;
119
-
position: relative;
118
+
padding: 12px 0;
119
+
font-size: 0.95rem;
120
+
font-weight: 500;
121
+
color: var(--text-tertiary);
122
+
background: transparent;
123
+
border: none;
124
+
cursor: pointer;
125
+
transition: all 0.15s ease;
126
+
position: relative;
120
127
}
121
128
122
129
.profile-tab:hover {
123
-
color: var(--text-primary);
130
+
color: var(--text-primary);
124
131
}
125
132
126
133
.profile-tab.active {
127
-
color: var(--text-primary);
134
+
color: var(--text-primary);
128
135
}
129
136
130
137
.profile-tab.active::after {
131
-
content: "";
132
-
position: absolute;
133
-
bottom: -1px;
134
-
left: 0;
135
-
right: 0;
136
-
height: 2px;
137
-
background: var(--text-primary);
138
+
content: "";
139
+
position: absolute;
140
+
bottom: -1px;
141
+
left: 0;
142
+
right: 0;
143
+
height: 2px;
144
+
background: var(--text-primary);
138
145
}
139
146
140
147
.profile-badge-wrapper {
141
-
display: inline-flex;
142
-
align-items: center;
148
+
display: inline-flex;
149
+
align-items: center;
143
150
}
144
151
145
152
.profile-badge-clickable {
146
-
position: relative;
147
-
display: inline-flex;
148
-
align-items: center;
149
-
cursor: pointer;
150
-
margin-left: 8px;
153
+
position: relative;
154
+
display: inline-flex;
155
+
align-items: center;
156
+
cursor: pointer;
157
+
margin-left: 8px;
151
158
}
152
159
153
160
.badge-info-popover {
154
-
position: absolute;
155
-
top: calc(100% + 8px);
156
-
left: 50%;
157
-
transform: translateX(-50%);
158
-
padding: 16px;
159
-
background: var(--bg-elevated);
160
-
border: 1px solid var(--border);
161
-
border-radius: var(--radius-md);
162
-
box-shadow: var(--shadow-lg);
163
-
font-size: 0.85rem;
164
-
white-space: nowrap;
165
-
z-index: 100;
166
-
min-width: 200px;
161
+
position: absolute;
162
+
top: calc(100% + 8px);
163
+
left: 50%;
164
+
transform: translateX(-50%);
165
+
padding: 16px;
166
+
background: var(--bg-elevated);
167
+
border: 1px solid var(--border);
168
+
border-radius: var(--radius-md);
169
+
box-shadow: var(--shadow-lg);
170
+
font-size: 0.85rem;
171
+
white-space: nowrap;
172
+
z-index: 100;
173
+
min-width: 200px;
167
174
}
168
175
169
176
.badge-info-title {
170
-
font-weight: 600;
171
-
color: var(--text-primary);
172
-
margin-bottom: 8px;
177
+
font-weight: 600;
178
+
color: var(--text-primary);
179
+
margin-bottom: 8px;
173
180
}
174
181
175
182
.verifier-link {
176
-
display: flex;
177
-
align-items: center;
178
-
gap: 8px;
179
-
padding: 8px;
180
-
background: var(--bg-tertiary);
181
-
border-radius: var(--radius-sm);
182
-
text-decoration: none;
183
-
transition: background 0.15s ease;
183
+
display: flex;
184
+
align-items: center;
185
+
gap: 8px;
186
+
padding: 8px;
187
+
background: var(--bg-tertiary);
188
+
border-radius: var(--radius-sm);
189
+
text-decoration: none;
190
+
transition: background 0.15s ease;
184
191
}
185
192
186
193
.verifier-link:hover {
187
-
background: var(--bg-hover);
194
+
background: var(--bg-hover);
188
195
}
189
196
190
197
.verifier-avatar {
191
-
width: 24px;
192
-
height: 24px;
193
-
border-radius: 50%;
194
-
object-fit: cover;
198
+
width: 24px;
199
+
height: 24px;
200
+
border-radius: 50%;
201
+
object-fit: cover;
195
202
}
196
203
197
204
.verifier-name {
198
-
color: var(--text-primary);
199
-
font-size: 0.85rem;
200
-
font-weight: 500;
205
+
color: var(--text-primary);
206
+
font-size: 0.85rem;
207
+
font-weight: 500;
201
208
}
202
209
203
210
.profile-suspended {
204
-
display: flex;
205
-
flex-direction: column;
206
-
align-items: center;
207
-
justify-content: center;
208
-
padding: 60px 20px;
209
-
text-align: center;
210
-
background: var(--bg-secondary);
211
-
border-radius: var(--radius-lg);
212
-
margin-top: 20px;
213
-
border: 1px solid var(--border);
211
+
display: flex;
212
+
flex-direction: column;
213
+
align-items: center;
214
+
justify-content: center;
215
+
padding: 60px 20px;
216
+
text-align: center;
217
+
background: var(--bg-secondary);
218
+
border-radius: var(--radius-lg);
219
+
margin-top: 20px;
220
+
border: 1px solid var(--border);
214
221
}
215
222
216
223
.suspended-icon {
217
-
font-size: 40px;
218
-
margin-bottom: 16px;
219
-
color: var(--text-tertiary);
224
+
font-size: 40px;
225
+
margin-bottom: 16px;
226
+
color: var(--text-tertiary);
220
227
}
221
228
222
229
.profile-suspended h2 {
223
-
color: var(--text-primary);
224
-
margin-bottom: 8px;
225
-
font-size: 1.25rem;
230
+
color: var(--text-primary);
231
+
margin-bottom: 8px;
232
+
font-size: 1.25rem;
226
233
}
227
234
228
235
@media (max-width: 640px) {
229
-
.profile-header {
230
-
flex-direction: column;
231
-
text-align: center;
232
-
}
236
+
.profile-header {
237
+
flex-direction: column;
238
+
text-align: center;
239
+
}
233
240
234
-
.profile-info {
235
-
align-items: center;
236
-
}
241
+
.profile-info {
242
+
align-items: center;
243
+
}
237
244
238
-
.profile-handle-row {
239
-
justify-content: center;
240
-
}
245
+
.profile-handle-row {
246
+
justify-content: center;
247
+
}
241
248
242
-
.profile-stats {
243
-
justify-content: center;
244
-
}
249
+
.profile-stats {
250
+
justify-content: center;
251
+
}
245
252
246
-
.profile-tabs {
247
-
justify-content: center;
248
-
gap: 16px;
249
-
}
250
-
}
253
+
.profile-tabs {
254
+
justify-content: center;
255
+
gap: 16px;
256
+
}
257
+
}
+19
web/src/css/utilities.css
+19
web/src/css/utilities.css
···
261
261
margin-bottom: 16px;
262
262
font-style: italic;
263
263
color: var(--text-secondary);
264
+
overflow-wrap: break-word;
265
+
word-break: break-word;
266
+
max-width: 100%;
264
267
}
265
268
266
269
.composer-quote-remove {
···
728
731
.bookmark-time {
729
732
color: var(--text-tertiary);
730
733
}
734
+
735
+
.bookmark-preview {
736
+
max-width: 100%;
737
+
width: 100%;
738
+
box-sizing: border-box;
739
+
}
740
+
741
+
@media (max-width: 600px) {
742
+
.bookmark-preview-content {
743
+
padding: 12px 14px;
744
+
}
745
+
746
+
.legal-content {
747
+
padding: 16px;
748
+
}
749
+
}
+7
-7
web/src/pages/Bookmarks.jsx
+7
-7
web/src/pages/Bookmarks.jsx
···
1
-
import { useState, useEffect } from "react";
1
+
import { useState, useEffect, useCallback } from "react";
2
2
import { Link } from "react-router-dom";
3
3
import { Plus } from "lucide-react";
4
4
import { useAuth } from "../context/AuthContext";
···
22
22
const [submitting, setSubmitting] = useState(false);
23
23
const [fetchingTitle, setFetchingTitle] = useState(false);
24
24
25
-
const loadBookmarks = async () => {
25
+
const loadBookmarks = useCallback(async () => {
26
26
if (!user?.did) return;
27
27
28
28
try {
···
35
35
} finally {
36
36
setLoadingBookmarks(false);
37
37
}
38
-
};
38
+
}, [user]);
39
39
40
40
useEffect(() => {
41
41
if (isAuthenticated && user) {
42
42
loadBookmarks();
43
43
}
44
-
}, [isAuthenticated, user]);
44
+
}, [isAuthenticated, user, loadBookmarks]);
45
45
46
46
const handleDelete = async (uri) => {
47
47
if (!confirm("Delete this bookmark?")) return;
···
133
133
>
134
134
<div>
135
135
<h1 className="page-title">My Bookmarks</h1>
136
-
<p className="page-description">Pages you've saved for later</p>
136
+
<p className="page-description">Pages you've saved for later</p>
137
137
</div>
138
138
<button
139
139
onClick={() => setShowAddForm(!showAddForm)}
···
274
274
</div>
275
275
<h3 className="empty-state-title">No bookmarks yet</h3>
276
276
<p className="empty-state-text">
277
-
Click "Add Bookmark" above to save a page, or use the browser
278
-
extension.
277
+
Click "Add Bookmark" above to save a page, or use the
278
+
browser extension.
279
279
</p>
280
280
</div>
281
281
) : (
+4
-4
web/src/pages/CollectionDetail.jsx
+4
-4
web/src/pages/CollectionDetail.jsx
···
1
-
import { useState, useEffect } from "react";
1
+
import { useState, useEffect, useCallback } from "react";
2
2
import { useParams, useNavigate, Link, useLocation } from "react-router-dom";
3
3
import { ArrowLeft, Edit2, Trash2, Plus } from "lucide-react";
4
4
import {
···
34
34
user?.did &&
35
35
(collection?.creator?.did === user.did || paramAuthorDid === user.did);
36
36
37
-
const fetchContext = async () => {
37
+
const fetchContext = useCallback(async () => {
38
38
try {
39
39
setLoading(true);
40
40
···
96
96
} finally {
97
97
setLoading(false);
98
98
}
99
-
};
99
+
}, [paramAuthorDid, user, handle, rkey, wildcardPath]);
100
100
101
101
useEffect(() => {
102
102
fetchContext();
103
-
}, [rkey, wildcardPath, handle, paramAuthorDid, user?.did]);
103
+
}, [fetchContext]);
104
104
105
105
const handleEditSuccess = () => {
106
106
fetchContext();
+5
-6
web/src/pages/Collections.jsx
+5
-6
web/src/pages/Collections.jsx
···
1
-
import { useState, useEffect } from "react";
2
-
import { Link } from "react-router-dom";
3
-
import { Folder, Plus, Edit2, ChevronRight } from "lucide-react";
1
+
import { useState, useEffect, useCallback } from "react";
2
+
import { Folder, Plus } from "lucide-react";
4
3
import { getCollections } from "../api/client";
5
4
import { useAuth } from "../context/AuthContext";
6
5
import CollectionModal from "../components/CollectionModal";
···
14
13
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
15
14
const [editingCollection, setEditingCollection] = useState(null);
16
15
17
-
const fetchCollections = async () => {
16
+
const fetchCollections = useCallback(async () => {
18
17
try {
19
18
setLoading(true);
20
19
const data = await getCollections(user.did);
···
25
24
} finally {
26
25
setLoading(false);
27
26
}
28
-
};
27
+
}, [user]);
29
28
30
29
useEffect(() => {
31
30
if (user) {
32
31
fetchCollections();
33
32
}
34
-
}, [user]);
33
+
}, [user, fetchCollections]);
35
34
36
35
const handleCreateSuccess = () => {
37
36
fetchCollections();
+1
-1
web/src/pages/Highlights.jsx
+1
-1
web/src/pages/Highlights.jsx
+24
-22
web/src/pages/Login.jsx
+24
-22
web/src/pages/Login.jsx
···
23
23
const isSelectionRef = useRef(false);
24
24
25
25
useEffect(() => {
26
-
if (handle.length < 3) {
27
-
setSuggestions([]);
28
-
setShowSuggestions(false);
29
-
return;
30
-
}
26
+
if (handle.length >= 3) {
27
+
if (isSelectionRef.current) {
28
+
isSelectionRef.current = false;
29
+
return;
30
+
}
31
31
32
-
if (isSelectionRef.current) {
33
-
isSelectionRef.current = false;
34
-
return;
32
+
const timer = setTimeout(async () => {
33
+
try {
34
+
const data = await searchActors(handle);
35
+
setSuggestions(data.actors || []);
36
+
setShowSuggestions(true);
37
+
setSelectedIndex(-1);
38
+
} catch (e) {
39
+
console.error("Search failed:", e);
40
+
}
41
+
}, 300);
42
+
return () => clearTimeout(timer);
35
43
}
36
-
37
-
const timer = setTimeout(async () => {
38
-
try {
39
-
const data = await searchActors(handle);
40
-
setSuggestions(data.actors || []);
41
-
setShowSuggestions(true);
42
-
setSelectedIndex(-1);
43
-
} catch (e) {
44
-
console.error("Search failed:", e);
45
-
}
46
-
}, 300);
47
-
48
-
return () => clearTimeout(timer);
49
44
}, [handle]);
50
45
51
46
useEffect(() => {
···
178
173
className="login-input"
179
174
placeholder="yourname.bsky.social"
180
175
value={handle}
181
-
onChange={(e) => setHandle(e.target.value)}
176
+
onChange={(e) => {
177
+
const val = e.target.value;
178
+
setHandle(val);
179
+
if (val.length < 3) {
180
+
setSuggestions([]);
181
+
setShowSuggestions(false);
182
+
}
183
+
}}
182
184
onKeyDown={handleKeyDown}
183
185
onFocus={() =>
184
186
handle.length >= 3 &&
+2
-1
web/src/pages/Notifications.jsx
+2
-1
web/src/pages/Notifications.jsx
+7
-7
web/src/pages/Privacy.jsx
+7
-7
web/src/pages/Privacy.jsx
···
16
16
<section>
17
17
<h2>Overview</h2>
18
18
<p>
19
-
Margin ("we", "our", or "us") is a web annotation tool that lets you
20
-
highlight, annotate, and bookmark any webpage. Your data is stored
21
-
on the decentralized AT Protocol network, giving you ownership and
22
-
control over your content.
19
+
Margin ("we", "our", or "us") is a web
20
+
annotation tool that lets you highlight, annotate, and bookmark any
21
+
webpage. Your data is stored on the decentralized AT Protocol
22
+
network, giving you ownership and control over your content.
23
23
</p>
24
24
</section>
25
25
···
111
111
<strong>Cookies:</strong> To maintain your logged-in session
112
112
</li>
113
113
<li>
114
-
<strong>Tabs:</strong> To know which page you're viewing
114
+
<strong>Tabs:</strong> To know which page you're viewing
115
115
</li>
116
116
</ul>
117
117
</section>
···
121
121
<p>You can:</p>
122
122
<ul>
123
123
<li>
124
-
Delete any annotation, highlight, or bookmark you've created
124
+
Delete any annotation, highlight, or bookmark you've created
125
125
</li>
126
126
<li>Delete your collections</li>
127
127
<li>Export your data from your PDS</li>
128
-
<li>Revoke the extension's access at any time</li>
128
+
<li>Revoke the extension's access at any time</li>
129
129
</ul>
130
130
</section>
131
131
+250
-4
web/src/pages/Profile.jsx
+250
-4
web/src/pages/Profile.jsx
···
7
7
getUserHighlights,
8
8
getUserBookmarks,
9
9
getCollections,
10
+
getAPIKeys,
11
+
createAPIKey,
12
+
deleteAPIKey,
10
13
} from "../api/client";
14
+
import { useAuth } from "../context/AuthContext";
11
15
import CollectionIcon from "../components/CollectionIcon";
12
16
import CollectionRow from "../components/CollectionRow";
13
17
import {
···
17
21
BlueskyIcon,
18
22
} from "../components/Icons";
19
23
24
+
function KeyIcon({ size = 16 }) {
25
+
return (
26
+
<svg
27
+
width={size}
28
+
height={size}
29
+
viewBox="0 0 24 24"
30
+
fill="none"
31
+
stroke="currentColor"
32
+
strokeWidth="2"
33
+
strokeLinecap="round"
34
+
strokeLinejoin="round"
35
+
>
36
+
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" />
37
+
</svg>
38
+
);
39
+
}
40
+
20
41
export default function Profile() {
21
42
const { handle } = useParams();
43
+
const { user } = useAuth();
22
44
const [activeTab, setActiveTab] = useState("annotations");
23
45
const [profile, setProfile] = useState(null);
24
46
const [annotations, setAnnotations] = useState([]);
25
47
const [highlights, setHighlights] = useState([]);
26
48
const [bookmarks, setBookmarks] = useState([]);
27
49
const [collections, setCollections] = useState([]);
50
+
const [apiKeys, setApiKeys] = useState([]);
51
+
const [newKeyName, setNewKeyName] = useState("");
52
+
const [newKey, setNewKey] = useState(null);
53
+
const [keysLoading, setKeysLoading] = useState(false);
28
54
const [loading, setLoading] = useState(true);
29
55
const [error, setError] = useState(null);
56
+
57
+
const isOwnProfile = user && (user.did === handle || user.handle === handle);
30
58
31
59
useEffect(() => {
32
60
async function fetchProfile() {
···
62
90
fetchProfile();
63
91
}, [handle]);
64
92
93
+
useEffect(() => {
94
+
if (isOwnProfile && activeTab === "apikeys") {
95
+
loadAPIKeys();
96
+
}
97
+
}, [isOwnProfile, activeTab]);
98
+
99
+
const loadAPIKeys = async () => {
100
+
setKeysLoading(true);
101
+
try {
102
+
const data = await getAPIKeys();
103
+
setApiKeys(data.keys || []);
104
+
} catch {
105
+
setApiKeys([]);
106
+
} finally {
107
+
setKeysLoading(false);
108
+
}
109
+
};
110
+
111
+
const handleCreateKey = async () => {
112
+
if (!newKeyName.trim()) return;
113
+
try {
114
+
const data = await createAPIKey(newKeyName.trim());
115
+
setNewKey(data.key);
116
+
setNewKeyName("");
117
+
loadAPIKeys();
118
+
} catch (err) {
119
+
alert("Failed to create key: " + err.message);
120
+
}
121
+
};
122
+
123
+
const handleDeleteKey = async (id) => {
124
+
if (!confirm("Delete this API key? This cannot be undone.")) return;
125
+
try {
126
+
await deleteAPIKey(id);
127
+
loadAPIKeys();
128
+
} catch (err) {
129
+
alert("Failed to delete key: " + err.message);
130
+
}
131
+
};
132
+
65
133
const displayName = profile?.displayName || profile?.handle || handle;
66
134
const displayHandle =
67
135
profile?.handle || (handle?.startsWith("did:") ? null : handle);
···
89
157
</div>
90
158
<h3 className="empty-state-title">No annotations</h3>
91
159
<p className="empty-state-text">
92
-
This user hasn't posted any annotations.
160
+
This user hasn't posted any annotations.
93
161
</p>
94
162
</div>
95
163
);
···
108
176
</div>
109
177
<h3 className="empty-state-title">No highlights</h3>
110
178
<p className="empty-state-text">
111
-
This user hasn't saved any highlights.
179
+
This user hasn't saved any highlights.
112
180
</p>
113
181
</div>
114
182
);
···
125
193
</div>
126
194
<h3 className="empty-state-title">No bookmarks</h3>
127
195
<p className="empty-state-text">
128
-
This user hasn't bookmarked any pages.
196
+
This user hasn't bookmarked any pages.
129
197
</p>
130
198
</div>
131
199
);
···
142
210
</div>
143
211
<h3 className="empty-state-title">No collections</h3>
144
212
<p className="empty-state-text">
145
-
This user hasn't created any collections.
213
+
This user hasn't created any collections.
146
214
</p>
147
215
</div>
148
216
);
···
155
223
</div>
156
224
);
157
225
}
226
+
227
+
if (activeTab === "apikeys" && isOwnProfile) {
228
+
return (
229
+
<div className="api-keys-section">
230
+
<div className="card" style={{ marginBottom: "1rem" }}>
231
+
<h3 style={{ marginBottom: "0.5rem" }}>Create API Key</h3>
232
+
<p
233
+
style={{
234
+
color: "var(--text-muted)",
235
+
marginBottom: "1rem",
236
+
fontSize: "0.875rem",
237
+
}}
238
+
>
239
+
Use API keys to create bookmarks from iOS Shortcuts or other
240
+
tools.
241
+
</p>
242
+
<div style={{ display: "flex", gap: "0.5rem" }}>
243
+
<input
244
+
type="text"
245
+
value={newKeyName}
246
+
onChange={(e) => setNewKeyName(e.target.value)}
247
+
placeholder="Key name (e.g., iOS Shortcut)"
248
+
className="input"
249
+
style={{ flex: 1 }}
250
+
/>
251
+
<button className="btn btn-primary" onClick={handleCreateKey}>
252
+
Generate
253
+
</button>
254
+
</div>
255
+
{newKey && (
256
+
<div
257
+
style={{
258
+
marginTop: "1rem",
259
+
padding: "1rem",
260
+
background: "var(--bg-secondary)",
261
+
borderRadius: "8px",
262
+
}}
263
+
>
264
+
<p
265
+
style={{
266
+
color: "var(--text-success)",
267
+
fontWeight: 500,
268
+
marginBottom: "0.5rem",
269
+
}}
270
+
>
271
+
โ Key created! Copy it now, you won't see it again.
272
+
</p>
273
+
<code
274
+
style={{
275
+
display: "block",
276
+
padding: "0.75rem",
277
+
background: "var(--bg-tertiary)",
278
+
borderRadius: "4px",
279
+
wordBreak: "break-all",
280
+
fontSize: "0.8rem",
281
+
}}
282
+
>
283
+
{newKey}
284
+
</code>
285
+
<button
286
+
className="btn btn-secondary"
287
+
style={{ marginTop: "0.5rem" }}
288
+
onClick={() => {
289
+
navigator.clipboard.writeText(newKey);
290
+
alert("Copied!");
291
+
}}
292
+
>
293
+
Copy to clipboard
294
+
</button>
295
+
</div>
296
+
)}
297
+
</div>
298
+
299
+
{keysLoading ? (
300
+
<div className="card">
301
+
<div className="skeleton skeleton-text" />
302
+
</div>
303
+
) : apiKeys.length === 0 ? (
304
+
<div className="empty-state">
305
+
<div className="empty-state-icon">
306
+
<KeyIcon size={32} />
307
+
</div>
308
+
<h3 className="empty-state-title">No API keys</h3>
309
+
<p className="empty-state-text">
310
+
Create a key to use with iOS Shortcuts.
311
+
</p>
312
+
</div>
313
+
) : (
314
+
<div className="card">
315
+
<h3 style={{ marginBottom: "1rem" }}>Your API Keys</h3>
316
+
{apiKeys.map((key) => (
317
+
<div
318
+
key={key.id}
319
+
style={{
320
+
display: "flex",
321
+
justifyContent: "space-between",
322
+
alignItems: "center",
323
+
padding: "0.75rem 0",
324
+
borderBottom: "1px solid var(--border-color)",
325
+
}}
326
+
>
327
+
<div>
328
+
<strong>{key.name}</strong>
329
+
<div
330
+
style={{
331
+
fontSize: "0.75rem",
332
+
color: "var(--text-muted)",
333
+
}}
334
+
>
335
+
Created {new Date(key.createdAt).toLocaleDateString()}
336
+
{key.lastUsedAt &&
337
+
` โข Last used ${new Date(key.lastUsedAt).toLocaleDateString()}`}
338
+
</div>
339
+
</div>
340
+
<button
341
+
className="btn btn-sm"
342
+
style={{
343
+
fontSize: "0.75rem",
344
+
padding: "0.25rem 0.5rem",
345
+
color: "#ef4444",
346
+
border: "1px solid #ef4444",
347
+
}}
348
+
onClick={() => handleDeleteKey(key.id)}
349
+
>
350
+
Revoke
351
+
</button>
352
+
</div>
353
+
))}
354
+
</div>
355
+
)}
356
+
357
+
<div className="card" style={{ marginTop: "1rem" }}>
358
+
<h3 style={{ marginBottom: "0.5rem" }}>iOS Shortcut</h3>
359
+
<p
360
+
style={{
361
+
color: "var(--text-muted)",
362
+
marginBottom: "1rem",
363
+
fontSize: "0.875rem",
364
+
}}
365
+
>
366
+
Save bookmarks from Safari's share sheet.
367
+
</p>
368
+
<a
369
+
href="#"
370
+
className="btn btn-primary"
371
+
style={{
372
+
display: "inline-flex",
373
+
alignItems: "center",
374
+
gap: "0.5rem",
375
+
opacity: 0.5,
376
+
pointerEvents: "none",
377
+
cursor: "default",
378
+
}}
379
+
onClick={(e) => e.preventDefault()}
380
+
>
381
+
<AppleIcon size={16} /> Coming Soon
382
+
</a>
383
+
</div>
384
+
</div>
385
+
);
386
+
}
158
387
};
159
388
160
389
const bskyProfileUrl = displayHandle
···
230
459
>
231
460
Collections ({collections.length})
232
461
</button>
462
+
463
+
{isOwnProfile && (
464
+
<button
465
+
className={`profile-tab ${activeTab === "apikeys" ? "active" : ""}`}
466
+
onClick={() => setActiveTab("apikeys")}
467
+
>
468
+
<KeyIcon size={14} /> API Keys
469
+
</button>
470
+
)}
233
471
</div>
234
472
235
473
{loading && (
···
262
500
</div>
263
501
);
264
502
}
503
+
504
+
function AppleIcon({ size = 16 }) {
505
+
return (
506
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
507
+
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" />
508
+
</svg>
509
+
);
510
+
}
+7
-7
web/src/pages/Terms.jsx
+7
-7
web/src/pages/Terms.jsx
···
17
17
<h2>Overview</h2>
18
18
<p>
19
19
Margin is an open-source project. By using our service, you agree to
20
-
these terms ("Terms"). If you do not agree to these Terms, please do
21
-
not use the Service.
20
+
these terms ("Terms"). If you do not agree to these Terms,
21
+
please do not use the Service.
22
22
</p>
23
23
</section>
24
24
···
26
26
<h2>Open Source</h2>
27
27
<p>
28
28
Margin is open source software. The code is available publicly and
29
-
is provided "as is", without warranty of any kind, express or
30
-
implied.
29
+
is provided "as is", without warranty of any kind, express
30
+
or implied.
31
31
</p>
32
32
</section>
33
33
···
62
62
<section>
63
63
<h2>Disclaimer</h2>
64
64
<p>
65
-
THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE". WE DISCLAIM ALL
66
-
CONDITIONS, REPRESENTATIONS AND WARRANTIES NOT EXPRESSLY SET OUT IN
67
-
THESE TERMS.
65
+
THE SERVICE IS PROVIDED "AS IS" AND "AS
66
+
AVAILABLE". WE DISCLAIM ALL CONDITIONS, REPRESENTATIONS AND
67
+
WARRANTIES NOT EXPRESSLY SET OUT IN THESE TERMS.
68
68
</p>
69
69
</section>
70
70