+2
-8
README.md
+2
-8
README.md
···
85
85
86
86
#### Install the root certificate on your machine
87
87
88
-
First, get your Caddy container ID:
89
-
90
-
```bash
91
-
docker ps
92
-
```
93
-
94
-
Then copy the cert out:
88
+
Copy the cert out:
95
89
96
90
```bash
97
-
docker cp <caddy_container_id>:/data/pki/authorities/grain/root.crt ./grain-root.crt
91
+
docker cp caddy:/data/pki/authorities/grain/root.crt ./grain-root.crt
98
92
```
99
93
100
94
Once you have grain-root.crt, install it:
+50
__generated__/index.ts
+50
__generated__/index.ts
···
39
39
export class Server {
40
40
xrpc: XrpcServer
41
41
app: AppNS
42
+
sh: ShNS
42
43
social: SocialNS
43
44
com: ComNS
44
45
45
46
constructor(options?: XrpcOptions) {
46
47
this.xrpc = createXrpcServer(schemas, options)
47
48
this.app = new AppNS(this)
49
+
this.sh = new ShNS(this)
48
50
this.social = new SocialNS(this)
49
51
this.com = new ComNS(this)
50
52
}
···
118
120
}
119
121
}
120
122
123
+
export class ShNS {
124
+
_server: Server
125
+
tangled: ShTangledNS
126
+
127
+
constructor(server: Server) {
128
+
this._server = server
129
+
this.tangled = new ShTangledNS(server)
130
+
}
131
+
}
132
+
133
+
export class ShTangledNS {
134
+
_server: Server
135
+
graph: ShTangledGraphNS
136
+
actor: ShTangledActorNS
137
+
138
+
constructor(server: Server) {
139
+
this._server = server
140
+
this.graph = new ShTangledGraphNS(server)
141
+
this.actor = new ShTangledActorNS(server)
142
+
}
143
+
}
144
+
145
+
export class ShTangledGraphNS {
146
+
_server: Server
147
+
148
+
constructor(server: Server) {
149
+
this._server = server
150
+
}
151
+
}
152
+
153
+
export class ShTangledActorNS {
154
+
_server: Server
155
+
156
+
constructor(server: Server) {
157
+
this._server = server
158
+
}
159
+
}
160
+
121
161
export class SocialNS {
122
162
_server: Server
123
163
grain: SocialGrainNS
···
131
171
export class SocialGrainNS {
132
172
_server: Server
133
173
gallery: SocialGrainGalleryNS
174
+
graph: SocialGrainGraphNS
134
175
actor: SocialGrainActorNS
135
176
photo: SocialGrainPhotoNS
136
177
137
178
constructor(server: Server) {
138
179
this._server = server
139
180
this.gallery = new SocialGrainGalleryNS(server)
181
+
this.graph = new SocialGrainGraphNS(server)
140
182
this.actor = new SocialGrainActorNS(server)
141
183
this.photo = new SocialGrainPhotoNS(server)
142
184
}
143
185
}
144
186
145
187
export class SocialGrainGalleryNS {
188
+
_server: Server
189
+
190
+
constructor(server: Server) {
191
+
this._server = server
192
+
}
193
+
}
194
+
195
+
export class SocialGrainGraphNS {
146
196
_server: Server
147
197
148
198
constructor(server: Server) {
+130
-215
__generated__/lexicons.ts
+130
-215
__generated__/lexicons.ts
···
2296
2296
},
2297
2297
},
2298
2298
},
2299
+
ShTangledGraphFollow: {
2300
+
lexicon: 1,
2301
+
id: 'sh.tangled.graph.follow',
2302
+
defs: {
2303
+
main: {
2304
+
type: 'record',
2305
+
key: 'tid',
2306
+
record: {
2307
+
type: 'object',
2308
+
required: ['subject', 'createdAt'],
2309
+
properties: {
2310
+
subject: {
2311
+
type: 'string',
2312
+
format: 'did',
2313
+
},
2314
+
createdAt: {
2315
+
type: 'string',
2316
+
format: 'datetime',
2317
+
},
2318
+
},
2319
+
},
2320
+
},
2321
+
},
2322
+
},
2323
+
ShTangledActorProfile: {
2324
+
lexicon: 1,
2325
+
id: 'sh.tangled.actor.profile',
2326
+
defs: {
2327
+
main: {
2328
+
type: 'record',
2329
+
description: 'A declaration of a Tangled account profile.',
2330
+
key: 'literal:self',
2331
+
record: {
2332
+
type: 'object',
2333
+
required: ['bluesky'],
2334
+
properties: {
2335
+
description: {
2336
+
type: 'string',
2337
+
description: 'Free-form profile description text.',
2338
+
maxGraphemes: 256,
2339
+
maxLength: 2560,
2340
+
},
2341
+
links: {
2342
+
type: 'array',
2343
+
minLength: 0,
2344
+
maxLength: 5,
2345
+
items: {
2346
+
type: 'string',
2347
+
description:
2348
+
'Any URI, intended for social profiles or websites, can be used to link DIDs/AT-URIs too.',
2349
+
},
2350
+
},
2351
+
stats: {
2352
+
type: 'array',
2353
+
minLength: 0,
2354
+
maxLength: 2,
2355
+
items: {
2356
+
type: 'string',
2357
+
description: 'Vanity stats.',
2358
+
enum: [
2359
+
'merged-pull-request-count',
2360
+
'closed-pull-request-count',
2361
+
'open-pull-request-count',
2362
+
'open-issue-count',
2363
+
'closed-issue-count',
2364
+
'repository-count',
2365
+
],
2366
+
},
2367
+
},
2368
+
bluesky: {
2369
+
type: 'boolean',
2370
+
description: 'Include link to this account on Bluesky.',
2371
+
},
2372
+
location: {
2373
+
type: 'string',
2374
+
description: 'Free-form location text.',
2375
+
maxGraphemes: 40,
2376
+
maxLength: 400,
2377
+
},
2378
+
pinnedRepositories: {
2379
+
type: 'array',
2380
+
description:
2381
+
'Any ATURI, it is up to appviews to validate these fields.',
2382
+
minLength: 0,
2383
+
maxLength: 6,
2384
+
items: {
2385
+
type: 'string',
2386
+
format: 'at-uri',
2387
+
},
2388
+
},
2389
+
},
2390
+
},
2391
+
},
2392
+
},
2393
+
},
2299
2394
SocialGrainDefs: {
2300
2395
lexicon: 1,
2301
2396
id: 'social.grain.defs',
···
2349
2444
reason: {
2350
2445
type: 'string',
2351
2446
description:
2352
-
"Expected values are 'gallery-favorite', and 'unknown'.",
2353
-
knownValues: ['gallery-favorite', 'unknown'],
2447
+
'The reason why this notification was delivered - e.g. your gallery was favd, or you received a new follower.',
2448
+
knownValues: ['follow', 'gallery-favorite', 'unknown'],
2354
2449
},
2355
2450
reasonSubject: {
2356
2451
type: 'string',
···
2468
2563
},
2469
2564
},
2470
2565
},
2566
+
SocialGrainGraphFollow: {
2567
+
lexicon: 1,
2568
+
id: 'social.grain.graph.follow',
2569
+
defs: {
2570
+
main: {
2571
+
key: 'tid',
2572
+
type: 'record',
2573
+
record: {
2574
+
type: 'object',
2575
+
required: ['subject', 'createdAt'],
2576
+
properties: {
2577
+
subject: {
2578
+
type: 'string',
2579
+
format: 'did',
2580
+
},
2581
+
createdAt: {
2582
+
type: 'string',
2583
+
format: 'datetime',
2584
+
},
2585
+
},
2586
+
},
2587
+
},
2588
+
},
2589
+
},
2471
2590
SocialGrainFavorite: {
2472
2591
lexicon: 1,
2473
2592
id: 'social.grain.favorite',
···
2632
2751
type: 'string',
2633
2752
format: 'datetime',
2634
2753
},
2635
-
apertureValue: {
2636
-
type: 'integer',
2637
-
},
2638
-
brightnessValue: {
2639
-
type: 'integer',
2640
-
},
2641
-
colorSpace: {
2642
-
type: 'integer',
2643
-
},
2644
-
contrast: {
2645
-
type: 'string',
2646
-
enum: ['Normal', 'Soft', 'Hard'],
2647
-
},
2648
-
createDate: {
2649
-
type: 'string',
2650
-
format: 'datetime',
2651
-
},
2652
-
customRendered: {
2653
-
type: 'string',
2654
-
},
2655
2754
dateTimeOriginal: {
2656
2755
type: 'string',
2657
-
format: 'datetime',
2658
2756
},
2659
-
digitalZoomRatio: {
2660
-
type: 'integer',
2661
-
},
2662
-
exifVersion: {
2757
+
exposureTime: {
2663
2758
type: 'string',
2664
2759
},
2665
-
exposureCompensation: {
2666
-
type: 'integer',
2667
-
},
2668
-
exposureMode: {
2669
-
type: 'string',
2670
-
},
2671
-
exposureProgram: {
2672
-
type: 'string',
2673
-
},
2674
-
exposureTime: {
2675
-
type: 'integer',
2676
-
},
2677
2760
fNumber: {
2678
-
type: 'integer',
2679
-
},
2680
-
fileSource: {
2681
2761
type: 'string',
2682
2762
},
2683
2763
flash: {
2684
2764
type: 'string',
2685
2765
},
2686
-
focalLength: {
2687
-
type: 'integer',
2688
-
},
2689
2766
focalLengthIn35mmFormat: {
2690
-
type: 'integer',
2691
-
},
2692
-
focalPlaneResolutionUnit: {
2693
2767
type: 'string',
2694
2768
},
2695
-
focalPlaneXResolution: {
2696
-
type: 'integer',
2697
-
},
2698
-
focalPlaneYResolution: {
2699
-
type: 'integer',
2700
-
},
2701
2769
iSO: {
2702
2770
type: 'integer',
2703
2771
},
2704
-
lensInfo: {
2705
-
type: 'array',
2706
-
items: {
2707
-
type: 'integer',
2708
-
},
2709
-
maxLength: 4,
2710
-
},
2711
-
lensModel: {
2772
+
lensMake: {
2712
2773
type: 'string',
2713
2774
},
2714
-
lightSource: {
2775
+
lensModel: {
2715
2776
type: 'string',
2716
2777
},
2717
2778
make: {
2718
2779
type: 'string',
2719
2780
},
2720
-
maxApertureValue: {
2721
-
type: 'integer',
2722
-
},
2723
-
meteringMode: {
2724
-
type: 'string',
2725
-
},
2726
2781
model: {
2727
2782
type: 'string',
2728
2783
},
2729
-
modifyDate: {
2730
-
type: 'string',
2731
-
format: 'datetime',
2732
-
},
2733
-
recommendedExposureIndex: {
2734
-
type: 'integer',
2735
-
},
2736
-
resolutionUnit: {
2737
-
type: 'string',
2738
-
},
2739
-
saturation: {
2740
-
type: 'string',
2741
-
},
2742
-
sceneCaptureType: {
2743
-
type: 'string',
2744
-
},
2745
-
sceneType: {
2746
-
type: 'string',
2747
-
},
2748
-
sensitivityType: {
2749
-
type: 'integer',
2750
-
},
2751
-
sharpness: {
2752
-
type: 'string',
2753
-
},
2754
-
shutterSpeedValue: {
2755
-
type: 'integer',
2756
-
},
2757
-
software: {
2758
-
type: 'string',
2759
-
},
2760
-
whiteBalance: {
2761
-
type: 'string',
2762
-
},
2763
-
xResolution: {
2764
-
type: 'integer',
2765
-
},
2766
-
yResolution: {
2767
-
type: 'integer',
2768
-
},
2769
2784
},
2770
2785
},
2771
2786
},
···
2776
2791
defs: {
2777
2792
main: {
2778
2793
type: 'record',
2779
-
description: 'EXIF metadata for a photo',
2794
+
description: 'Basic EXIF metadata for a photo',
2780
2795
key: 'tid',
2781
2796
record: {
2782
2797
type: 'object',
···
2790
2805
type: 'string',
2791
2806
format: 'datetime',
2792
2807
},
2793
-
apertureValue: {
2794
-
type: 'integer',
2795
-
},
2796
-
brightnessValue: {
2797
-
type: 'integer',
2798
-
},
2799
-
colorSpace: {
2800
-
type: 'integer',
2801
-
},
2802
-
contrast: {
2803
-
type: 'string',
2804
-
enum: ['Normal', 'Soft', 'Hard'],
2805
-
},
2806
-
createDate: {
2807
-
type: 'string',
2808
-
format: 'datetime',
2809
-
},
2810
-
customRendered: {
2811
-
type: 'string',
2812
-
},
2813
2808
dateTimeOriginal: {
2814
2809
type: 'string',
2815
2810
format: 'datetime',
2816
2811
},
2817
-
digitalZoomRatio: {
2818
-
type: 'integer',
2819
-
},
2820
-
exifVersion: {
2821
-
type: 'string',
2822
-
},
2823
-
exposureCompensation: {
2824
-
type: 'integer',
2825
-
},
2826
-
exposureMode: {
2827
-
type: 'string',
2828
-
},
2829
-
exposureProgram: {
2830
-
type: 'string',
2831
-
},
2832
2812
exposureTime: {
2833
2813
type: 'integer',
2834
2814
},
2835
2815
fNumber: {
2836
2816
type: 'integer',
2837
2817
},
2838
-
fileSource: {
2839
-
type: 'string',
2840
-
},
2841
2818
flash: {
2842
2819
type: 'string',
2843
2820
},
2844
-
focalLength: {
2845
-
type: 'integer',
2846
-
},
2847
2821
focalLengthIn35mmFormat: {
2848
2822
type: 'integer',
2849
2823
},
2850
-
focalPlaneResolutionUnit: {
2851
-
type: 'string',
2852
-
},
2853
-
focalPlaneXResolution: {
2854
-
type: 'integer',
2855
-
},
2856
-
focalPlaneYResolution: {
2857
-
type: 'integer',
2858
-
},
2859
2824
iSO: {
2860
2825
type: 'integer',
2861
2826
},
2862
-
lensInfo: {
2863
-
type: 'array',
2864
-
items: {
2865
-
type: 'integer',
2866
-
},
2867
-
maxLength: 4,
2827
+
lensMake: {
2828
+
type: 'string',
2868
2829
},
2869
2830
lensModel: {
2870
2831
type: 'string',
2871
2832
},
2872
-
lightSource: {
2873
-
type: 'string',
2874
-
},
2875
2833
make: {
2876
2834
type: 'string',
2877
2835
},
2878
-
maxApertureValue: {
2879
-
type: 'integer',
2880
-
},
2881
-
meteringMode: {
2882
-
type: 'string',
2883
-
},
2884
2836
model: {
2885
2837
type: 'string',
2886
-
},
2887
-
modifyDate: {
2888
-
type: 'string',
2889
-
format: 'datetime',
2890
-
},
2891
-
recommendedExposureIndex: {
2892
-
type: 'integer',
2893
-
},
2894
-
resolutionUnit: {
2895
-
type: 'string',
2896
-
},
2897
-
saturation: {
2898
-
type: 'string',
2899
-
},
2900
-
sceneCaptureType: {
2901
-
type: 'string',
2902
-
},
2903
-
sceneType: {
2904
-
type: 'string',
2905
-
},
2906
-
sensitivityType: {
2907
-
type: 'integer',
2908
-
},
2909
-
sharpness: {
2910
-
type: 'string',
2911
-
},
2912
-
shutterSpeedValue: {
2913
-
type: 'integer',
2914
-
},
2915
-
software: {
2916
-
type: 'string',
2917
-
},
2918
-
whiteBalance: {
2919
-
type: 'string',
2920
-
},
2921
-
xResolution: {
2922
-
type: 'integer',
2923
-
},
2924
-
yResolution: {
2925
-
type: 'integer',
2926
2838
},
2927
2839
},
2928
2840
},
···
3211
3123
AppBskyActorDefs: 'app.bsky.actor.defs',
3212
3124
AppBskyActorProfile: 'app.bsky.actor.profile',
3213
3125
AppBskyLabelerDefs: 'app.bsky.labeler.defs',
3126
+
ShTangledGraphFollow: 'sh.tangled.graph.follow',
3127
+
ShTangledActorProfile: 'sh.tangled.actor.profile',
3214
3128
SocialGrainDefs: 'social.grain.defs',
3215
3129
SocialGrainNotificationDefs: 'social.grain.notification.defs',
3216
3130
SocialGrainGalleryItem: 'social.grain.gallery.item',
3217
3131
SocialGrainGalleryDefs: 'social.grain.gallery.defs',
3218
3132
SocialGrainGallery: 'social.grain.gallery',
3133
+
SocialGrainGraphFollow: 'social.grain.graph.follow',
3219
3134
SocialGrainFavorite: 'social.grain.favorite',
3220
3135
SocialGrainActorDefs: 'social.grain.actor.defs',
3221
3136
SocialGrainActorProfile: 'social.grain.actor.profile',
+47
__generated__/types/sh/tangled/actor/profile.ts
+47
__generated__/types/sh/tangled/actor/profile.ts
···
1
+
/**
2
+
* GENERATED CODE - DO NOT MODIFY
3
+
*/
4
+
import { type ValidationResult, BlobRef } from "npm:@atproto/lexicon"
5
+
import { CID } from "npm:multiformats/cid"
6
+
import { validate as _validate } from '../../../../lexicons.ts'
7
+
import {
8
+
type $Typed,
9
+
is$typed as _is$typed,
10
+
type OmitKey,
11
+
} from '../../../../util.ts'
12
+
13
+
const is$typed = _is$typed,
14
+
validate = _validate
15
+
const id = 'sh.tangled.actor.profile'
16
+
17
+
export interface Record {
18
+
$type: 'sh.tangled.actor.profile'
19
+
/** Free-form profile description text. */
20
+
description?: string
21
+
links?: string[]
22
+
stats?: (
23
+
| 'merged-pull-request-count'
24
+
| 'closed-pull-request-count'
25
+
| 'open-pull-request-count'
26
+
| 'open-issue-count'
27
+
| 'closed-issue-count'
28
+
| 'repository-count'
29
+
)[]
30
+
/** Include link to this account on Bluesky. */
31
+
bluesky: boolean
32
+
/** Free-form location text. */
33
+
location?: string
34
+
/** Any ATURI, it is up to appviews to validate these fields. */
35
+
pinnedRepositories?: string[]
36
+
[k: string]: unknown
37
+
}
38
+
39
+
const hashRecord = 'main'
40
+
41
+
export function isRecord<V>(v: V) {
42
+
return is$typed(v, id, hashRecord)
43
+
}
44
+
45
+
export function validateRecord<V>(v: V) {
46
+
return validate<Record & V>(v, id, hashRecord, true)
47
+
}
+32
__generated__/types/sh/tangled/graph/follow.ts
+32
__generated__/types/sh/tangled/graph/follow.ts
···
1
+
/**
2
+
* GENERATED CODE - DO NOT MODIFY
3
+
*/
4
+
import { type ValidationResult, BlobRef } from "npm:@atproto/lexicon"
5
+
import { CID } from "npm:multiformats/cid"
6
+
import { validate as _validate } from '../../../../lexicons.ts'
7
+
import {
8
+
type $Typed,
9
+
is$typed as _is$typed,
10
+
type OmitKey,
11
+
} from '../../../../util.ts'
12
+
13
+
const is$typed = _is$typed,
14
+
validate = _validate
15
+
const id = 'sh.tangled.graph.follow'
16
+
17
+
export interface Record {
18
+
$type: 'sh.tangled.graph.follow'
19
+
subject: string
20
+
createdAt: string
21
+
[k: string]: unknown
22
+
}
23
+
24
+
const hashRecord = 'main'
25
+
26
+
export function isRecord<V>(v: V) {
27
+
return is$typed(v, id, hashRecord)
28
+
}
29
+
30
+
export function validateRecord<V>(v: V) {
31
+
return validate<Record & V>(v, id, hashRecord, true)
32
+
}
+29
build.ts
+29
build.ts
···
1
+
import * as esbuild from "esbuild";
2
+
3
+
import { denoPlugins } from "@luca/esbuild-deno-loader";
4
+
5
+
console.log("Bundling js...");
6
+
7
+
await esbuild.build({
8
+
plugins: [...denoPlugins()],
9
+
entryPoints: ["./src/static/mod.ts"],
10
+
outfile: "./static/app.esm.js",
11
+
bundle: true,
12
+
format: "esm",
13
+
sourcemap: Deno.env.get("DEV") === "true" ? "linked" : false,
14
+
minify: Deno.env.get("DEV") !== "true",
15
+
});
16
+
17
+
const command = new Deno.Command("du", {
18
+
args: ["-h", "./static/app.esm.js"],
19
+
stdout: "piped",
20
+
stderr: "piped",
21
+
});
22
+
const { code, stdout, stderr } = await command.output();
23
+
if (code === 0) {
24
+
console.log(new TextDecoder().decode(stdout));
25
+
} else {
26
+
console.error(new TextDecoder().decode(stderr));
27
+
}
28
+
29
+
esbuild.stop();
+15
-4
deno.json
+15
-4
deno.json
···
2
2
"imports": {
3
3
"$lexicon/": "./__generated__/",
4
4
"@atproto/syntax": "npm:@atproto/syntax@^0.4.0",
5
-
"@bigmoves/bff": "jsr:@bigmoves/bff@0.3.0-beta.29",
5
+
"@bigmoves/bff": "jsr:@bigmoves/bff@0.3.0-beta.32",
6
+
"@luca/esbuild-deno-loader": "jsr:@luca/esbuild-deno-loader@^0.11.1",
6
7
"@std/http": "jsr:@std/http@^1.0.17",
7
8
"@std/path": "jsr:@std/path@^1.0.9",
8
9
"@tailwindcss/cli": "npm:@tailwindcss/cli@^4.1.4",
9
10
"date-fns": "npm:date-fns@^4.1.0",
11
+
"esbuild": "npm:esbuild@^0.25.5",
12
+
"exifr": "npm:exifr@^7.1.3",
13
+
"htmx.org": "npm:htmx.org@^1.9.12",
14
+
"hyperscript.org": "npm:hyperscript.org@^0.9.14",
10
15
"popmotion": "npm:popmotion@^11.0.5",
11
16
"preact": "npm:preact@^10.26.5",
17
+
"sortablejs": "npm:sortablejs@^1.15.6",
12
18
"tailwindcss": "npm:tailwindcss@^4.1.4",
13
19
"typed-htmx": "npm:typed-htmx@^0.3.1"
14
20
},
21
+
"patch": [
22
+
"../bff/packages/bff"
23
+
],
15
24
"tasks": {
16
25
"start": "deno run -A ./src/main.tsx",
17
-
"dev": "deno run \"dev:*\"",
26
+
"build": "rm -f ./static/app.esm.js.map && deno run -A build.ts",
27
+
"dev": "DEV=true deno run \"dev:*\"",
28
+
"dev:build": "deno -A --watch=src/static/ build.ts",
18
29
"dev:server": "deno run -A --env-file=.env --watch ./src/main.tsx",
19
30
"dev:tailwind": "deno run -A --node-modules-dir npm:@tailwindcss/cli -i ./src/input.css -o ./static/styles.css --watch",
20
-
"sync": "deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.29 sync --collections=social.grain.gallery,social.grain.actor.profile,social.grain.photo,social.grain.photo.exif,social.grain.favorite,social.grain.gallery.item --external-collections=app.bsky.actor.profile,app.bsky.graph.follow",
21
-
"codegen": "deno run -A jsr:@bigmoves/bff-cli@0.3.0-beta.29 lexgen"
31
+
"sync": "deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.30 sync --collections=social.grain.gallery,social.grain.actor.profile,social.grain.photo,social.grain.favorite,social.grain.gallery.item,social.grain.graph.follow --external-collections=app.bsky.actor.profile,app.bsky.graph.follow,sh.tangled.graph.follow,sh.tangled.actor.profile",
32
+
"codegen": "deno run -A jsr:@bigmoves/bff-cli@0.3.0-beta.30 lexgen"
22
33
},
23
34
"compilerOptions": {
24
35
"jsx": "precompile",
+709
-105
deno.lock
+709
-105
deno.lock
···
2
2
"version": "5",
3
3
"specifiers": {
4
4
"jsr:@bigmoves/atproto-oauth-client@0.2": "0.2.0",
5
-
"jsr:@bigmoves/bff@0.3.0-beta.29": "0.3.0-beta.29",
5
+
"jsr:@bigmoves/bff@0.3.0-beta.32": "0.3.0-beta.32",
6
+
"jsr:@deno/gfm@0.10": "0.10.0",
7
+
"jsr:@denosaurs/emoji@0.3": "0.3.1",
8
+
"jsr:@luca/esbuild-deno-loader@~0.11.1": "0.11.1",
9
+
"jsr:@std/assert@^1.0.12": "1.0.13",
6
10
"jsr:@std/assert@^1.0.13": "1.0.13",
7
-
"jsr:@std/cli@^1.0.18": "1.0.18",
11
+
"jsr:@std/async@^1.0.12": "1.0.12",
12
+
"jsr:@std/bytes@^1.0.2": "1.0.6",
13
+
"jsr:@std/cache@0.2": "0.2.0",
14
+
"jsr:@std/cli@^1.0.16": "1.0.19",
15
+
"jsr:@std/cli@^1.0.18": "1.0.19",
16
+
"jsr:@std/data-structures@^1.0.6": "1.0.7",
8
17
"jsr:@std/encoding@^1.0.10": "1.0.10",
18
+
"jsr:@std/encoding@^1.0.5": "1.0.10",
9
19
"jsr:@std/fmt@^1.0.8": "1.0.8",
20
+
"jsr:@std/fs@^1.0.16": "1.0.17",
10
21
"jsr:@std/html@^1.0.4": "1.0.4",
11
22
"jsr:@std/http@^1.0.13": "1.0.17",
12
23
"jsr:@std/http@^1.0.17": "1.0.17",
13
24
"jsr:@std/internal@^1.0.6": "1.0.8",
14
25
"jsr:@std/media-types@^1.1.0": "1.1.0",
15
26
"jsr:@std/net@^1.0.4": "1.0.4",
27
+
"jsr:@std/path@^1.0.6": "1.1.0",
16
28
"jsr:@std/path@^1.0.8": "1.1.0",
17
29
"jsr:@std/path@^1.0.9": "1.1.0",
18
30
"jsr:@std/path@^1.1.0": "1.1.0",
19
31
"jsr:@std/streams@^1.0.9": "1.0.9",
20
-
"npm:@atproto-labs/handle-resolver-node@~0.1.14": "0.1.15",
32
+
"jsr:@std/testing@^1.0.11": "1.0.11",
33
+
"npm:@atproto-labs/handle-resolver-node@~0.1.14": "0.1.16",
21
34
"npm:@atproto-labs/simple-store@~0.1.2": "0.1.2",
22
-
"npm:@atproto/api@~0.15.7": "0.15.7",
35
+
"npm:@atproto/api@~0.15.7": "0.15.12",
23
36
"npm:@atproto/common@~0.4.10": "0.4.11",
24
37
"npm:@atproto/identity@~0.4.7": "0.4.8",
25
38
"npm:@atproto/jwk@0.1.4": "0.1.4",
26
-
"npm:@atproto/lex-cli@*": "0.8.1",
39
+
"npm:@atproto/lex-cli@*": "0.8.2",
27
40
"npm:@atproto/lexicon@*": "0.4.11",
28
41
"npm:@atproto/lexicon@~0.4.11": "0.4.11",
29
-
"npm:@atproto/oauth-client@~0.3.13": "0.3.16",
30
-
"npm:@atproto/oauth-types@~0.2.4": "0.2.7",
42
+
"npm:@atproto/oauth-client@~0.3.13": "0.3.20",
43
+
"npm:@atproto/oauth-types@~0.2.4": "0.2.8",
31
44
"npm:@atproto/syntax@0.4": "0.4.0",
32
-
"npm:@atproto/xrpc-server@*": "0.7.18",
33
-
"npm:@tailwindcss/cli@*": "4.1.7",
34
-
"npm:@tailwindcss/cli@^4.1.4": "4.1.7",
45
+
"npm:@atproto/xrpc-server@*": "0.7.19",
46
+
"npm:@tailwindcss/cli@*": "4.1.8",
47
+
"npm:@tailwindcss/cli@^4.0.12": "4.1.8",
48
+
"npm:@tailwindcss/cli@^4.1.3": "4.1.8",
49
+
"npm:@tailwindcss/cli@^4.1.4": "4.1.8",
35
50
"npm:@types/node@*": "22.15.15",
51
+
"npm:buffer@^6.0.3": "6.0.3",
36
52
"npm:clsx@^2.1.1": "2.1.1",
37
53
"npm:date-fns@^4.1.0": "4.1.0",
54
+
"npm:esbuild@~0.25.5": "0.25.5",
55
+
"npm:exif-esm@^1.0.1": "1.0.1",
56
+
"npm:exifr@^7.1.3": "7.1.3",
57
+
"npm:github-slugger@2": "2.0.0",
58
+
"npm:he@^1.2.0": "1.2.0",
59
+
"npm:htmx.org@^1.9.12": "1.9.12",
60
+
"npm:hyperscript.org@~0.9.14": "0.9.14",
38
61
"npm:jose@5.9.6": "5.9.6",
39
-
"npm:multiformats@*": "13.3.4",
40
-
"npm:multiformats@^13.3.2": "13.3.4",
62
+
"npm:katex@0.16": "0.16.22",
63
+
"npm:marked-alert@2": "2.1.2_marked@12.0.2",
64
+
"npm:marked-footnote@^1.2.0": "1.2.4_marked@12.0.2",
65
+
"npm:marked-gfm-heading-id@^3.1.0": "3.2.0_marked@12.0.2",
66
+
"npm:marked@12": "12.0.2",
67
+
"npm:multiformats@*": "13.3.6",
68
+
"npm:multiformats@^13.3.2": "13.3.6",
41
69
"npm:popmotion@^11.0.5": "11.0.5",
42
-
"npm:preact-render-to-string@^6.5.13": "6.5.13_preact@10.26.6",
43
-
"npm:preact@^10.26.5": "10.26.6",
70
+
"npm:preact-render-to-string@^6.5.13": "6.5.13_preact@10.26.8",
71
+
"npm:preact@^10.26.5": "10.26.8",
72
+
"npm:prismjs@^1.29.0": "1.30.0",
73
+
"npm:sanitize-html@^2.13.0": "2.17.0",
74
+
"npm:sharp@~0.34.1": "0.34.1",
75
+
"npm:sortablejs@^1.15.6": "1.15.6",
44
76
"npm:tailwind-merge@^3.2.0": "3.3.0",
45
-
"npm:tailwindcss@^4.1.4": "4.1.7",
77
+
"npm:tailwindcss@^4.0.12": "4.1.8",
78
+
"npm:tailwindcss@^4.1.3": "4.1.8",
79
+
"npm:tailwindcss@^4.1.4": "4.1.8",
46
80
"npm:typed-htmx@~0.3.1": "0.3.1"
47
81
},
48
82
"jsr": {
···
65
99
"npm:tailwind-merge"
66
100
]
67
101
},
68
-
"@bigmoves/bff@0.3.0-beta.29": {
69
-
"integrity": "dd96a6601940ccf4be342bb89c87d5f0fae2f366341fbaa0722f8d17ebb3c047",
102
+
"@bigmoves/bff@0.3.0-beta.32": {
103
+
"integrity": "d33581157c6d52bd9ecccdbcb090559377de71d28137ce7fdf3882740390a389",
70
104
"dependencies": [
71
105
"jsr:@bigmoves/atproto-oauth-client",
72
-
"jsr:@std/assert",
106
+
"jsr:@std/assert@^1.0.13",
73
107
"jsr:@std/fmt",
74
108
"jsr:@std/http@^1.0.13",
75
109
"jsr:@std/path@^1.0.8",
···
86
120
"npm:tailwind-merge"
87
121
]
88
122
},
123
+
"@deno/gfm@0.10.0": {
124
+
"integrity": "51708205e3559a4aeb6afb29d07c5bfafe7941f91bb360351ef6621de9a39527",
125
+
"dependencies": [
126
+
"jsr:@denosaurs/emoji",
127
+
"npm:github-slugger",
128
+
"npm:he",
129
+
"npm:katex",
130
+
"npm:marked",
131
+
"npm:marked-alert",
132
+
"npm:marked-footnote",
133
+
"npm:marked-gfm-heading-id",
134
+
"npm:prismjs",
135
+
"npm:sanitize-html"
136
+
]
137
+
},
138
+
"@denosaurs/emoji@0.3.1": {
139
+
"integrity": "b0aed5f55dec99e83da7c9637fe0a36d1d6252b7c99deaaa3fc5dea3fcf3da8b"
140
+
},
141
+
"@luca/esbuild-deno-loader@0.11.1": {
142
+
"integrity": "dc020d16d75b591f679f6b9288b10f38bdb4f24345edb2f5732affa1d9885267",
143
+
"dependencies": [
144
+
"jsr:@std/bytes",
145
+
"jsr:@std/encoding@^1.0.5",
146
+
"jsr:@std/path@^1.0.6"
147
+
]
148
+
},
89
149
"@std/assert@1.0.13": {
90
150
"integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29",
91
151
"dependencies": [
92
152
"jsr:@std/internal"
93
153
]
94
154
},
95
-
"@std/cli@1.0.18": {
96
-
"integrity": "33846eab6a7cac52156cc105a798451df06965693606e4668adfe0436a155fd7"
155
+
"@std/async@1.0.12": {
156
+
"integrity": "d1bfcec459e8012846fe4e38dfc4241ab23240ecda3d8d6dfcf6d81a632e803d"
157
+
},
158
+
"@std/bytes@1.0.6": {
159
+
"integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a"
160
+
},
161
+
"@std/cache@0.2.0": {
162
+
"integrity": "63a2ccd5a9e7c03e430f7d34dfcfd0d0cfc90731a1eaf8208f4c66e418fc3035"
163
+
},
164
+
"@std/cli@1.0.19": {
165
+
"integrity": "b3601a54891f89f3f738023af11960c4e6f7a45dc76cde39a6861124cba79e88"
166
+
},
167
+
"@std/data-structures@1.0.7": {
168
+
"integrity": "16932d2c8d281f65eaaa2209af2473209881e33b1ced54cd1b015e7b4cdbb0d2"
97
169
},
98
170
"@std/encoding@1.0.10": {
99
171
"integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1"
···
101
173
"@std/fmt@1.0.8": {
102
174
"integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7"
103
175
},
176
+
"@std/fs@1.0.17": {
177
+
"integrity": "1c00c632677c1158988ef7a004cb16137f870aafdb8163b9dce86ec652f3952b",
178
+
"dependencies": [
179
+
"jsr:@std/path@^1.0.9"
180
+
]
181
+
},
104
182
"@std/html@1.0.4": {
105
183
"integrity": "eff3497c08164e6ada49b7f81a28b5108087033823153d065e3f89467dd3d50e"
106
184
},
107
185
"@std/http@1.0.17": {
108
186
"integrity": "98aec8ab4080d95c21f731e3008f69c29c5012d12f1b4e553f85935db601569f",
109
187
"dependencies": [
110
-
"jsr:@std/cli",
111
-
"jsr:@std/encoding",
188
+
"jsr:@std/cli@^1.0.18",
189
+
"jsr:@std/encoding@^1.0.10",
112
190
"jsr:@std/fmt",
113
191
"jsr:@std/html",
114
192
"jsr:@std/media-types",
···
131
209
},
132
210
"@std/streams@1.0.9": {
133
211
"integrity": "a9d26b1988cdd7aa7b1f4b51e1c36c1557f3f252880fa6cc5b9f37078b1a5035"
212
+
},
213
+
"@std/testing@1.0.11": {
214
+
"integrity": "12b3db12d34f0f385a26248933bde766c0f8c5ad8b6ab34d4d38f528ab852f48",
215
+
"dependencies": [
216
+
"jsr:@std/assert@^1.0.12",
217
+
"jsr:@std/async",
218
+
"jsr:@std/data-structures",
219
+
"jsr:@std/fs",
220
+
"jsr:@std/internal",
221
+
"jsr:@std/path@^1.0.8"
222
+
]
134
223
}
135
224
},
136
225
"npm": {
···
141
230
"@jridgewell/trace-mapping"
142
231
]
143
232
},
144
-
"@atproto-labs/did-resolver@0.1.12": {
145
-
"integrity": "sha512-criWN7o21C5TFsauB+bGTlkqqerOU6gT2TbxdQVgZUWqNcfazUmUjT4gJAY02i+O4d3QmZa27fv9CcaRKWkSug==",
233
+
"@atproto-labs/did-resolver@0.1.13": {
234
+
"integrity": "sha512-DG3YNaCKc6PAIv1Gsz3E1Kufw2t14OBxe4LdKK7KKLCNoex51hm+A5yMevShe3BSll+QosqWYIEgkPSc5xBoGQ==",
146
235
"dependencies": [
147
236
"@atproto-labs/fetch",
148
237
"@atproto-labs/pipe",
···
152
241
"zod"
153
242
]
154
243
},
155
-
"@atproto-labs/fetch-node@0.1.8": {
156
-
"integrity": "sha512-OOTIhZNPEDDm7kaYU8iYRgzM+D5n3mP2iiBSyKuLakKTaZBL5WwYlUsJVsqX26SnUXtGEroOJEVJ6f66OcG80w==",
244
+
"@atproto-labs/fetch-node@0.1.9": {
245
+
"integrity": "sha512-8sHDDXZEzQptLu8ddUU/8U+THS6dumgPynVX0/1PjUYd4S/FWyPcz6yMIiVChTfzKnZvYRRz47+qvOKhydrHQw==",
157
246
"dependencies": [
158
247
"@atproto-labs/fetch",
159
248
"@atproto-labs/pipe",
160
249
"ipaddr.js@2.2.0",
161
-
"psl",
162
250
"undici"
163
251
]
164
252
},
165
-
"@atproto-labs/fetch@0.2.2": {
166
-
"integrity": "sha512-QyafkedbFeVaN20DYUpnY2hcArYxjdThPXbYMqOSoZhcvkrUqaw4xDND4wZB5TBD9cq2yqe9V6mcw9P4XQKQuQ==",
253
+
"@atproto-labs/fetch@0.2.3": {
254
+
"integrity": "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==",
167
255
"dependencies": [
168
256
"@atproto-labs/pipe"
169
257
]
170
258
},
171
-
"@atproto-labs/handle-resolver-node@0.1.15": {
172
-
"integrity": "sha512-krl9KqfCCrGID35VAAHKBIiXOxe3gYxAtOJLYpZc5cOPFwnvPlAdhTYZLIc1dJRKDayi8gh6Q4XZRDv7i8dryg==",
259
+
"@atproto-labs/handle-resolver-node@0.1.16": {
260
+
"integrity": "sha512-i2F989zjyC7b/odrV3/tOpIT1IDIxR3F0khPG4REfOWcmJ89QcP8BiejJ6KFJk3hbTJHq6X9/pTG1vesCvyIKA==",
173
261
"dependencies": [
174
262
"@atproto-labs/fetch-node",
175
263
"@atproto-labs/handle-resolver",
···
185
273
"zod"
186
274
]
187
275
},
188
-
"@atproto-labs/identity-resolver@0.1.16": {
189
-
"integrity": "sha512-pFrtKT49cYBhCDd2U1t/CcUBiMmQzaNQxh8oSkDUlGs/K3P8rJFTAGAMm8UjokfGEKwF4hX9oo7O8Kn+GkyExw==",
276
+
"@atproto-labs/identity-resolver@0.1.17": {
277
+
"integrity": "sha512-EaH9Lm8M85IKRx+oWZ4tppYRVH8u+MYpEz1kjzYeM3ttZ2xcqKVmYHiOIgd5YPCVV2EIfXKnlM4soHQ+rZ1c6A==",
190
278
"dependencies": [
191
279
"@atproto-labs/did-resolver",
192
280
"@atproto-labs/handle-resolver",
193
281
"@atproto/syntax"
194
282
]
195
283
},
196
-
"@atproto-labs/pipe@0.1.0": {
197
-
"integrity": "sha512-ghOqHFyJlQVFPESzlVHjKroP0tPzbmG5Jms0dNI9yLDEfL8xp4OFPWLX4f6T8mRq69wWs4nIDM3sSsFbFqLa1w=="
284
+
"@atproto-labs/pipe@0.1.1": {
285
+
"integrity": "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg=="
198
286
},
199
287
"@atproto-labs/simple-store-memory@0.1.3": {
200
288
"integrity": "sha512-jkitT9+AtU+0b28DoN92iURLaCt/q/q4yX8q6V+9LSwYlUTqKoj/5NFKvF7x6EBuG+gpUdlcycbH7e60gjOhRQ==",
···
209
297
"@atproto-labs/simple-store@0.2.0": {
210
298
"integrity": "sha512-0bRbAlI8Ayh03wRwncAMEAyUKtZ+AuTS1jgPrfym1WVOAOiottI/ZmgccqLl6w5MbxVcClNQF7WYGKvGwGoIhA=="
211
299
},
212
-
"@atproto/api@0.15.7": {
213
-
"integrity": "sha512-YRETLcOwDCYfGs7Sl9ObqPwhOlVWrPkw4f1AYGIrXLQS58WHe/vz1lZbqOqMsC6gvCnyZnOuKlhsRHZ14rBLzg==",
300
+
"@atproto/api@0.15.12": {
301
+
"integrity": "sha512-51IHenZMA+Ekfe2OlZL/mTFqvZQU93jI4xsLvTFhGc4tSQYCHV9r/AJTANPZLFrhm9GfWZ0n90r/9IQl9eicjg==",
214
302
"dependencies": [
215
303
"@atproto/common-web",
216
304
"@atproto/lexicon",
···
270
358
"zod"
271
359
]
272
360
},
273
-
"@atproto/jwk@0.1.5": {
274
-
"integrity": "sha512-OzZFLhX41TOcMeanP3aZlL5bLeaUIZT15MI4aU5cwflNq/rwpGOpz3uwDjZc8ytgUjuTQ8LabSz5jMmwoTSWFg==",
361
+
"@atproto/jwk@0.2.0": {
362
+
"integrity": "sha512-foOxExbw04XCaoLaGdv9BQj0Ac7snZsk6IpQjOsjYatf+i62Pi9bUkZ0MAoA75HPk8ZmKoDnbA60uBMmiOPPHQ==",
275
363
"dependencies": [
276
364
"multiformats@9.9.0",
277
365
"zod"
278
366
]
279
367
},
280
-
"@atproto/lex-cli@0.8.1": {
281
-
"integrity": "sha512-0Ns6kX46gum2jU8bpvWCSVqoYhjmJrOGR/NLfLHgPbJtBlyxMGQAxqpy1x6zOi6SkkRGWYhHvRfr5J8lTHbxjA==",
368
+
"@atproto/lex-cli@0.8.2": {
369
+
"integrity": "sha512-yNQFYBV3tBBLnVrRUtUBlx/WIF4ypMFsvOsCLjA7pHL1SyW9JbczSEAoiNtoDmPc4UXCjMtXggz0ovBG8lynNA==",
282
370
"dependencies": [
283
371
"@atproto/lexicon",
284
372
"@atproto/syntax",
285
373
"chalk",
286
-
"commander",
374
+
"commander@9.5.0",
287
375
"prettier",
288
376
"ts-morph",
289
377
"yesno",
···
301
389
"zod"
302
390
]
303
391
},
304
-
"@atproto/oauth-client@0.3.16": {
305
-
"integrity": "sha512-AEtGLOXRJzBcBa8LyUXwFf/M7cZc+CcOBjLsiqmVQriSwccfyTkALgiyM0UcRHJqlwtLPuf9RYtgKPc8rW5F/w==",
392
+
"@atproto/oauth-client@0.3.20": {
393
+
"integrity": "sha512-aclxN2vD5ldc9YiQtX6z4S5g5lU12sz297gzuTxBFUYiS3bh7dxU8j/cbD/BDvXIiVRzzzc5kOgE1CgT9XZ2mg==",
306
394
"dependencies": [
307
395
"@atproto-labs/did-resolver",
308
396
"@atproto-labs/fetch",
···
311
399
"@atproto-labs/simple-store@0.2.0",
312
400
"@atproto-labs/simple-store-memory",
313
401
"@atproto/did",
314
-
"@atproto/jwk@0.1.5",
402
+
"@atproto/jwk@0.2.0",
315
403
"@atproto/oauth-types",
316
404
"@atproto/xrpc",
317
405
"multiformats@9.9.0",
318
406
"zod"
319
407
]
320
408
},
321
-
"@atproto/oauth-types@0.2.7": {
322
-
"integrity": "sha512-2SlDveiSI0oowC+sfuNd/npV8jw/FhokSS26qyUyldTg1g9ZlhxXUfMP4IZOPeZcVn9EszzQRHs1H9ZJqVQIew==",
409
+
"@atproto/oauth-types@0.2.8": {
410
+
"integrity": "sha512-xcYI2JmhrWwscePDoaKeDawVCCZkcvBqrBFMpMk4gf/OujH0pNSKBD/aWsayc6WvujVbTqwrG2hwPLfRqzJbwg==",
323
411
"dependencies": [
324
-
"@atproto/jwk@0.1.5",
412
+
"@atproto/jwk@0.2.0",
325
413
"zod"
326
414
]
327
415
},
328
416
"@atproto/syntax@0.4.0": {
329
417
"integrity": "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA=="
330
418
},
331
-
"@atproto/xrpc-server@0.7.18": {
332
-
"integrity": "sha512-kjlAsI+UNbbm6AK3Y5Hb4BJ7VQHNKiYYu2kX5vhZJZHO8qfO40GPYYb/2TknZV8IG6fDPBQhUpcDRolI86sgag==",
419
+
"@atproto/xrpc-server@0.7.19": {
420
+
"integrity": "sha512-YSCl/tU2NDykgDYslFSOYCr96esUgDwncFiADKL59/fyIFPLoT0qY8Uq/budpxUh0qPzjow4HHgVWESOaOpUmA==",
333
421
"dependencies": [
334
422
"@atproto/common",
335
423
"@atproto/crypto",
···
401
489
"tslib@2.8.1"
402
490
]
403
491
},
492
+
"@esbuild/aix-ppc64@0.25.5": {
493
+
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
494
+
"os": ["aix"],
495
+
"cpu": ["ppc64"]
496
+
},
497
+
"@esbuild/android-arm64@0.25.5": {
498
+
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
499
+
"os": ["android"],
500
+
"cpu": ["arm64"]
501
+
},
502
+
"@esbuild/android-arm@0.25.5": {
503
+
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
504
+
"os": ["android"],
505
+
"cpu": ["arm"]
506
+
},
507
+
"@esbuild/android-x64@0.25.5": {
508
+
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
509
+
"os": ["android"],
510
+
"cpu": ["x64"]
511
+
},
512
+
"@esbuild/darwin-arm64@0.25.5": {
513
+
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
514
+
"os": ["darwin"],
515
+
"cpu": ["arm64"]
516
+
},
517
+
"@esbuild/darwin-x64@0.25.5": {
518
+
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
519
+
"os": ["darwin"],
520
+
"cpu": ["x64"]
521
+
},
522
+
"@esbuild/freebsd-arm64@0.25.5": {
523
+
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
524
+
"os": ["freebsd"],
525
+
"cpu": ["arm64"]
526
+
},
527
+
"@esbuild/freebsd-x64@0.25.5": {
528
+
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
529
+
"os": ["freebsd"],
530
+
"cpu": ["x64"]
531
+
},
532
+
"@esbuild/linux-arm64@0.25.5": {
533
+
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
534
+
"os": ["linux"],
535
+
"cpu": ["arm64"]
536
+
},
537
+
"@esbuild/linux-arm@0.25.5": {
538
+
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
539
+
"os": ["linux"],
540
+
"cpu": ["arm"]
541
+
},
542
+
"@esbuild/linux-ia32@0.25.5": {
543
+
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
544
+
"os": ["linux"],
545
+
"cpu": ["ia32"]
546
+
},
547
+
"@esbuild/linux-loong64@0.25.5": {
548
+
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
549
+
"os": ["linux"],
550
+
"cpu": ["loong64"]
551
+
},
552
+
"@esbuild/linux-mips64el@0.25.5": {
553
+
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
554
+
"os": ["linux"],
555
+
"cpu": ["mips64el"]
556
+
},
557
+
"@esbuild/linux-ppc64@0.25.5": {
558
+
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
559
+
"os": ["linux"],
560
+
"cpu": ["ppc64"]
561
+
},
562
+
"@esbuild/linux-riscv64@0.25.5": {
563
+
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
564
+
"os": ["linux"],
565
+
"cpu": ["riscv64"]
566
+
},
567
+
"@esbuild/linux-s390x@0.25.5": {
568
+
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
569
+
"os": ["linux"],
570
+
"cpu": ["s390x"]
571
+
},
572
+
"@esbuild/linux-x64@0.25.5": {
573
+
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
574
+
"os": ["linux"],
575
+
"cpu": ["x64"]
576
+
},
577
+
"@esbuild/netbsd-arm64@0.25.5": {
578
+
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
579
+
"os": ["netbsd"],
580
+
"cpu": ["arm64"]
581
+
},
582
+
"@esbuild/netbsd-x64@0.25.5": {
583
+
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
584
+
"os": ["netbsd"],
585
+
"cpu": ["x64"]
586
+
},
587
+
"@esbuild/openbsd-arm64@0.25.5": {
588
+
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
589
+
"os": ["openbsd"],
590
+
"cpu": ["arm64"]
591
+
},
592
+
"@esbuild/openbsd-x64@0.25.5": {
593
+
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
594
+
"os": ["openbsd"],
595
+
"cpu": ["x64"]
596
+
},
597
+
"@esbuild/sunos-x64@0.25.5": {
598
+
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
599
+
"os": ["sunos"],
600
+
"cpu": ["x64"]
601
+
},
602
+
"@esbuild/win32-arm64@0.25.5": {
603
+
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
604
+
"os": ["win32"],
605
+
"cpu": ["arm64"]
606
+
},
607
+
"@esbuild/win32-ia32@0.25.5": {
608
+
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
609
+
"os": ["win32"],
610
+
"cpu": ["ia32"]
611
+
},
612
+
"@esbuild/win32-x64@0.25.5": {
613
+
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
614
+
"os": ["win32"],
615
+
"cpu": ["x64"]
616
+
},
617
+
"@img/sharp-darwin-arm64@0.34.1": {
618
+
"integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==",
619
+
"optionalDependencies": [
620
+
"@img/sharp-libvips-darwin-arm64"
621
+
],
622
+
"os": ["darwin"],
623
+
"cpu": ["arm64"]
624
+
},
625
+
"@img/sharp-darwin-x64@0.34.1": {
626
+
"integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==",
627
+
"optionalDependencies": [
628
+
"@img/sharp-libvips-darwin-x64"
629
+
],
630
+
"os": ["darwin"],
631
+
"cpu": ["x64"]
632
+
},
633
+
"@img/sharp-libvips-darwin-arm64@1.1.0": {
634
+
"integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==",
635
+
"os": ["darwin"],
636
+
"cpu": ["arm64"]
637
+
},
638
+
"@img/sharp-libvips-darwin-x64@1.1.0": {
639
+
"integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==",
640
+
"os": ["darwin"],
641
+
"cpu": ["x64"]
642
+
},
643
+
"@img/sharp-libvips-linux-arm64@1.1.0": {
644
+
"integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==",
645
+
"os": ["linux"],
646
+
"cpu": ["arm64"]
647
+
},
648
+
"@img/sharp-libvips-linux-arm@1.1.0": {
649
+
"integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==",
650
+
"os": ["linux"],
651
+
"cpu": ["arm"]
652
+
},
653
+
"@img/sharp-libvips-linux-ppc64@1.1.0": {
654
+
"integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==",
655
+
"os": ["linux"],
656
+
"cpu": ["ppc64"]
657
+
},
658
+
"@img/sharp-libvips-linux-s390x@1.1.0": {
659
+
"integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==",
660
+
"os": ["linux"],
661
+
"cpu": ["s390x"]
662
+
},
663
+
"@img/sharp-libvips-linux-x64@1.1.0": {
664
+
"integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==",
665
+
"os": ["linux"],
666
+
"cpu": ["x64"]
667
+
},
668
+
"@img/sharp-libvips-linuxmusl-arm64@1.1.0": {
669
+
"integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==",
670
+
"os": ["linux"],
671
+
"cpu": ["arm64"]
672
+
},
673
+
"@img/sharp-libvips-linuxmusl-x64@1.1.0": {
674
+
"integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==",
675
+
"os": ["linux"],
676
+
"cpu": ["x64"]
677
+
},
678
+
"@img/sharp-linux-arm64@0.34.1": {
679
+
"integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==",
680
+
"optionalDependencies": [
681
+
"@img/sharp-libvips-linux-arm64"
682
+
],
683
+
"os": ["linux"],
684
+
"cpu": ["arm64"]
685
+
},
686
+
"@img/sharp-linux-arm@0.34.1": {
687
+
"integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==",
688
+
"optionalDependencies": [
689
+
"@img/sharp-libvips-linux-arm"
690
+
],
691
+
"os": ["linux"],
692
+
"cpu": ["arm"]
693
+
},
694
+
"@img/sharp-linux-s390x@0.34.1": {
695
+
"integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==",
696
+
"optionalDependencies": [
697
+
"@img/sharp-libvips-linux-s390x"
698
+
],
699
+
"os": ["linux"],
700
+
"cpu": ["s390x"]
701
+
},
702
+
"@img/sharp-linux-x64@0.34.1": {
703
+
"integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==",
704
+
"optionalDependencies": [
705
+
"@img/sharp-libvips-linux-x64"
706
+
],
707
+
"os": ["linux"],
708
+
"cpu": ["x64"]
709
+
},
710
+
"@img/sharp-linuxmusl-arm64@0.34.1": {
711
+
"integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==",
712
+
"optionalDependencies": [
713
+
"@img/sharp-libvips-linuxmusl-arm64"
714
+
],
715
+
"os": ["linux"],
716
+
"cpu": ["arm64"]
717
+
},
718
+
"@img/sharp-linuxmusl-x64@0.34.1": {
719
+
"integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==",
720
+
"optionalDependencies": [
721
+
"@img/sharp-libvips-linuxmusl-x64"
722
+
],
723
+
"os": ["linux"],
724
+
"cpu": ["x64"]
725
+
},
726
+
"@img/sharp-wasm32@0.34.1": {
727
+
"integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==",
728
+
"dependencies": [
729
+
"@emnapi/runtime"
730
+
],
731
+
"cpu": ["wasm32"]
732
+
},
733
+
"@img/sharp-win32-ia32@0.34.1": {
734
+
"integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==",
735
+
"os": ["win32"],
736
+
"cpu": ["ia32"]
737
+
},
738
+
"@img/sharp-win32-x64@0.34.1": {
739
+
"integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==",
740
+
"os": ["win32"],
741
+
"cpu": ["x64"]
742
+
},
404
743
"@ipld/dag-cbor@7.0.3": {
405
744
"integrity": "sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA==",
406
745
"dependencies": [
···
428
767
"@jridgewell/set-array@1.2.1": {
429
768
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
430
769
},
770
+
"@jridgewell/source-map@0.3.6": {
771
+
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
772
+
"dependencies": [
773
+
"@jridgewell/gen-mapping",
774
+
"@jridgewell/trace-mapping"
775
+
]
776
+
},
431
777
"@jridgewell/sourcemap-codec@1.5.0": {
432
778
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
433
779
},
···
446
792
"@tybys/wasm-util"
447
793
]
448
794
},
449
-
"@noble/curves@1.9.1": {
450
-
"integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
795
+
"@noble/curves@1.9.2": {
796
+
"integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==",
451
797
"dependencies": [
452
798
"@noble/hashes"
453
799
]
···
545
891
],
546
892
"scripts": true
547
893
},
548
-
"@tailwindcss/cli@4.1.7": {
549
-
"integrity": "sha512-hJNjpov/UiJc9ZWH4j/eEQxqklADrD/71s+t8Y0wbyQVAwtLkSp+MeC/sHTb03X+28rfbe0fRXkiBsf73/IwPg==",
894
+
"@tailwindcss/cli@4.1.8": {
895
+
"integrity": "sha512-+6lkjXSr/68zWiabK3mVYVHmOq/SAHjJ13mR8spyB4LgUWZbWzU9kCSErlAUo+gK5aVfgqe8kY6Ltz9+nz5XYA==",
550
896
"dependencies": [
551
897
"@parcel/watcher",
552
898
"@tailwindcss/node",
···
558
904
],
559
905
"bin": true
560
906
},
561
-
"@tailwindcss/node@4.1.7": {
562
-
"integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==",
907
+
"@tailwindcss/node@4.1.8": {
908
+
"integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==",
563
909
"dependencies": [
564
910
"@ampproject/remapping",
565
911
"enhanced-resolve",
···
570
916
"tailwindcss"
571
917
]
572
918
},
573
-
"@tailwindcss/oxide-android-arm64@4.1.7": {
574
-
"integrity": "sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==",
919
+
"@tailwindcss/oxide-android-arm64@4.1.8": {
920
+
"integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==",
575
921
"os": ["android"],
576
922
"cpu": ["arm64"]
577
923
},
578
-
"@tailwindcss/oxide-darwin-arm64@4.1.7": {
579
-
"integrity": "sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==",
924
+
"@tailwindcss/oxide-darwin-arm64@4.1.8": {
925
+
"integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==",
580
926
"os": ["darwin"],
581
927
"cpu": ["arm64"]
582
928
},
583
-
"@tailwindcss/oxide-darwin-x64@4.1.7": {
584
-
"integrity": "sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==",
929
+
"@tailwindcss/oxide-darwin-x64@4.1.8": {
930
+
"integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==",
585
931
"os": ["darwin"],
586
932
"cpu": ["x64"]
587
933
},
588
-
"@tailwindcss/oxide-freebsd-x64@4.1.7": {
589
-
"integrity": "sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==",
934
+
"@tailwindcss/oxide-freebsd-x64@4.1.8": {
935
+
"integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==",
590
936
"os": ["freebsd"],
591
937
"cpu": ["x64"]
592
938
},
593
-
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7": {
594
-
"integrity": "sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==",
939
+
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8": {
940
+
"integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==",
595
941
"os": ["linux"],
596
942
"cpu": ["arm"]
597
943
},
598
-
"@tailwindcss/oxide-linux-arm64-gnu@4.1.7": {
599
-
"integrity": "sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==",
944
+
"@tailwindcss/oxide-linux-arm64-gnu@4.1.8": {
945
+
"integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==",
600
946
"os": ["linux"],
601
947
"cpu": ["arm64"]
602
948
},
603
-
"@tailwindcss/oxide-linux-arm64-musl@4.1.7": {
604
-
"integrity": "sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==",
949
+
"@tailwindcss/oxide-linux-arm64-musl@4.1.8": {
950
+
"integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==",
605
951
"os": ["linux"],
606
952
"cpu": ["arm64"]
607
953
},
608
-
"@tailwindcss/oxide-linux-x64-gnu@4.1.7": {
609
-
"integrity": "sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==",
954
+
"@tailwindcss/oxide-linux-x64-gnu@4.1.8": {
955
+
"integrity": "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==",
610
956
"os": ["linux"],
611
957
"cpu": ["x64"]
612
958
},
613
-
"@tailwindcss/oxide-linux-x64-musl@4.1.7": {
614
-
"integrity": "sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==",
959
+
"@tailwindcss/oxide-linux-x64-musl@4.1.8": {
960
+
"integrity": "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==",
615
961
"os": ["linux"],
616
962
"cpu": ["x64"]
617
963
},
618
-
"@tailwindcss/oxide-wasm32-wasi@4.1.7": {
619
-
"integrity": "sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==",
964
+
"@tailwindcss/oxide-wasm32-wasi@4.1.8": {
965
+
"integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==",
620
966
"dependencies": [
621
967
"@emnapi/core",
622
968
"@emnapi/runtime",
···
627
973
],
628
974
"cpu": ["wasm32"]
629
975
},
630
-
"@tailwindcss/oxide-win32-arm64-msvc@4.1.7": {
631
-
"integrity": "sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==",
976
+
"@tailwindcss/oxide-win32-arm64-msvc@4.1.8": {
977
+
"integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==",
632
978
"os": ["win32"],
633
979
"cpu": ["arm64"]
634
980
},
635
-
"@tailwindcss/oxide-win32-x64-msvc@4.1.7": {
636
-
"integrity": "sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==",
981
+
"@tailwindcss/oxide-win32-x64-msvc@4.1.8": {
982
+
"integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==",
637
983
"os": ["win32"],
638
984
"cpu": ["x64"]
639
985
},
640
-
"@tailwindcss/oxide@4.1.7": {
641
-
"integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==",
986
+
"@tailwindcss/oxide@4.1.8": {
987
+
"integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==",
642
988
"dependencies": [
643
989
"detect-libc@2.0.4",
644
990
"tar"
···
679
1025
"undici-types"
680
1026
]
681
1027
},
1028
+
"@xmldom/xmldom@0.9.8": {
1029
+
"integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A=="
1030
+
},
682
1031
"abort-controller@3.0.0": {
683
1032
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
684
1033
"dependencies": [
···
692
1041
"negotiator"
693
1042
]
694
1043
},
1044
+
"acorn@8.14.1": {
1045
+
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
1046
+
"bin": true
1047
+
},
695
1048
"ansi-styles@4.3.0": {
696
1049
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
697
1050
"dependencies": [
···
741
1094
"dependencies": [
742
1095
"fill-range"
743
1096
]
1097
+
},
1098
+
"buffer-from@1.1.2": {
1099
+
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
744
1100
},
745
1101
"buffer@6.0.3": {
746
1102
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
···
817
1173
"color-name@1.1.4": {
818
1174
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
819
1175
},
1176
+
"color-string@1.9.1": {
1177
+
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
1178
+
"dependencies": [
1179
+
"color-name",
1180
+
"simple-swizzle"
1181
+
]
1182
+
},
1183
+
"color@4.2.3": {
1184
+
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
1185
+
"dependencies": [
1186
+
"color-convert",
1187
+
"color-string"
1188
+
]
1189
+
},
1190
+
"commander@2.20.3": {
1191
+
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
1192
+
},
1193
+
"commander@8.3.0": {
1194
+
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
1195
+
},
820
1196
"commander@9.5.0": {
821
1197
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="
822
1198
},
···
844
1220
"ms@2.0.0"
845
1221
]
846
1222
},
1223
+
"deepmerge@4.3.1": {
1224
+
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
1225
+
},
847
1226
"depd@2.0.0": {
848
1227
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
849
1228
},
···
857
1236
"detect-libc@2.0.4": {
858
1237
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="
859
1238
},
1239
+
"dom-serializer@2.0.0": {
1240
+
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
1241
+
"dependencies": [
1242
+
"domelementtype",
1243
+
"domhandler",
1244
+
"entities"
1245
+
]
1246
+
},
1247
+
"domelementtype@2.3.0": {
1248
+
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
1249
+
},
1250
+
"domhandler@5.0.3": {
1251
+
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
1252
+
"dependencies": [
1253
+
"domelementtype"
1254
+
]
1255
+
},
1256
+
"domutils@3.2.2": {
1257
+
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
1258
+
"dependencies": [
1259
+
"dom-serializer",
1260
+
"domelementtype",
1261
+
"domhandler"
1262
+
]
1263
+
},
860
1264
"dunder-proto@1.0.1": {
861
1265
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
862
1266
"dependencies": [
···
881
1285
"tapable"
882
1286
]
883
1287
},
1288
+
"entities@4.5.0": {
1289
+
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
1290
+
},
884
1291
"es-define-property@1.0.1": {
885
1292
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
886
1293
},
···
893
1300
"es-errors"
894
1301
]
895
1302
},
1303
+
"esbuild@0.25.5": {
1304
+
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
1305
+
"optionalDependencies": [
1306
+
"@esbuild/aix-ppc64",
1307
+
"@esbuild/android-arm",
1308
+
"@esbuild/android-arm64",
1309
+
"@esbuild/android-x64",
1310
+
"@esbuild/darwin-arm64",
1311
+
"@esbuild/darwin-x64",
1312
+
"@esbuild/freebsd-arm64",
1313
+
"@esbuild/freebsd-x64",
1314
+
"@esbuild/linux-arm",
1315
+
"@esbuild/linux-arm64",
1316
+
"@esbuild/linux-ia32",
1317
+
"@esbuild/linux-loong64",
1318
+
"@esbuild/linux-mips64el",
1319
+
"@esbuild/linux-ppc64",
1320
+
"@esbuild/linux-riscv64",
1321
+
"@esbuild/linux-s390x",
1322
+
"@esbuild/linux-x64",
1323
+
"@esbuild/netbsd-arm64",
1324
+
"@esbuild/netbsd-x64",
1325
+
"@esbuild/openbsd-arm64",
1326
+
"@esbuild/openbsd-x64",
1327
+
"@esbuild/sunos-x64",
1328
+
"@esbuild/win32-arm64",
1329
+
"@esbuild/win32-ia32",
1330
+
"@esbuild/win32-x64"
1331
+
],
1332
+
"scripts": true,
1333
+
"bin": true
1334
+
},
896
1335
"escape-html@1.0.3": {
897
1336
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
898
1337
},
1338
+
"escape-string-regexp@4.0.0": {
1339
+
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
1340
+
},
899
1341
"etag@1.8.1": {
900
1342
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
901
1343
},
···
905
1347
"events@3.3.0": {
906
1348
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
907
1349
},
1350
+
"exif-esm@1.0.1": {
1351
+
"integrity": "sha512-IV4exsKAoDG7B1SpHXdfl76+8KQlq0P3H2BdCIu/KJIT2lu5sQExU6WvRJuw/LSiRHEvqxxNh2rMR1es1qS8mg=="
1352
+
},
1353
+
"exifr@7.1.3": {
1354
+
"integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw=="
1355
+
},
908
1356
"express@4.21.2": {
909
1357
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
910
1358
"dependencies": [
···
944
1392
"fast-redact@3.5.0": {
945
1393
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="
946
1394
},
947
-
"fdir@6.4.4_picomatch@4.0.2": {
948
-
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
1395
+
"fdir@6.4.5_picomatch@4.0.2": {
1396
+
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
949
1397
"dependencies": [
950
1398
"picomatch@4.0.2"
951
1399
],
···
1008
1456
"es-object-atoms"
1009
1457
]
1010
1458
},
1459
+
"github-slugger@2.0.0": {
1460
+
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
1461
+
},
1011
1462
"gopd@1.2.0": {
1012
1463
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
1013
1464
},
···
1029
1480
"function-bind"
1030
1481
]
1031
1482
},
1483
+
"he@1.2.0": {
1484
+
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
1485
+
"bin": true
1486
+
},
1032
1487
"hey-listen@1.0.8": {
1033
1488
"integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="
1034
1489
},
1490
+
"htmlparser2@8.0.2": {
1491
+
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
1492
+
"dependencies": [
1493
+
"domelementtype",
1494
+
"domhandler",
1495
+
"domutils",
1496
+
"entities"
1497
+
]
1498
+
},
1499
+
"htmx.org@1.9.12": {
1500
+
"integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw=="
1501
+
},
1035
1502
"http-errors@2.0.0": {
1036
1503
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
1037
1504
"dependencies": [
···
1042
1509
"toidentifier"
1043
1510
]
1044
1511
},
1512
+
"hyperscript.org@0.9.14": {
1513
+
"integrity": "sha512-ugmojsQQUMmXcnwaXYiYf8L3GbeANy/m59EmE/0Z6C5eQ52fOuSrvFkuEIejG9BdpbYB4iTtoYGqV99eYqDVMA==",
1514
+
"dependencies": [
1515
+
"markdown-it-deflist",
1516
+
"terser"
1517
+
],
1518
+
"bin": true
1519
+
},
1045
1520
"iconv-lite@0.4.24": {
1046
1521
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1047
1522
"dependencies": [
···
1060
1535
"ipaddr.js@2.2.0": {
1061
1536
"integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="
1062
1537
},
1538
+
"is-arrayish@0.3.2": {
1539
+
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
1540
+
},
1063
1541
"is-extglob@2.1.1": {
1064
1542
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
1065
1543
},
···
1072
1550
"is-number@7.0.0": {
1073
1551
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
1074
1552
},
1553
+
"is-plain-object@5.0.0": {
1554
+
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
1555
+
},
1075
1556
"iso-datestring-validator@2.2.2": {
1076
1557
"integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA=="
1077
1558
},
···
1081
1562
},
1082
1563
"jose@5.9.6": {
1083
1564
"integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="
1565
+
},
1566
+
"katex@0.16.22": {
1567
+
"integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==",
1568
+
"dependencies": [
1569
+
"commander@8.3.0"
1570
+
],
1571
+
"bin": true
1084
1572
},
1085
1573
"lightningcss-darwin-arm64@1.30.1": {
1086
1574
"integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
···
1159
1647
"@jridgewell/sourcemap-codec"
1160
1648
]
1161
1649
},
1650
+
"markdown-it-deflist@2.1.0": {
1651
+
"integrity": "sha512-3OuqoRUlSxJiuQYu0cWTLHNhhq2xtoSFqsZK8plANg91+RJQU1ziQ6lA2LzmFAEes18uPBsHZpcX6We5l76Nzg=="
1652
+
},
1653
+
"marked-alert@2.1.2_marked@12.0.2": {
1654
+
"integrity": "sha512-EFNRZ08d8L/iEIPLTlQMDjvwIsj03gxWCczYTht6DCiHJIZhMk4NK5gtPY9UqAYb09eV5VGT+jD4lp396E0I+w==",
1655
+
"dependencies": [
1656
+
"marked"
1657
+
]
1658
+
},
1659
+
"marked-footnote@1.2.4_marked@12.0.2": {
1660
+
"integrity": "sha512-DB2Kl+wFh6YwZd70qABMY6WUkG1UuyqoNTFoDfGyG79Pz24neYtLBkB+45a7o72V7gkfvbC3CGzIYFobxfMT1Q==",
1661
+
"dependencies": [
1662
+
"marked"
1663
+
]
1664
+
},
1665
+
"marked-gfm-heading-id@3.2.0_marked@12.0.2": {
1666
+
"integrity": "sha512-Xfxpr5lXLDLY10XqzSCA9l2dDaiabQUgtYM9hw8yunyVsB/xYBRpiic6BOiY/EAJw1ik1eWr1ET1HKOAPZBhXg==",
1667
+
"dependencies": [
1668
+
"github-slugger",
1669
+
"marked"
1670
+
]
1671
+
},
1672
+
"marked@12.0.2": {
1673
+
"integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==",
1674
+
"bin": true
1675
+
},
1162
1676
"math-intrinsics@1.1.0": {
1163
1677
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
1164
1678
},
···
1219
1733
"ms@2.1.3": {
1220
1734
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1221
1735
},
1222
-
"multiformats@13.3.4": {
1223
-
"integrity": "sha512-JXpM5p9TpJ/BHsUtmLaWuRN0ft0gJPGa6BhkX2KXjFHvkFQOQkDManoar3gx0JsTLNrOojBE2Mj4hFxohGnXZA=="
1736
+
"multiformats@13.3.6": {
1737
+
"integrity": "sha512-yakbt9cPYj8d3vi/8o/XWm61MrOILo7fsTL0qxNx6zS0Nso6K5JqqS2WV7vK/KSuDBvrW3KfCwAdAgarAgOmww=="
1224
1738
},
1225
1739
"multiformats@9.9.0": {
1226
1740
"integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="
1227
1741
},
1742
+
"nanoid@3.3.11": {
1743
+
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1744
+
"bin": true
1745
+
},
1228
1746
"negotiator@0.6.3": {
1229
1747
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
1230
1748
},
···
1249
1767
"dependencies": [
1250
1768
"ee-first"
1251
1769
]
1770
+
},
1771
+
"parse-srcset@1.0.2": {
1772
+
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
1252
1773
},
1253
1774
"parseurl@1.3.3": {
1254
1775
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
···
1304
1825
"tslib@2.4.0"
1305
1826
]
1306
1827
},
1307
-
"preact-render-to-string@6.5.13_preact@10.26.6": {
1828
+
"postcss@8.5.3": {
1829
+
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
1830
+
"dependencies": [
1831
+
"nanoid",
1832
+
"picocolors",
1833
+
"source-map-js"
1834
+
]
1835
+
},
1836
+
"preact-render-to-string@6.5.13_preact@10.26.8": {
1308
1837
"integrity": "sha512-iGPd+hKPMFKsfpR2vL4kJ6ZPcFIoWZEcBf0Dpm3zOpdVvj77aY8RlLiQji5OMrngEyaxGogeakTb54uS2FvA6w==",
1309
1838
"dependencies": [
1310
1839
"preact"
1311
1840
]
1312
1841
},
1313
-
"preact@10.26.6": {
1314
-
"integrity": "sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g=="
1842
+
"preact@10.26.8": {
1843
+
"integrity": "sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ=="
1315
1844
},
1316
1845
"prettier@3.5.3": {
1317
1846
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
1318
1847
"bin": true
1319
1848
},
1849
+
"prismjs@1.30.0": {
1850
+
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="
1851
+
},
1320
1852
"process-warning@3.0.0": {
1321
1853
"integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="
1322
1854
},
···
1330
1862
"ipaddr.js@1.9.1"
1331
1863
]
1332
1864
},
1333
-
"psl@1.15.0": {
1334
-
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
1335
-
"dependencies": [
1336
-
"punycode"
1337
-
]
1338
-
},
1339
-
"punycode@2.3.1": {
1340
-
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
1341
-
},
1342
1865
"qs@6.13.0": {
1343
1866
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
1344
1867
"dependencies": [
···
1385
1908
"safer-buffer@2.1.2": {
1386
1909
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1387
1910
},
1911
+
"sanitize-html@2.17.0": {
1912
+
"integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==",
1913
+
"dependencies": [
1914
+
"deepmerge",
1915
+
"escape-string-regexp",
1916
+
"htmlparser2",
1917
+
"is-plain-object",
1918
+
"parse-srcset",
1919
+
"postcss"
1920
+
]
1921
+
},
1922
+
"semver@7.7.2": {
1923
+
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
1924
+
"bin": true
1925
+
},
1388
1926
"send@0.19.0": {
1389
1927
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
1390
1928
"dependencies": [
···
1415
1953
"setprototypeof@1.2.0": {
1416
1954
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1417
1955
},
1956
+
"sharp@0.34.1": {
1957
+
"integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==",
1958
+
"dependencies": [
1959
+
"color",
1960
+
"detect-libc@2.0.4",
1961
+
"semver"
1962
+
],
1963
+
"optionalDependencies": [
1964
+
"@img/sharp-darwin-arm64",
1965
+
"@img/sharp-darwin-x64",
1966
+
"@img/sharp-libvips-darwin-arm64",
1967
+
"@img/sharp-libvips-darwin-x64",
1968
+
"@img/sharp-libvips-linux-arm",
1969
+
"@img/sharp-libvips-linux-arm64",
1970
+
"@img/sharp-libvips-linux-ppc64",
1971
+
"@img/sharp-libvips-linux-s390x",
1972
+
"@img/sharp-libvips-linux-x64",
1973
+
"@img/sharp-libvips-linuxmusl-arm64",
1974
+
"@img/sharp-libvips-linuxmusl-x64",
1975
+
"@img/sharp-linux-arm",
1976
+
"@img/sharp-linux-arm64",
1977
+
"@img/sharp-linux-s390x",
1978
+
"@img/sharp-linux-x64",
1979
+
"@img/sharp-linuxmusl-arm64",
1980
+
"@img/sharp-linuxmusl-x64",
1981
+
"@img/sharp-wasm32",
1982
+
"@img/sharp-win32-ia32",
1983
+
"@img/sharp-win32-x64"
1984
+
],
1985
+
"scripts": true
1986
+
},
1418
1987
"side-channel-list@1.0.0": {
1419
1988
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
1420
1989
"dependencies": [
···
1451
2020
"side-channel-weakmap"
1452
2021
]
1453
2022
},
2023
+
"simple-swizzle@0.2.2": {
2024
+
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
2025
+
"dependencies": [
2026
+
"is-arrayish"
2027
+
]
2028
+
},
1454
2029
"sonic-boom@3.8.1": {
1455
2030
"integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==",
1456
2031
"dependencies": [
1457
2032
"atomic-sleep"
1458
2033
]
1459
2034
},
2035
+
"sortablejs@1.15.6": {
2036
+
"integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A=="
2037
+
},
1460
2038
"source-map-js@1.2.1": {
1461
2039
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
1462
2040
},
2041
+
"source-map-support@0.5.21": {
2042
+
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
2043
+
"dependencies": [
2044
+
"buffer-from",
2045
+
"source-map"
2046
+
]
2047
+
},
2048
+
"source-map@0.6.1": {
2049
+
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
2050
+
},
1463
2051
"split2@4.2.0": {
1464
2052
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
1465
2053
},
···
1488
2076
"tailwind-merge@3.3.0": {
1489
2077
"integrity": "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ=="
1490
2078
},
1491
-
"tailwindcss@4.1.7": {
1492
-
"integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg=="
2079
+
"tailwindcss@4.1.8": {
2080
+
"integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og=="
1493
2081
},
1494
2082
"tapable@2.2.2": {
1495
2083
"integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="
···
1505
2093
"yallist"
1506
2094
]
1507
2095
},
2096
+
"terser@5.41.0": {
2097
+
"integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==",
2098
+
"dependencies": [
2099
+
"@jridgewell/source-map",
2100
+
"acorn",
2101
+
"commander@2.20.3",
2102
+
"source-map-support"
2103
+
],
2104
+
"bin": true
2105
+
},
1508
2106
"thread-stream@2.7.0": {
1509
2107
"integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==",
1510
2108
"dependencies": [
1511
2109
"real-require"
1512
2110
]
1513
2111
},
1514
-
"tinyglobby@0.2.13_picomatch@4.0.2": {
1515
-
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
2112
+
"tinyglobby@0.2.14_picomatch@4.0.2": {
2113
+
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
1516
2114
"dependencies": [
1517
2115
"fdir",
1518
2116
"picomatch@4.0.2"
···
1590
2188
"yesno@0.4.0": {
1591
2189
"integrity": "sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA=="
1592
2190
},
1593
-
"zod@3.25.7": {
1594
-
"integrity": "sha512-YGdT1cVRmKkOg6Sq7vY7IkxdphySKnXhaUmFI4r4FcuFVNgpCb9tZfNwXbT6BPjD5oz0nubFsoo9pIqKrDcCvg=="
2191
+
"zod@3.25.51": {
2192
+
"integrity": "sha512-TQSnBldh+XSGL+opiSIq0575wvDPqu09AqWe1F7JhUMKY+M91/aGlK4MhpVNO7MgYfHcVCB1ffwAUTJzllKJqg=="
1595
2193
}
1596
2194
},
1597
2195
"workspace": {
1598
2196
"dependencies": [
1599
-
"jsr:@bigmoves/bff@0.3.0-beta.29",
2197
+
"jsr:@bigmoves/bff@0.3.0-beta.32",
2198
+
"jsr:@luca/esbuild-deno-loader@~0.11.1",
1600
2199
"jsr:@std/http@^1.0.17",
1601
2200
"jsr:@std/path@^1.0.9",
1602
2201
"npm:@atproto/syntax@0.4",
1603
2202
"npm:@tailwindcss/cli@^4.1.4",
1604
2203
"npm:date-fns@^4.1.0",
2204
+
"npm:esbuild@~0.25.5",
2205
+
"npm:exifr@^7.1.3",
2206
+
"npm:htmx.org@^1.9.12",
2207
+
"npm:hyperscript.org@~0.9.14",
1605
2208
"npm:popmotion@^11.0.5",
1606
2209
"npm:preact@^10.26.5",
2210
+
"npm:sortablejs@^1.15.6",
1607
2211
"npm:tailwindcss@^4.1.4",
1608
2212
"npm:typed-htmx@~0.3.1"
1609
2213
]
+1
fly.toml
+1
fly.toml
+71
lexicons/sh/tangled/actor/profile.json
+71
lexicons/sh/tangled/actor/profile.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "sh.tangled.actor.profile",
4
+
"defs": {
5
+
"main": {
6
+
"type": "record",
7
+
"description": "A declaration of a Tangled account profile.",
8
+
"key": "literal:self",
9
+
"record": {
10
+
"type": "object",
11
+
"required": [
12
+
"bluesky"
13
+
],
14
+
"properties": {
15
+
"description": {
16
+
"type": "string",
17
+
"description": "Free-form profile description text.",
18
+
"maxGraphemes": 256,
19
+
"maxLength": 2560
20
+
},
21
+
"links": {
22
+
"type": "array",
23
+
"minLength": 0,
24
+
"maxLength": 5,
25
+
"items": {
26
+
"type": "string",
27
+
"description": "Any URI, intended for social profiles or websites, can be used to link DIDs/AT-URIs too."
28
+
}
29
+
},
30
+
"stats": {
31
+
"type": "array",
32
+
"minLength": 0,
33
+
"maxLength": 2,
34
+
"items": {
35
+
"type": "string",
36
+
"description": "Vanity stats.",
37
+
"enum": [
38
+
"merged-pull-request-count",
39
+
"closed-pull-request-count",
40
+
"open-pull-request-count",
41
+
"open-issue-count",
42
+
"closed-issue-count",
43
+
"repository-count"
44
+
]
45
+
}
46
+
},
47
+
"bluesky": {
48
+
"type": "boolean",
49
+
"description": "Include link to this account on Bluesky."
50
+
},
51
+
"location": {
52
+
"type": "string",
53
+
"description": "Free-form location text.",
54
+
"maxGraphemes": 40,
55
+
"maxLength": 400
56
+
},
57
+
"pinnedRepositories": {
58
+
"type": "array",
59
+
"description": "Any ATURI, it is up to appviews to validate these fields.",
60
+
"minLength": 0,
61
+
"maxLength": 6,
62
+
"items": {
63
+
"type": "string",
64
+
"format": "at-uri"
65
+
}
66
+
}
67
+
}
68
+
}
69
+
}
70
+
}
71
+
}
+27
lexicons/sh/tangled/graph/follow.json
+27
lexicons/sh/tangled/graph/follow.json
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "sh.tangled.graph.follow",
4
+
"defs": {
5
+
"main": {
6
+
"type": "record",
7
+
"key": "tid",
8
+
"record": {
9
+
"type": "object",
10
+
"required": [
11
+
"subject",
12
+
"createdAt"
13
+
],
14
+
"properties": {
15
+
"subject": {
16
+
"type": "string",
17
+
"format": "did"
18
+
},
19
+
"createdAt": {
20
+
"type": "string",
21
+
"format": "datetime"
22
+
}
23
+
}
24
+
}
25
+
}
26
+
}
27
+
}
+12
-1
local-infra/pds.env
+12
-1
local-infra/pds.env
···
11
11
PDS_DID_PLC_URL=http://plc:8080
12
12
PDS_HOSTNAME=pds.dev.grain.social
13
13
PDS_EMAIL_SMTP_URL=smtp://maildev:1025
14
-
PDS_EMAIL_FROM_ADDRESS=admin@grain.social
14
+
PDS_EMAIL_FROM_ADDRESS=support@grain.social
15
15
PDS_SERVICE_NAME=Grain Social
16
16
PDS_INVITE_REQUIRED=0
17
+
PDS_OAUTH_PROVIDER_NAME=Grain Social
18
+
PDS_HOME_URL=http://localhost:8080
19
+
PDS_LIGHT_COLOR=#fff
20
+
PDS_DARK_COLOR=#09090b
21
+
PDS_PRIMARY_COLOR=#00a6f4
22
+
PDS_PRIMARY_COLOR_CONTRAST=#fff
23
+
PDS_PRIMARY_COLOR_HUE=#fff
24
+
PDS_TERMS_OF_SERVICE_URL = 'http://localhost:8080/support/terms'
25
+
PDS_PRIVACY_POLICY_URL = 'http://localhost:8080/support/privacy'
26
+
PDS_SUPPORT_URL= 'http://localhost:8080/support'
27
+
PDS_CONTACT_EMAIL_ADDRESS = 'support@grain.social'
+23
services/nginx/fly.toml
+23
services/nginx/fly.toml
···
1
+
# fly.toml app configuration file generated for grain-nginx on 2025-06-01T22:28:51-07:00
2
+
#
3
+
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
4
+
#
5
+
6
+
app = 'grain-nginx'
7
+
primary_region = 'sea'
8
+
9
+
[build]
10
+
dockerfile = './Dockerfile'
11
+
12
+
[http_service]
13
+
internal_port = 80
14
+
force_https = true
15
+
auto_stop_machines = 'stop'
16
+
auto_start_machines = true
17
+
min_machines_running = 0
18
+
processes = ['app']
19
+
20
+
[[vm]]
21
+
memory = '256mb'
22
+
cpu_kind = 'shared'
23
+
cpus = 1
+46
services/nginx/nginx.conf
+46
services/nginx/nginx.conf
···
1
+
worker_processes 1;
2
+
3
+
events {
4
+
worker_connections 1024;
5
+
}
6
+
7
+
http {
8
+
resolver [fdaa::3];
9
+
client_max_body_size 50M;
10
+
11
+
map $http_host $pds {
12
+
default http://grain-pds.internal:3000;
13
+
}
14
+
15
+
map $http_host $appview {
16
+
default http://atphoto.internal:8080;
17
+
}
18
+
19
+
map $http_upgrade $connection_upgrade {
20
+
default upgrade;
21
+
'' close;
22
+
}
23
+
24
+
server {
25
+
listen 80;
26
+
server_name *.grain.social;
27
+
28
+
location /xrpc {
29
+
proxy_pass $pds;
30
+
proxy_set_header Host $host;
31
+
32
+
proxy_set_header Upgrade $http_upgrade;
33
+
proxy_set_header Connection $connection_upgrade;
34
+
}
35
+
36
+
location = /.well-known/atproto-did {
37
+
proxy_pass $pds/.well-known/atproto-did;
38
+
proxy_set_header Host $host;
39
+
}
40
+
41
+
location / {
42
+
proxy_pass $appview;
43
+
proxy_set_header Host $host;
44
+
}
45
+
}
46
+
}
+25
services/pds/.env.example
+25
services/pds/.env.example
···
1
+
# You do not necessarily need to fill this file out, this is just for reference
2
+
# If you do want to use the official pdsadmin tool, then you should fill this out
3
+
4
+
# public
5
+
PDS_HOSTNAME=pds.example.com
6
+
PDS_DATA_DIRECTORY=/pds
7
+
PDS_BLOBSTORE_DISK_LOCATION=/pds/blocks
8
+
PDS_BLOB_UPLOAD_LIMIT=52428800
9
+
PDS_DID_PLC_URL=https://plc.directory
10
+
PDS_BSKY_APP_VIEW_URL=https://api.bsky.app
11
+
PDS_BSKY_APP_VIEW_DID=did:web:api.bsky.app
12
+
PDS_REPORT_SERVICE_URL=https://mod.bsky.app
13
+
PDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac
14
+
PDS_CRAWLERS=https://bsky.network
15
+
LOG_ENABLED=true
16
+
PDS_SERVICE_HANDLE_DOMAINS=.example.com
17
+
18
+
# private
19
+
PDS_JWT_SECRET=<secret>
20
+
PDS_ADMIN_PASSWORD=<secret>
21
+
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=<secret>
22
+
23
+
# private: email
24
+
PDS_EMAIL_SMTP_URL=smtps://resend:<secret api key here>@smtp.resend.com:465/
25
+
PDS_EMAIL_FROM_ADDRESS=support@your.domain
+65
services/pds/README.md
+65
services/pds/README.md
···
1
+
# Setting up a PDS
2
+
3
+
1. Customizing _fly.toml_
4
+
- You should replace values `app`, `primary_region`, `env.PDS_HOSTNAME` to
5
+
values that will make sense for your installation.
6
+
- `app` controls the name of the project on fly.io
7
+
- `primary_region` controls where the app will be deployed globally, `iad`
8
+
is in Northern Virginia (USA)
9
+
- `[env]`, `PDS_HOSTNAME` should make the URL from where you plan to reach
10
+
the application, so for example, if you're planning to add a DNS entry to
11
+
reach your PDS from `my-pds.my-site.com`, then, use that as the value
12
+
here
13
+
2. Generate the necessary secret values for your PDS
14
+
> 🚧 All of these values are super secret, do not share them!
15
+
>
16
+
> Make sure you have them written down somewhere because fly.io will never
17
+
> let you see them again
18
+
1. _PDS_JWT_SECRET_: `openssl rand --hex 16`
19
+
2. _PDS_ADMIN_PASSWORD_: `openssl rand --hex 16`
20
+
3. _PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX_:
21
+
`openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32`
22
+
3. Create the project in fly.io
23
+
1. Run `fly launch --no-deploy`, this will create the project on fly without
24
+
deploying it. You need to make some changes ahead of an initial deployment
25
+
2. Create the volume that you specified earlier, make sure to choose the
26
+
primary_region as the region for your volume `fly volume create pdsdata`
27
+
3. Apply the secrets you generated earlier
28
+
`fly secrets set PDS_JWT_SECRET=secret PDS_ADMIN_PASSWORD=secret PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=secret`
29
+
4. Deploy the app using `fly deploy`
30
+
> 🚧 This should create only one machine, make sure using `fly m ls`
31
+
>
32
+
> If you have more than one machine scale down using `fly scale count 1`
33
+
5. Test your PDS: You can do this quickly by visitng
34
+
`https://<your-app-name>.fly.dev/xrpc/com.atproto.sync.listRepos`, at this
35
+
point you should see a response like this:
36
+
```json
37
+
{ "repos": [] }
38
+
```
39
+
4. Setup your DNS
40
+
41
+
- You need to create an entry for your PDS's hostname in the DNS console you use
42
+
for your domain name: `pds.example.com`
43
+
44
+
> 🚧 You need to create an entry that allows you to map handles to the pds
45
+
>
46
+
> The handle `username.pds.example.com` needs be able to resolve, so your PDS
47
+
> should also be available at `username.pds.example.com`. If you don't do this,
48
+
> other atproto services can't resolve the handle and you get `Invalid Handle`
49
+
> everywhere you go
50
+
51
+
- Now you should be able to reach your PDS at
52
+
`https://pds.example.com/xrpc/com.atproto.sync.listRepos`
53
+
54
+
5. Bonus, Setting up emails: Blue Sky will ask you to verify your email, but,
55
+
without having a mail service setup, you'll never be able to get the
56
+
confirmation code! Follow the official PDS guide on setting up email
57
+
services, it covers the topic fully:
58
+
[link](https://github.com/bluesky-social/pds?tab=readme-ov-file#setting-up-smtp)
59
+
> 🚧 Remember: You can add secrets to your fly service using
60
+
>
61
+
> `fly secrets set KEY1=VALUE1 KEY2=VALE2 ...`
62
+
63
+
# Credits
64
+
65
+
[keaysma](https://github.com/keaysma/pds-fly.io-template)
+234
services/pds/account.sh
+234
services/pds/account.sh
···
1
+
#!/bin/bash
2
+
set -o errexit
3
+
set -o nounset
4
+
set -o pipefail
5
+
6
+
PDS_ENV_FILE=${PDS_ENV_FILE:-".env"}
7
+
source "${PDS_ENV_FILE}"
8
+
9
+
# curl a URL and fail if the request fails.
10
+
function curl_cmd_get {
11
+
curl --fail --silent --show-error "$@"
12
+
}
13
+
14
+
# curl a URL and fail if the request fails.
15
+
function curl_cmd_post {
16
+
curl --fail --silent --show-error --request POST --header "Content-Type: application/json" "$@"
17
+
}
18
+
19
+
# curl a URL but do not fail if the request fails.
20
+
function curl_cmd_post_nofail {
21
+
curl --silent --show-error --request POST --header "Content-Type: application/json" "$@"
22
+
}
23
+
24
+
# The subcommand to run.
25
+
SUBCOMMAND="${1:-}"
26
+
27
+
#
28
+
# account list
29
+
#
30
+
if [[ "${SUBCOMMAND}" == "list" ]]; then
31
+
DIDS="$(curl_cmd_get \
32
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.sync.listRepos?limit=100" | jq --raw-output '.repos[].did'
33
+
)"
34
+
OUTPUT='[{"handle":"Handle","email":"Email","did":"DID"}'
35
+
for did in ${DIDS}; do
36
+
ITEM="$(curl_cmd_get \
37
+
--user "admin:${PDS_ADMIN_PASSWORD}" \
38
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.getAccountInfo?did=${did}"
39
+
)"
40
+
OUTPUT="${OUTPUT},${ITEM}"
41
+
done
42
+
OUTPUT="${OUTPUT}]"
43
+
echo "${OUTPUT}" | jq --raw-output '.[] | [.handle, .email, .did] | @tsv' | column -t
44
+
45
+
#
46
+
# account create
47
+
#
48
+
elif [[ "${SUBCOMMAND}" == "create" ]]; then
49
+
EMAIL="${2:-}"
50
+
HANDLE="${3:-}"
51
+
52
+
if [[ "${EMAIL}" == "" ]]; then
53
+
read -p "Enter an email address (e.g. alice@${PDS_HOSTNAME}): " EMAIL
54
+
fi
55
+
if [[ "${HANDLE}" == "" ]]; then
56
+
read -p "Enter a handle (e.g. alice.${PDS_HOSTNAME}): " HANDLE
57
+
fi
58
+
59
+
if [[ "${EMAIL}" == "" || "${HANDLE}" == "" ]]; then
60
+
echo "ERROR: missing EMAIL and/or HANDLE parameters." >/dev/stderr
61
+
echo "Usage: $0 ${SUBCOMMAND} <EMAIL> <HANDLE>" >/dev/stderr
62
+
exit 1
63
+
fi
64
+
65
+
PASSWORD="$(openssl rand -base64 30 | tr -d "=+/" | cut -c1-24)"
66
+
INVITE_CODE="$(curl_cmd_post \
67
+
--user "admin:${PDS_ADMIN_PASSWORD}" \
68
+
--data '{"useCount": 1}' \
69
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createInviteCode" | jq --raw-output '.code'
70
+
)"
71
+
RESULT="$(curl_cmd_post_nofail \
72
+
--data "{\"email\":\"${EMAIL}\", \"handle\":\"${HANDLE}\", \"password\":\"${PASSWORD}\", \"inviteCode\":\"${INVITE_CODE}\"}" \
73
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createAccount"
74
+
)"
75
+
76
+
DID="$(echo $RESULT | jq --raw-output '.did')"
77
+
if [[ "${DID}" != did:* ]]; then
78
+
ERR="$(echo ${RESULT} | jq --raw-output '.message')"
79
+
echo "ERROR: ${ERR}" >/dev/stderr
80
+
echo "Usage: $0 ${SUBCOMMAND} <EMAIL> <HANDLE>" >/dev/stderr
81
+
exit 1
82
+
fi
83
+
84
+
echo
85
+
echo "Account created successfully!"
86
+
echo "-----------------------------"
87
+
echo "Handle : ${HANDLE}"
88
+
echo "DID : ${DID}"
89
+
echo "Password : ${PASSWORD}"
90
+
echo "-----------------------------"
91
+
echo "Save this password, it will not be displayed again."
92
+
echo
93
+
94
+
#
95
+
# account delete
96
+
#
97
+
elif [[ "${SUBCOMMAND}" == "delete" ]]; then
98
+
DID="${2:-}"
99
+
100
+
if [[ "${DID}" == "" ]]; then
101
+
echo "ERROR: missing DID parameter." >/dev/stderr
102
+
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
103
+
exit 1
104
+
fi
105
+
106
+
if [[ "${DID}" != did:* ]]; then
107
+
echo "ERROR: DID parameter must start with \"did:\"." >/dev/stderr
108
+
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
109
+
exit 1
110
+
fi
111
+
112
+
echo "This action is permanent."
113
+
read -r -p "Are you sure you'd like to delete ${DID}? [y/N] " response
114
+
if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
115
+
exit 0
116
+
fi
117
+
118
+
curl_cmd_post \
119
+
--user "admin:${PDS_ADMIN_PASSWORD}" \
120
+
--data "{\"did\": \"${DID}\"}" \
121
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.deleteAccount" >/dev/null
122
+
123
+
echo "${DID} deleted"
124
+
125
+
#
126
+
# account takedown
127
+
#
128
+
elif [[ "${SUBCOMMAND}" == "takedown" ]]; then
129
+
DID="${2:-}"
130
+
TAKEDOWN_REF="$(date +%s)"
131
+
132
+
if [[ "${DID}" == "" ]]; then
133
+
echo "ERROR: missing DID parameter." >/dev/stderr
134
+
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
135
+
exit 1
136
+
fi
137
+
138
+
if [[ "${DID}" != did:* ]]; then
139
+
echo "ERROR: DID parameter must start with \"did:\"." >/dev/stderr
140
+
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
141
+
exit 1
142
+
fi
143
+
144
+
PAYLOAD="$(cat <<EOF
145
+
{
146
+
"subject": {
147
+
"\$type": "com.atproto.admin.defs#repoRef",
148
+
"did": "${DID}"
149
+
},
150
+
"takedown": {
151
+
"applied": true,
152
+
"ref": "${TAKEDOWN_REF}"
153
+
}
154
+
}
155
+
EOF
156
+
)"
157
+
158
+
curl_cmd_post \
159
+
--user "admin:${PDS_ADMIN_PASSWORD}" \
160
+
--data "${PAYLOAD}" \
161
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.updateSubjectStatus" >/dev/null
162
+
163
+
echo "${DID} taken down"
164
+
165
+
#
166
+
# account untakedown
167
+
#
168
+
elif [[ "${SUBCOMMAND}" == "untakedown" ]]; then
169
+
DID="${2:-}"
170
+
171
+
if [[ "${DID}" == "" ]]; then
172
+
echo "ERROR: missing DID parameter." >/dev/stderr
173
+
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
174
+
exit 1
175
+
fi
176
+
177
+
if [[ "${DID}" != did:* ]]; then
178
+
echo "ERROR: DID parameter must start with \"did:\"." >/dev/stderr
179
+
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
180
+
exit 1
181
+
fi
182
+
183
+
PAYLOAD=$(cat <<EOF
184
+
{
185
+
"subject": {
186
+
"\$type": "com.atproto.admin.defs#repoRef",
187
+
"did": "${DID}"
188
+
},
189
+
"takedown": {
190
+
"applied": false
191
+
}
192
+
}
193
+
EOF
194
+
)
195
+
196
+
curl_cmd_post \
197
+
--user "admin:${PDS_ADMIN_PASSWORD}" \
198
+
--data "${PAYLOAD}" \
199
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.updateSubjectStatus" >/dev/null
200
+
201
+
echo "${DID} untaken down"
202
+
#
203
+
# account reset-password
204
+
#
205
+
elif [[ "${SUBCOMMAND}" == "reset-password" ]]; then
206
+
DID="${2:-}"
207
+
PASSWORD="$(openssl rand -base64 30 | tr -d "=+/" | cut -c1-24)"
208
+
209
+
if [[ "${DID}" == "" ]]; then
210
+
echo "ERROR: missing DID parameter." >/dev/stderr
211
+
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
212
+
exit 1
213
+
fi
214
+
215
+
if [[ "${DID}" != did:* ]]; then
216
+
echo "ERROR: DID parameter must start with \"did:\"." >/dev/stderr
217
+
echo "Usage: $0 ${SUBCOMMAND} <DID>" >/dev/stderr
218
+
exit 1
219
+
fi
220
+
221
+
curl_cmd_post \
222
+
--user "admin:${PDS_ADMIN_PASSWORD}" \
223
+
--data "{ \"did\": \"${DID}\", \"password\": \"${PASSWORD}\" }" \
224
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.admin.updateAccountPassword" >/dev/null
225
+
226
+
echo
227
+
echo "Password reset for ${DID}"
228
+
echo "New password: ${PASSWORD}"
229
+
echo
230
+
231
+
else
232
+
echo "Unknown subcommand: ${SUBCOMMAND}" >/dev/stderr
233
+
exit 1
234
+
fi
+56
services/pds/fly.toml
+56
services/pds/fly.toml
···
1
+
# fly.toml app configuration file generated for grain-pds on 2025-06-01T21:15:20-07:00
2
+
#
3
+
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
4
+
#
5
+
6
+
app = 'grain-pds'
7
+
primary_region = 'sea'
8
+
9
+
[build]
10
+
image = 'ghcr.io/bluesky-social/pds:0.4'
11
+
12
+
[env]
13
+
LOG_ENABLED = 'true'
14
+
PDS_BLOBSTORE_DISK_LOCATION = '/pds/blocks'
15
+
PDS_BLOB_UPLOAD_LIMIT = '52428800'
16
+
PDS_BSKY_APP_VIEW_DID = 'did:web:api.bsky.app'
17
+
PDS_BSKY_APP_VIEW_URL = 'https://api.bsky.app'
18
+
PDS_CRAWLERS = 'https://bsky.network'
19
+
PDS_DATA_DIRECTORY = '/pds'
20
+
PDS_DID_PLC_URL = 'https://plc.directory'
21
+
PDS_HOSTNAME = 'ansel.grainsocial.network'
22
+
PDS_REPORT_SERVICE_DID = 'did:plc:ar7c4by46qjdydhdevvrndac'
23
+
PDS_REPORT_SERVICE_URL = 'https://mod.bsky.app'
24
+
PDS_SERVICE_HANDLE_DOMAINS = '.grain.social'
25
+
PDS_SERVICE_NAME = 'Grain Social'
26
+
PDS_HOME_URL = 'https://grain.social'
27
+
PDS_LIGHT_COLOR = '#fff'
28
+
PDS_DARK_COLOR = '#09090b'
29
+
PDS_PRIMARY_COLOR = '#00a6f4'
30
+
PDS_PRIMARY_COLOR_CONTRAST = '#fff'
31
+
PDS_PRIMARY_COLOR_HUE = '#fff'
32
+
PDS_TERMS_OF_SERVICE_URL = 'https://grain.social/support/terms'
33
+
PDS_PRIVACY_POLICY_URL = 'https://grain.social/support/privacy'
34
+
PDS_SUPPORT_URL= 'https://grain.social/support'
35
+
PDS_CONTACT_EMAIL_ADDRESS = 'support@grain.social'
36
+
37
+
[[mounts]]
38
+
source = 'pdsdata'
39
+
destination = '/pds'
40
+
41
+
[[services]]
42
+
protocol = 'tcp'
43
+
internal_port = 3000
44
+
auto_stop_machines = 'off'
45
+
auto_start_machines = true
46
+
min_machines_running = 1
47
+
processes = ['app']
48
+
49
+
[[services.ports]]
50
+
port = 443
51
+
handlers = ['tls', 'http']
52
+
53
+
[[vm]]
54
+
memory = '512mb'
55
+
cpu_kind = 'shared'
56
+
cpus = 1
+35
services/pds/pdsadmin-command.sh
+35
services/pds/pdsadmin-command.sh
···
1
+
# pdsadmin is a tool for managing the Personal Data Store (PDS) server.
2
+
# But at the end of the day, it's just a bash script that makes curl requests
3
+
# Even worse, it does all sorts of annoying checks that don't apply to OSX
4
+
# So I have reversed engineered the requests I cared about and put them here
5
+
6
+
# You can copy and paste these into your terminal,
7
+
# Remove the underscores before the curl command
8
+
# Replace the variables with your own values
9
+
10
+
PDS_ENV_FILE=${PDS_ENV_FILE:-".env"}
11
+
source "${PDS_ENV_FILE}"
12
+
13
+
export DID=""
14
+
15
+
# make an invite code
16
+
curl \
17
+
--fail \
18
+
--silent \
19
+
--show-error \
20
+
--request POST \
21
+
--header "Content-Type: application/json" \
22
+
--user "admin:${PDS_ADMIN_PASSWORD}" \
23
+
--data '{"useCount": 20}' \
24
+
"https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createInviteCode"
25
+
26
+
# delete an account
27
+
# curl \
28
+
# --fail \
29
+
# --silent \
30
+
# --show-error \
31
+
# --request POST \
32
+
# --header "Content-Type: application/json" \
33
+
# --user "admin:${PDS_ADMIN_PASSWORD}" \
34
+
# --data "{\"did\": \"${DID}\"}" \
35
+
# "https://${PDS_HOST}/xrpc/com.atproto.admin.deleteAccount"
+35
services/pds/request-crawl.sh
+35
services/pds/request-crawl.sh
···
1
+
#!/bin/bash
2
+
set -o errexit
3
+
set -o nounset
4
+
set -o pipefail
5
+
6
+
PDS_ENV_FILE=${PDS_ENV_FILE:-".env"}
7
+
source "${PDS_ENV_FILE}"
8
+
9
+
RELAY_HOSTS="${1:-}"
10
+
if [[ "${RELAY_HOSTS}" == "" ]]; then
11
+
RELAY_HOSTS="${PDS_CRAWLERS}"
12
+
fi
13
+
14
+
if [[ "${RELAY_HOSTS}" == "" ]]; then
15
+
echo "ERROR: missing RELAY HOST parameter." >/dev/stderr
16
+
echo "Usage: $0 <RELAY HOST>[,<RELAY HOST>,...]" >/dev/stderr
17
+
exit 1
18
+
fi
19
+
20
+
for host in ${RELAY_HOSTS//,/ }; do
21
+
echo "Requesting crawl from ${host}"
22
+
if [[ $host != https:* && $host != http:* ]]; then
23
+
host="https://${host}"
24
+
fi
25
+
curl \
26
+
--fail \
27
+
--silent \
28
+
--show-error \
29
+
--request POST \
30
+
--header "Content-Type: application/json" \
31
+
--data "{\"hostname\": \"${PDS_HOSTNAME}\"}" \
32
+
"${host}/xrpc/com.atproto.sync.requestCrawl" >/dev/null
33
+
done
34
+
35
+
echo "done"
+5
-11
src/app.tsx
+5
-11
src/app.tsx
···
6
6
7
7
export function Root(props: Readonly<RootProps<State>>) {
8
8
const profile = props.ctx.state.profile;
9
-
const scripts = props.ctx.state.scripts;
10
9
const hasNotifications =
11
10
props.ctx.state.notifications?.find((n) => n.isRead === false) !==
12
11
undefined;
···
26
25
/>
27
26
)
28
27
: null}
29
-
<script src="https://unpkg.com/htmx.org@1.9.10" />
30
-
<script src="https://unpkg.com/hyperscript.org@0.9.14" />
31
-
<script src="https://unpkg.com/sortablejs@1.15.6" />
32
-
<script src="https://cdn.jsdelivr.net/npm/exifr/dist/lite.umd.js" />
33
28
<style dangerouslySetInnerHTML={{ __html: CSS }} />
34
29
<link
35
30
rel="stylesheet"
···
50
45
href="https://unpkg.com/@fortawesome/fontawesome-free@6.7.2/css/all.min.css"
51
46
preload
52
47
/>
53
-
{scripts?.map((file) => (
54
-
<script
55
-
key={file}
56
-
src={`/static/${file}?${staticFilesHash?.get(file)}`}
57
-
/>
58
-
))}
48
+
<script
49
+
type="module"
50
+
key="app.esm.js"
51
+
src={`/static/app.esm.js?${staticFilesHash?.get("app.esm.js")}`}
52
+
/>
59
53
</head>
60
54
<body class="h-full dark:bg-zinc-950 dark:text-white">
61
55
<Layout id="layout">
+1
-1
src/components/AvatarInput.tsx
+1
-1
src/components/AvatarInput.tsx
+27
src/components/Breadcrumb.tsx
+27
src/components/Breadcrumb.tsx
···
1
+
type BreadcrumbItem = {
2
+
label: string;
3
+
href?: string;
4
+
};
5
+
6
+
export function Breadcrumb({ items }: Readonly<{ items: BreadcrumbItem[] }>) {
7
+
return (
8
+
<nav className="mb-4 text-sm text-zinc-500 dark:text-zinc-300">
9
+
{items.map((item, idx) => (
10
+
<>
11
+
{item.href
12
+
? (
13
+
<a href={item.href} className="text-sky-500 hover:underline">
14
+
{item.label}
15
+
</a>
16
+
)
17
+
: (
18
+
<span className="text-zinc-700 dark:text-zinc-100">
19
+
{item.label}
20
+
</span>
21
+
)}
22
+
{idx < items.length - 1 && <span className="mx-2">></span>}
23
+
</>
24
+
))}
25
+
</nav>
26
+
);
27
+
}
+22
src/components/CameraBadges.tsx
+22
src/components/CameraBadges.tsx
···
1
+
import { cn } from "@bigmoves/bff/components";
2
+
3
+
export function CameraBadges(
4
+
{ cameras, class: classProp }: Readonly<
5
+
{ cameras: string[]; class?: string }
6
+
>,
7
+
) {
8
+
if (cameras.length === 0) return null;
9
+
return (
10
+
<div class={cn("flex gap-1", classProp)} id="camera-badges">
11
+
{cameras.sort().map((camera) => (
12
+
<span class="text-xs font-semibold bg-zinc-100 dark:bg-zinc-800 w-fit px-1">
13
+
📷 {camera}
14
+
</span>
15
+
))}
16
+
</div>
17
+
);
18
+
}
19
+
20
+
// <span class="text-xs font-semibold bg-zinc-100 dark:bg-zinc-800 w-fit px-1">
21
+
// 📷 {cameras.join(", ").replace(/, ([^,]*)$/, " & $1")}
22
+
// </span>
+47
src/components/CreateAccountDialog.tsx
+47
src/components/CreateAccountDialog.tsx
···
1
+
import { OAUTH_ROUTES } from "@bigmoves/bff";
2
+
import { Button, Dialog } from "@bigmoves/bff/components";
3
+
import { PDS_HOST_URL } from "../env.ts";
4
+
5
+
export function CreateAccountDialog({}: Readonly<{}>) {
6
+
return (
7
+
<Dialog id="photo-alt-dialog" class="z-100">
8
+
<Dialog.Content class="dark:bg-zinc-950 relative">
9
+
<Dialog.X class="fill-zinc-950 dark:fill-zinc-50" />
10
+
<Dialog.Title>Choose your handle</Dialog.Title>
11
+
<div className="flex flex-col space-y-4 my-10">
12
+
<form hx-post={OAUTH_ROUTES.signup} hx-swap="none" class="w-full">
13
+
<input
14
+
type="hidden"
15
+
name="pdsHostUrl"
16
+
value="https://bsky.social"
17
+
/>
18
+
<Button
19
+
type="submit"
20
+
variant="primary"
21
+
class="w-full"
22
+
>
23
+
user.bsky.social
24
+
</Button>
25
+
</form>
26
+
<form hx-post={OAUTH_ROUTES.signup} hx-swap="none" class="w-full">
27
+
<input
28
+
type="hidden"
29
+
name="pdsHostUrl"
30
+
value={PDS_HOST_URL}
31
+
/>
32
+
<Button
33
+
type="submit"
34
+
variant="primary"
35
+
class="w-full"
36
+
>
37
+
user.grain.social
38
+
</Button>
39
+
</form>
40
+
<p>
41
+
Note: <b>.grain.social</b> handles are currently invite only.
42
+
</p>
43
+
</div>
44
+
</Dialog.Content>
45
+
</Dialog>
46
+
);
47
+
}
+33
src/components/ExifOverlayDialog.tsx
+33
src/components/ExifOverlayDialog.tsx
···
1
+
import { PhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
2
+
import { Dialog } from "@bigmoves/bff/components";
3
+
import { getOrderedExifData } from "../lib/photo.ts";
4
+
5
+
export function ExifOverlayDialog({
6
+
photo,
7
+
}: Readonly<{
8
+
photo: PhotoView;
9
+
}>) {
10
+
return (
11
+
<Dialog class="z-101">
12
+
<Dialog.Content
13
+
class="bg-transparent text-zinc-50 relative"
14
+
_={Dialog._closeOnClick}
15
+
>
16
+
<Dialog.Title>Camera Settings</Dialog.Title>
17
+
<Dialog.X />
18
+
{photo.exif && (
19
+
<div className="mt-4 text-sm">
20
+
<dl className="grid grid-cols-2 gap-x-4 gap-y-2">
21
+
{getOrderedExifData(photo).map(({ displayKey, value }) => (
22
+
<>
23
+
<dt className="font-medium text-right">{displayKey}:</dt>
24
+
<dd className="text-left">{String(value)}</dd>
25
+
</>
26
+
))}
27
+
</dl>
28
+
</div>
29
+
)}
30
+
</Dialog.Content>
31
+
</Dialog>
32
+
);
33
+
}
+41
src/components/FollowsList.tsx
+41
src/components/FollowsList.tsx
···
1
+
import { ProfileView } from "$lexicon/types/social/grain/actor/defs.ts";
2
+
import { profileLink } from "../utils.ts";
3
+
import { ActorAvatar } from "./ActorAvatar.tsx";
4
+
5
+
export function FollowsList(
6
+
{ profiles }: Readonly<{ profiles: ProfileView[] }>,
7
+
) {
8
+
return (
9
+
<ul class="space-y-4 relative divide-zinc-200 dark:divide-zinc-800 divide-y">
10
+
{profiles.length === 0
11
+
? (
12
+
<li>
13
+
Not following anyone yet.
14
+
</li>
15
+
)
16
+
: (
17
+
profiles.map((profile) => (
18
+
<li key={profile.did} class="pb-4">
19
+
<a
20
+
href={profileLink(profile.handle)}
21
+
class="flex items-center"
22
+
>
23
+
<div class="flex flex-col space-y-2">
24
+
<div class="flex items-center">
25
+
<ActorAvatar profile={profile} size={32} class="mr-2" />
26
+
<div class="flex flex-col">
27
+
<p>{profile.displayName}</p>
28
+
<p class="text-zinc-600 dark:text-zinc-500">
29
+
@{profile.handle || profile.displayName}
30
+
</p>
31
+
</div>
32
+
</div>
33
+
<p>{profile.description}</p>
34
+
</div>
35
+
</a>
36
+
</li>
37
+
))
38
+
)}
39
+
</ul>
40
+
);
41
+
}
+23
src/components/GalleryInfo.tsx
+23
src/components/GalleryInfo.tsx
···
1
+
import { Record as Gallery } from "$lexicon/types/social/grain/gallery.ts";
2
+
import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
3
+
import { getGalleryCameras } from "../lib/gallery.ts";
4
+
import { ActorInfo } from "./ActorInfo.tsx";
5
+
import { CameraBadges } from "./CameraBadges.tsx";
6
+
7
+
export function GalleryInfo({ gallery }: Readonly<{ gallery: GalleryView }>) {
8
+
const description = (gallery.record as Gallery).description;
9
+
const cameras = getGalleryCameras(gallery);
10
+
return (
11
+
<div
12
+
class="flex flex-col space-y-2 mb-4 max-w-[500px]"
13
+
id="gallery-info"
14
+
>
15
+
<h1 class="font-bold text-2xl">
16
+
{(gallery.record as Gallery).title}
17
+
</h1>
18
+
<ActorInfo profile={gallery.creator} />
19
+
{description ? <p>{description}</p> : null}
20
+
<CameraBadges class="my-1" cameras={cameras} />
21
+
</div>
22
+
);
23
+
}
+103
src/components/GalleryLayout.tsx
+103
src/components/GalleryLayout.tsx
···
1
+
import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
2
+
import { PhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
3
+
import { AtUri } from "@atproto/syntax";
4
+
import { Button } from "@bigmoves/bff/components";
5
+
import { ComponentChildren } from "preact";
6
+
import { photoDialogLink } from "../utils.ts";
7
+
import { JustifiedSvg } from "./JustifiedSvg.tsx";
8
+
import { MasonrySvg } from "./MasonrySvg.tsx";
9
+
10
+
interface GalleryLayoutProps {
11
+
children: ComponentChildren;
12
+
layoutButtons?: ComponentChildren;
13
+
}
14
+
15
+
function GalleryLayout({ children, layoutButtons }: GalleryLayoutProps) {
16
+
return (
17
+
<>
18
+
{layoutButtons
19
+
? (
20
+
<div class="mb-2 flex justify-end">
21
+
{layoutButtons}
22
+
</div>
23
+
)
24
+
: null}
25
+
{children}
26
+
</>
27
+
);
28
+
}
29
+
30
+
function GalleryContainer({ children }: { children: ComponentChildren }) {
31
+
return (
32
+
<div
33
+
id="gallery-container"
34
+
class="h-0 overflow-hidden relative mx-auto w-full"
35
+
>
36
+
{children}
37
+
</div>
38
+
);
39
+
}
40
+
41
+
function GalleryLayoutModeButton({
42
+
mode,
43
+
}: Readonly<{
44
+
mode: "justified" | "masonry";
45
+
}>) {
46
+
return (
47
+
<Button
48
+
id={`${mode}-button`}
49
+
title={`${mode.charAt(0).toUpperCase() + mode.slice(1)} layout`}
50
+
variant="primary"
51
+
data-selected={mode === "justified" ? "true" : "false"}
52
+
class="flex justify-center w-full sm:w-fit bg-zinc-100 dark:bg-zinc-800 border-zinc-100 dark:border-zinc-800 data-[selected=false]:bg-transparent data-[selected=false]:border-transparent text-zinc-950 dark:text-zinc-50"
53
+
_={`on click call Grain.galleryLayout.setLayoutMode('${mode}')
54
+
set @data-selected to 'true'
55
+
set #${
56
+
mode === "justified" ? "masonry" : "justified"
57
+
}-button's @data-selected to 'false'`}
58
+
>
59
+
{mode === "masonry" ? <MasonrySvg /> : <JustifiedSvg />}
60
+
</Button>
61
+
);
62
+
}
63
+
64
+
function GalleryLayoutItem({
65
+
photo,
66
+
gallery,
67
+
}: Readonly<{
68
+
photo: PhotoView;
69
+
gallery: GalleryView;
70
+
}>) {
71
+
return (
72
+
<button
73
+
id={`photo-${new AtUri(photo.uri).rkey}`}
74
+
type="button"
75
+
hx-get={photoDialogLink(gallery, photo)}
76
+
hx-trigger="click"
77
+
hx-target="#layout"
78
+
hx-swap="afterbegin"
79
+
class="gallery-item absolute cursor-pointer"
80
+
data-width={photo.aspectRatio?.width}
81
+
data-height={photo.aspectRatio?.height}
82
+
>
83
+
<img
84
+
src={photo.fullsize}
85
+
alt={photo.alt}
86
+
class="w-full h-full object-cover"
87
+
/>
88
+
{photo.alt
89
+
? (
90
+
<div class="absolute bg-zinc-950 dark:bg-zinc-900 bottom-1 right-1 sm:bottom-1 sm:right-1 text-xs text-white font-semibold py-[1px] px-[3px]">
91
+
ALT
92
+
</div>
93
+
)
94
+
: null}
95
+
</button>
96
+
);
97
+
}
98
+
99
+
GalleryLayout.Container = GalleryContainer;
100
+
GalleryLayout.ModeButton = GalleryLayoutModeButton;
101
+
GalleryLayout.Item = GalleryLayoutItem;
102
+
103
+
export { GalleryLayout };
+19
-126
src/components/GalleryPage.tsx
+19
-126
src/components/GalleryPage.tsx
···
1
1
import { Record as Favorite } from "$lexicon/types/social/grain/favorite.ts";
2
-
import { Record as Gallery } from "$lexicon/types/social/grain/gallery.ts";
3
2
import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
4
3
import { isPhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
5
4
import { AtUri } from "@atproto/syntax";
6
5
import { WithBffMeta } from "@bigmoves/bff";
7
6
import { Button } from "@bigmoves/bff/components";
8
-
import { ActorInfo } from "./ActorInfo.tsx";
9
7
import { FavoriteButton } from "./FavoriteButton.tsx";
10
-
import { PhotoButton } from "./PhotoButton.tsx";
8
+
import { GalleryInfo } from "./GalleryInfo.tsx";
9
+
import { GalleryLayout } from "./GalleryLayout.tsx";
11
10
import { ShareGalleryButton } from "./ShareGalleryButton.tsx";
12
11
13
12
export function GalleryPage({
···
21
20
}>) {
22
21
const isCreator = currentUserDid === gallery.creator.did;
23
22
const isLoggedIn = !!currentUserDid;
24
-
const description = (gallery.record as Gallery).description;
23
+
const galleryItems = gallery.items?.filter(isPhotoView) ?? [];
25
24
return (
26
-
<div class="px-4">
25
+
<div class="px-4" id="gallery-page">
27
26
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mt-4 mb-2">
28
-
<div class="flex flex-col space-y-2 mb-4 max-w-[500px]">
29
-
<h1 class="font-bold text-2xl">
30
-
{(gallery.record as Gallery).title}
31
-
</h1>
32
-
<ActorInfo profile={gallery.creator} />
33
-
{description ? <p>{description}</p> : null}
34
-
</div>
27
+
<GalleryInfo gallery={gallery} />
35
28
{isLoggedIn && isCreator
36
29
? (
37
30
<div class="flex self-start gap-2 w-full sm:w-fit flex-col sm:flex-row sm:flex-wrap sm:justify-end">
···
84
77
)
85
78
: null}
86
79
</div>
87
-
<div class="flex justify-end mb-2">
88
-
<Button
89
-
id="justified-button"
90
-
title="Justified layout"
91
-
variant="primary"
92
-
class="flex justify-center w-full sm:w-fit bg-zinc-100 dark:bg-zinc-800 border-zinc-100 dark:border-zinc-800 data-[selected=false]:bg-transparent data-[selected=false]:border-transparent text-zinc-950 dark:text-zinc-50"
93
-
_="on click call Grain.toggleLayout('justified')
94
-
set @data-selected to 'true'
95
-
set #masonry-button's @data-selected to 'false'"
96
-
>
97
-
<svg
98
-
width="24"
99
-
height="24"
100
-
viewBox="0 0 24 24"
101
-
xmlns="http://www.w3.org/2000/svg"
102
-
>
103
-
<rect x="2" y="2" width="8" height="6" fill="currentColor" rx="1" />
104
-
<rect
105
-
x="12"
106
-
y="2"
107
-
width="10"
108
-
height="6"
109
-
fill="currentColor"
110
-
rx="1"
111
-
/>
112
-
<rect
113
-
x="2"
114
-
y="10"
115
-
width="6"
116
-
height="6"
117
-
fill="currentColor"
118
-
rx="1"
119
-
/>
120
-
<rect
121
-
x="10"
122
-
y="10"
123
-
width="12"
124
-
height="6"
125
-
fill="currentColor"
126
-
rx="1"
127
-
/>
128
-
<rect
129
-
x="2"
130
-
y="18"
131
-
width="20"
132
-
height="4"
133
-
fill="currentColor"
134
-
rx="1"
135
-
/>
136
-
</svg>
137
-
</Button>
138
-
<Button
139
-
id="masonry-button"
140
-
title="Masonry layout"
141
-
variant="primary"
142
-
data-selected="false"
143
-
class="flex justify-center w-full sm:w-fit bg-zinc-100 dark:bg-zinc-800 border-zinc-100 dark:border-zinc-800 data-[selected=false]:bg-transparent data-[selected=false]:border-transparent text-zinc-950 dark:text-zinc-50"
144
-
_="on click call Grain.toggleLayout('masonry')
145
-
set @data-selected to 'true'
146
-
set #justified-button's @data-selected to 'false'"
147
-
>
148
-
<svg
149
-
width="24"
150
-
height="24"
151
-
viewBox="0 0 24 24"
152
-
xmlns="http://www.w3.org/2000/svg"
153
-
>
154
-
<rect x="2" y="2" width="8" height="8" fill="currentColor" rx="1" />
155
-
<rect
156
-
x="12"
157
-
y="2"
158
-
width="8"
159
-
height="4"
160
-
fill="currentColor"
161
-
rx="1"
162
-
/>
163
-
<rect
164
-
x="12"
165
-
y="8"
166
-
width="8"
167
-
height="6"
168
-
fill="currentColor"
169
-
rx="1"
170
-
/>
171
-
<rect
172
-
x="2"
173
-
y="12"
174
-
width="8"
175
-
height="8"
176
-
fill="currentColor"
177
-
rx="1"
178
-
/>
179
-
<rect
180
-
x="12"
181
-
y="16"
182
-
width="8"
183
-
height="4"
184
-
fill="currentColor"
185
-
rx="1"
186
-
/>
187
-
</svg>
188
-
</Button>
189
-
</div>
190
-
<div
191
-
id="masonry-container"
192
-
class="h-0 overflow-hidden relative mx-auto w-full"
193
-
_="on load or htmx:afterSettle call Grain.computeLayout()"
80
+
<GalleryLayout
81
+
layoutButtons={
82
+
<>
83
+
<GalleryLayout.ModeButton mode="justified" />
84
+
<GalleryLayout.ModeButton mode="masonry" />
85
+
</>
86
+
}
194
87
>
195
-
{gallery.items?.filter(isPhotoView)?.length
196
-
? gallery?.items
197
-
?.filter(isPhotoView)
198
-
?.map((photo) => (
199
-
<PhotoButton
88
+
<GalleryLayout.Container>
89
+
{galleryItems?.length
90
+
? galleryItems.map((photo) => (
91
+
<GalleryLayout.Item
200
92
key={photo.cid}
201
93
photo={photo}
202
94
gallery={gallery}
203
95
/>
204
96
))
205
-
: null}
206
-
</div>
97
+
: null}
98
+
</GalleryLayout.Container>
99
+
</GalleryLayout>
207
100
</div>
208
101
);
209
102
}
+1
-1
src/components/GallerySortDialog.tsx
+1
-1
src/components/GallerySortDialog.tsx
···
7
7
{ gallery }: Readonly<{ gallery: GalleryView }>,
8
8
) {
9
9
return (
10
-
<Dialog class="z-100">
10
+
<Dialog class="z-100" id="gallery-sort-dialog">
11
11
<Dialog.Content class="dark:bg-zinc-950 relative">
12
12
<Dialog.X class="fill-zinc-950 dark:fill-zinc-50" />
13
13
<Dialog.Title>Sort gallery</Dialog.Title>
+44
src/components/JustifiedSvg.tsx
+44
src/components/JustifiedSvg.tsx
···
1
+
export function JustifiedSvg() {
2
+
return (
3
+
<svg
4
+
width="24"
5
+
height="24"
6
+
viewBox="0 0 24 24"
7
+
xmlns="http://www.w3.org/2000/svg"
8
+
>
9
+
<rect x="2" y="2" width="8" height="6" fill="currentColor" rx="1" />
10
+
<rect
11
+
x="12"
12
+
y="2"
13
+
width="10"
14
+
height="6"
15
+
fill="currentColor"
16
+
rx="1"
17
+
/>
18
+
<rect
19
+
x="2"
20
+
y="10"
21
+
width="6"
22
+
height="6"
23
+
fill="currentColor"
24
+
rx="1"
25
+
/>
26
+
<rect
27
+
x="10"
28
+
y="10"
29
+
width="12"
30
+
height="6"
31
+
fill="currentColor"
32
+
rx="1"
33
+
/>
34
+
<rect
35
+
x="2"
36
+
y="18"
37
+
width="20"
38
+
height="4"
39
+
fill="currentColor"
40
+
rx="1"
41
+
/>
42
+
</svg>
43
+
);
44
+
}
+9
-5
src/components/Layout.tsx
+9
-5
src/components/Layout.tsx
···
111
111
)
112
112
: (
113
113
<div class="flex items-center space-x-4">
114
-
<form hx-post="/signup" hx-swap="none" class="inline">
115
-
<Button variant="secondary" type="submit">
116
-
Create account
117
-
</Button>
118
-
</form>
114
+
<Button
115
+
variant="secondary"
116
+
hx-get={`/dialogs/create-account`}
117
+
hx-trigger="click"
118
+
hx-target="body"
119
+
hx-swap="afterbegin"
120
+
>
121
+
Create account
122
+
</Button>
119
123
<Button variant="secondary" asChild>
120
124
<a href="/login">
121
125
Sign in
+46
-9
src/components/LoginPage.tsx
+46
-9
src/components/LoginPage.tsx
···
8
8
class="flex justify-center items-center w-full h-[calc(100vh-56px)] relative"
9
9
style="background-image: url('https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:bcgltzqazw5tb6k2g3ttenbj/bafkreiewhwu3ro5dv7omedphb62db4koa7qtvyzfhiiypg3ru4tvuxkrjy@jpeg'); background-size: cover; background-position: center;"
10
10
>
11
-
<Login hx-target="#login" error={error} errorClass="text-white" />
12
-
<div class="absolute bottom-2 right-2 text-white text-sm">
13
-
Photo by{" "}
14
-
<a
15
-
href={profileLink("chadtmiller.com")}
16
-
class="hover:underline font-semibold"
17
-
>
18
-
@chadtmiller.com
19
-
</a>
11
+
<Login
12
+
hx-target="#login"
13
+
error={error}
14
+
errorClass="text-white"
15
+
inputPlaceholder="Enter your handle or pds host"
16
+
submitText="Login"
17
+
infoText="e.g., user.bsky.social, user.grain.social, example.com, https://pds.example.com"
18
+
infoClass="text-white text-sm! bg-zinc-950/70 p-4 font-mono"
19
+
/>
20
+
<div class="absolute bottom-2 left-2 right-2 flex flex-col sm:flex-row justify-between items-start sm:items-end text-white text-sm gap-1 sm:gap-0">
21
+
<div class="flex flex-col sm:flex-row">
22
+
<span>
23
+
© 2025 Grain Social. All rights reserved.
24
+
</span>
25
+
<span class="flex flex-row items-center flex-wrap">
26
+
<a
27
+
href="/support/terms"
28
+
class="underline hover:no-underline ml-0 sm:ml-2 mt-1 sm:mt-0"
29
+
>
30
+
Terms
31
+
</a>
32
+
<span class="mx-1">|</span>
33
+
<a
34
+
href="/support/privacy"
35
+
class="underline hover:no-underline ml-0 sm:ml-1 mt-1 sm:mt-0"
36
+
>
37
+
Privacy
38
+
</a>
39
+
<span class="mx-1">|</span>
40
+
<a
41
+
href="/support/copyright"
42
+
class="underline hover:no-underline ml-0 sm:ml-1 mt-1 sm:mt-0"
43
+
>
44
+
Copyright
45
+
</a>
46
+
</span>
47
+
</div>
48
+
<div>
49
+
Photo by{" "}
50
+
<a
51
+
href={profileLink("chadtmiller.com")}
52
+
class="underline hover:no-underline"
53
+
>
54
+
@chadtmiller.com
55
+
</a>
56
+
</div>
20
57
</div>
21
58
</div>
22
59
);
+44
src/components/MasonrySvg.tsx
+44
src/components/MasonrySvg.tsx
···
1
+
export function MasonrySvg() {
2
+
return (
3
+
<svg
4
+
width="24"
5
+
height="24"
6
+
viewBox="0 0 24 24"
7
+
xmlns="http://www.w3.org/2000/svg"
8
+
>
9
+
<rect x="2" y="2" width="8" height="8" fill="currentColor" rx="1" />
10
+
<rect
11
+
x="12"
12
+
y="2"
13
+
width="8"
14
+
height="4"
15
+
fill="currentColor"
16
+
rx="1"
17
+
/>
18
+
<rect
19
+
x="12"
20
+
y="8"
21
+
width="8"
22
+
height="6"
23
+
fill="currentColor"
24
+
rx="1"
25
+
/>
26
+
<rect
27
+
x="2"
28
+
y="12"
29
+
width="8"
30
+
height="8"
31
+
fill="currentColor"
32
+
rx="1"
33
+
/>
34
+
<rect
35
+
x="12"
36
+
y="16"
37
+
width="8"
38
+
height="4"
39
+
fill="currentColor"
40
+
rx="1"
41
+
/>
42
+
</svg>
43
+
);
44
+
}
+15
-3
src/components/NotificationsPage.tsx
+15
-3
src/components/NotificationsPage.tsx
···
1
1
import { Record as Favorite } from "$lexicon/types/social/grain/favorite.ts";
2
2
import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
3
+
import { Record as Follow } from "$lexicon/types/social/grain/graph/follow.ts";
3
4
import { NotificationView } from "$lexicon/types/social/grain/notification/defs.ts";
4
5
import { Un$Typed } from "$lexicon/util.ts";
5
6
import { formatRelativeTime, profileLink } from "../utils.ts";
···
44
45
</span>
45
46
</a>
46
47
<span class="break-words">
47
-
favorited your gallery · {formatRelativeTime(
48
-
new Date((notification.record as Favorite).createdAt),
48
+
{notification.reason === "gallery-favorite" && (
49
+
<>
50
+
favorited your gallery · {formatRelativeTime(
51
+
new Date((notification.record as Favorite).createdAt),
52
+
)}
53
+
</>
54
+
)}
55
+
{notification.reason === "follow" && (
56
+
<>
57
+
followed you · {formatRelativeTime(
58
+
new Date((notification.record as Follow).createdAt),
59
+
)}
60
+
</>
49
61
)}
50
62
</span>
51
63
</div>
52
-
{galleriesMap.get(
64
+
{notification.reason === "gallery-favorite" && galleriesMap.get(
53
65
(notification.record as Favorite).subject,
54
66
)
55
67
? (
-39
src/components/PhotoButton.tsx
-39
src/components/PhotoButton.tsx
···
1
-
import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
2
-
import { PhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
3
-
import { AtUri } from "@atproto/syntax";
4
-
import { photoDialogLink } from "../utils.ts";
5
-
6
-
export function PhotoButton({
7
-
photo,
8
-
gallery,
9
-
}: Readonly<{
10
-
photo: PhotoView;
11
-
gallery: GalleryView;
12
-
}>) {
13
-
return (
14
-
<button
15
-
id={`photo-${new AtUri(photo.uri).rkey}`}
16
-
type="button"
17
-
hx-get={photoDialogLink(gallery, photo)}
18
-
hx-trigger="click"
19
-
hx-target="#layout"
20
-
hx-swap="afterbegin"
21
-
class="masonry-tile absolute cursor-pointer"
22
-
data-width={photo.aspectRatio?.width}
23
-
data-height={photo.aspectRatio?.height}
24
-
>
25
-
<img
26
-
src={photo.fullsize}
27
-
alt={photo.alt}
28
-
class="w-full h-full object-cover"
29
-
/>
30
-
{photo.alt
31
-
? (
32
-
<div class="absolute bg-zinc-950 dark:bg-zinc-900 bottom-1 right-1 sm:bottom-1 sm:right-1 text-xs text-white font-semibold py-[1px] px-[3px]">
33
-
ALT
34
-
</div>
35
-
)
36
-
: null}
37
-
</button>
38
-
);
39
-
}
+45
-2
src/components/PhotoDialog.tsx
+45
-2
src/components/PhotoDialog.tsx
···
1
1
import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
2
2
import { PhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
3
+
import { AtUri } from "@atproto/syntax";
3
4
import { Dialog } from "https://jsr.io/@bigmoves/bff/0.3.0-beta.21/components/Dialog.tsx";
5
+
import { cn } from "../../../bff/packages/bff/components/utils.ts";
4
6
import { photoDialogLink } from "../utils.ts";
5
7
6
8
export function PhotoDialog({
···
38
40
)
39
41
: null}
40
42
<div
41
-
class="flex flex-col w-5xl h-[calc(100vh-100px)] sm:h-screen z-20"
43
+
class="flex flex-col w-5xl h-[calc(100vh-100px)] sm:h-screen z-20 relative sm:static"
42
44
_={Dialog._closeOnClick}
43
45
>
44
46
<div class="flex flex-col p-4 z-20 flex-1 relative">
···
48
50
class="absolute inset-0 w-full h-full object-contain"
49
51
/>
50
52
</div>
53
+
{image.exif
54
+
? (
55
+
<div class="hidden sm:block absolute bottom-2 right-2">
56
+
<ExifButton photo={image} />
57
+
</div>
58
+
)
59
+
: null}
51
60
{image.alt
52
61
? (
53
-
<div class="px-4 sm:px-0 py-4 bg-black text-white text-left">
62
+
<div class="px-4 sm:px-0 py-4 bg-black text-white text-left flex">
54
63
{image.alt}
64
+
{image.exif
65
+
? (
66
+
<div class="block sm:hidden self-end justify-end -m-2">
67
+
<ExifButton photo={image} />
68
+
</div>
69
+
)
70
+
: null}
55
71
</div>
56
72
)
57
73
: null}
74
+
{!image.alt && image.exif
75
+
? (
76
+
<ExifButton
77
+
photo={image}
78
+
class="block sm:hidden absolute bottom-2 right-2 z-100"
79
+
/>
80
+
)
81
+
: null}
58
82
</div>
59
83
</Dialog>
60
84
);
61
85
}
86
+
87
+
function ExifButton(
88
+
{ photo, class: classProp }: Readonly<{ photo: PhotoView; class?: string }>,
89
+
) {
90
+
return (
91
+
<button
92
+
type="button"
93
+
class={cn("text-zinc-50 p-2 cursor-pointer", classProp)}
94
+
hx-get={`/dialogs/photo/${new AtUri(photo.uri).rkey}/exif-overlay`}
95
+
hx-trigger="click"
96
+
hx-target="#layout"
97
+
hx-swap="afterbegin"
98
+
_="on click halt"
99
+
>
100
+
<i class="fa fa-camera" />
101
+
<span class="sr-only">Show EXIF</span>
102
+
</button>
103
+
);
104
+
}
+9
-39
src/components/PhotoExifDialog.tsx
+9
-39
src/components/PhotoExifDialog.tsx
···
1
1
import { PhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
2
2
import { Dialog } from "@bigmoves/bff/components";
3
+
import { getOrderedExifData } from "../lib/photo.ts";
3
4
4
5
export function PhotoExifDialog({
5
6
photo,
6
7
}: Readonly<{
7
8
photo: PhotoView;
8
9
}>) {
10
+
console.log(getOrderedExifData(photo));
9
11
return (
10
12
<Dialog id="photo-alt-dialog" class="z-100">
11
13
<Dialog.Content class="dark:bg-zinc-950 relative">
···
19
21
/>
20
22
</div>
21
23
{photo.exif && (
22
-
<div className="mt-4 text-sm text-zinc-700 dark:text-zinc-300">
23
-
{
24
-
/* <a
25
-
href={`https://pdsls.dev/${photo.exif.uri}`}
26
-
className="my-4 hover:underline font-semibold block text-sky-500"
27
-
>
28
-
Inspect Record
29
-
</a> */
30
-
}
24
+
<div className="mt-4 text-sm">
31
25
<dl className="grid grid-cols-2 gap-x-4 gap-y-2">
32
-
{Object.entries(photo.exif)
33
-
.filter(
34
-
([key]) =>
35
-
![
36
-
"$type",
37
-
"photo",
38
-
"createdAt",
39
-
"uri",
40
-
"cid",
41
-
"did",
42
-
"indexedAt",
43
-
].includes(key),
44
-
)
45
-
.map(([key, value]) => {
46
-
let displayKey;
47
-
if (key.toLowerCase() === "iso") {
48
-
displayKey = "ISO";
49
-
} else {
50
-
displayKey = key
51
-
.replace(/([a-z])([A-Z])/g, "$1 $2")
52
-
.replace(/_/g, " ")
53
-
.replace(/\b\w/g, (c) => c.toUpperCase());
54
-
}
55
-
return (
56
-
<>
57
-
<dt className="font-medium text-right">{displayKey}:</dt>
58
-
<dd className="text-left">{String(value)}</dd>
59
-
</>
60
-
);
61
-
})}
26
+
{getOrderedExifData(photo).map(({ displayKey, value }) => (
27
+
<>
28
+
<dt className="font-medium text-right">{displayKey}:</dt>
29
+
<dd className="text-left">{String(value)}</dd>
30
+
</>
31
+
))}
62
32
</dl>
63
33
</div>
64
34
)}
+1
-1
src/components/ProfileDialog.tsx
+1
-1
src/components/ProfileDialog.tsx
···
19
19
halt the event
20
20
put 'Updating...' into #submit-button.innerText
21
21
add @disabled to #submit-button
22
-
call Grain.updateProfile(me)
22
+
call Grain.profileDialog.updateProfile(me)
23
23
on htmx:afterOnLoad
24
24
put 'Update' into #submit-button.innerText
25
25
remove @disabled from #submit-button
+148
-39
src/components/ProfilePage.tsx
+148
-39
src/components/ProfilePage.tsx
···
5
5
import { Un$Typed } from "$lexicon/util.ts";
6
6
import { AtUri } from "@atproto/syntax";
7
7
import { Button, cn } from "@bigmoves/bff/components";
8
-
import { TimelineItem } from "../lib/timeline.ts";
9
-
import { bskyProfileLink, galleryLink, profileLink } from "../utils.ts";
8
+
import { getGalleryCameras } from "../lib/gallery.ts";
9
+
import type { SocialNetwork } from "../lib/timeline.ts";
10
+
import {
11
+
bskyProfileLink,
12
+
followersLink,
13
+
followingLink,
14
+
galleryLink,
15
+
profileLink,
16
+
} from "../utils.ts";
17
+
import { ActorAvatar } from "./ActorAvatar.tsx";
10
18
import { AvatarButton } from "./AvatarButton.tsx";
19
+
import { CameraBadges } from "./CameraBadges.tsx";
11
20
import { FollowButton } from "./FollowButton.tsx";
12
-
import { TimelineItem as Item } from "./TimelineItem.tsx";
21
+
22
+
export type ProfileTabs = "favs" | "galleries";
13
23
14
24
export function ProfilePage({
15
25
followUri,
26
+
followersCount,
27
+
followingCount,
28
+
userProfiles,
16
29
loggedInUserDid,
17
-
timelineItems,
18
30
profile,
19
31
selectedTab,
20
32
galleries,
33
+
galleryFavs,
21
34
}: Readonly<{
22
35
followUri?: string;
36
+
followersCount?: number;
37
+
followingCount?: number;
38
+
userProfiles: SocialNetwork[];
39
+
actorProfiles: SocialNetwork[];
23
40
loggedInUserDid?: string;
24
-
timelineItems: TimelineItem[];
25
41
profile: Un$Typed<ProfileView>;
26
-
selectedTab?: string;
42
+
selectedTab?: ProfileTabs;
27
43
galleries?: GalleryView[];
44
+
galleryFavs?: GalleryView[];
28
45
}>) {
29
46
const isCreator = loggedInUserDid === profile.did;
30
47
const displayName = profile.displayName || profile.handle;
48
+
const cameras = Array.from(
49
+
new Set(galleries?.flatMap(getGalleryCameras) ?? []),
50
+
);
31
51
return (
32
52
<div class="px-4 mb-4" id="profile-page">
33
53
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between my-4">
···
35
55
<AvatarButton profile={profile} />
36
56
<p class="text-2xl font-bold">{displayName}</p>
37
57
<p class="text-zinc-600 dark:text-zinc-500">@{profile.handle}</p>
58
+
<p class="space-x-1">
59
+
<a href={followersLink(profile.handle)}>
60
+
<span class="font-semibold" id="followers-count">
61
+
{followersCount ?? 0}
62
+
</span>{" "}
63
+
<span class="text-zinc-600 dark:text-zinc-500">followers</span>
64
+
</a>{" "}
65
+
<a href={followingLink(profile.handle)}>
66
+
<span class="font-semibold" id="following-count">
67
+
{followingCount ?? 0}
68
+
</span>{" "}
69
+
<span class="text-zinc-600 dark:text-zinc-500">following</span>
70
+
</a>{" "}
71
+
<span class="font-semibold">{galleries?.length ?? 0}</span>
72
+
<span class="text-zinc-600 dark:text-zinc-500">galleries</span>
73
+
</p>
74
+
<CameraBadges cameras={cameras} class="mt-2" />
38
75
{profile.description
39
76
? <p class="mt-2 sm:max-w-[500px]">{profile.description}</p>
40
77
: null}
41
78
<p>
42
-
<a
43
-
href={bskyProfileLink(profile.handle)}
44
-
class="text-xs hover:underline"
45
-
>
46
-
<i class="fa-brands fa-bluesky text-sky-500" /> @{profile.handle}
47
-
</a>
79
+
{userProfiles.includes("bluesky") && (
80
+
<a
81
+
href={bskyProfileLink(profile.handle)}
82
+
class="text-xs hover:underline"
83
+
>
84
+
<i class="fa-brands fa-bluesky text-sky-500" />{" "}
85
+
@{profile.handle}
86
+
</a>
87
+
)}
48
88
</p>
49
89
</div>
50
90
{!isCreator && loggedInUserDid
51
91
? (
52
92
<div class="flex self-start gap-2 w-full sm:w-fit flex-col sm:flex-row">
53
-
<FollowButton followeeDid={profile.did} followUri={followUri} />
93
+
<FollowButton
94
+
followeeDid={profile.did}
95
+
followUri={followUri}
96
+
/>
54
97
</div>
55
98
)
56
99
: null}
···
91
134
)
92
135
: null}
93
136
</div>
94
-
<div class="my-4 space-x-2 w-full flex sm:w-fit" role="tablist">
137
+
<div
138
+
class="my-4 w-full flex sm:w-fit space-x-2 overflow-x-auto"
139
+
role="tablist"
140
+
style={{ WebkitOverflowScrolling: "touch" }}
141
+
>
95
142
<button
96
143
type="button"
144
+
name="tab"
145
+
value="galleries"
97
146
hx-get={profileLink(profile.handle)}
98
-
hx-target="body"
147
+
hx-target="#profile-page"
99
148
hx-swap="outerHTML"
100
149
class={cn(
101
-
"flex-1 py-2 px-4 cursor-pointer font-semibold",
102
-
!selectedTab && "bg-zinc-100 dark:bg-zinc-800 font-semibold",
150
+
"flex-1 min-w-[120px] py-2 px-4 cursor-pointer font-semibold",
151
+
selectedTab === "galleries" && "bg-zinc-100 dark:bg-zinc-800",
103
152
)}
104
153
role="tab"
105
-
aria-selected="true"
154
+
aria-selected={selectedTab === "galleries"}
106
155
aria-controls="tab-content"
107
156
>
108
-
Activity
157
+
Galleries
109
158
</button>
110
-
<button
159
+
{isCreator && (
160
+
<button
161
+
type="button"
162
+
name="tab"
163
+
value="favs"
164
+
hx-get={profileLink(profile.handle)}
165
+
hx-target="#profile-page"
166
+
hx-swap="outerHTML"
167
+
class={cn(
168
+
"flex-1 min-w-[120px] py-2 px-4 cursor-pointer font-semibold",
169
+
selectedTab === "favs" && "bg-zinc-100 dark:bg-zinc-800",
170
+
)}
171
+
role="tab"
172
+
aria-selected={selectedTab === "favs"}
173
+
aria-controls="tab-content"
174
+
>
175
+
Favs
176
+
</button>
177
+
)}
178
+
{
179
+
/* <button
111
180
type="button"
112
-
hx-get={profileLink(profile.handle) + "?tab=galleries"}
113
-
hx-target="#profile-page"
181
+
hx-get={profileLink(profile.handle)}
182
+
hx-target="body"
114
183
hx-swap="outerHTML"
115
184
class={cn(
116
-
"flex-1 py-2 px-4 cursor-pointer font-semibold",
117
-
selectedTab === "galleries" && "bg-zinc-100 dark:bg-zinc-800",
185
+
"flex-1 min-w-[120px] py-2 px-4 cursor-pointer font-semibold",
186
+
!selectedTab && "bg-zinc-100 dark:bg-zinc-800 font-semibold",
118
187
)}
119
188
role="tab"
120
-
aria-selected="false"
189
+
aria-selected={!selectedTab}
121
190
aria-controls="tab-content"
191
+
hx-push-url="true"
122
192
>
123
-
Galleries
124
-
</button>
193
+
Activity
194
+
</button> */
195
+
}
125
196
</div>
126
197
<div id="tab-content" role="tabpanel">
127
-
{!selectedTab
198
+
{selectedTab === "galleries"
128
199
? (
129
-
<ul class="space-y-4 relative divide-zinc-200 dark:divide-zinc-800 divide-y w-fit">
130
-
{timelineItems.length
200
+
<div class="grid grid-cols-1 sm:grid-cols-3 gap-2 mb-4">
201
+
{galleries?.length
131
202
? (
132
-
timelineItems.map((item) => (
133
-
<Item item={item} key={item.itemUri} />
203
+
galleries.map((gallery) => (
204
+
<a
205
+
href={galleryLink(
206
+
gallery.creator.handle,
207
+
new AtUri(gallery.uri).rkey,
208
+
)}
209
+
class="cursor-pointer relative aspect-square"
210
+
>
211
+
{gallery.items?.length
212
+
? (
213
+
<img
214
+
src={gallery.items?.filter(isPhotoView)?.[0]
215
+
?.fullsize}
216
+
alt={gallery.items?.filter(isPhotoView)?.[0]?.alt}
217
+
class="w-full h-full object-cover"
218
+
/>
219
+
)
220
+
: (
221
+
<div class="w-full h-full bg-zinc-200 dark:bg-zinc-900" />
222
+
)}
223
+
<div class="absolute bottom-0 left-0 bg-black/80 text-white p-2">
224
+
{(gallery.record as Gallery).title}
225
+
</div>
226
+
</a>
134
227
))
135
228
)
136
-
: <li>No activity yet.</li>}
137
-
</ul>
229
+
: <p>No galleries yet.</p>}
230
+
</div>
138
231
)
139
232
: null}
140
-
{selectedTab === "galleries"
233
+
{selectedTab === "favs"
141
234
? (
142
235
<div class="grid grid-cols-1 sm:grid-cols-3 gap-2 mb-4">
143
-
{galleries?.length
236
+
{galleryFavs?.length
144
237
? (
145
-
galleries.map((gallery) => (
238
+
galleryFavs.map((gallery) => (
146
239
<a
147
240
href={galleryLink(
148
241
gallery.creator.handle,
···
162
255
: (
163
256
<div class="w-full h-full bg-zinc-200 dark:bg-zinc-900" />
164
257
)}
165
-
<div class="absolute bottom-0 left-0 bg-black/80 text-white p-2">
258
+
<div class="absolute bottom-0 left-0 bg-black/80 text-white p-2 flex items-center gap-2">
259
+
<ActorAvatar profile={gallery.creator} size={20} />{" "}
166
260
{(gallery.record as Gallery).title}
167
261
</div>
168
262
</a>
169
263
))
170
264
)
171
-
: <p>No galleries yet.</p>}
265
+
: <p>No favs yet.</p>}
172
266
</div>
173
267
)
174
268
: null}
269
+
{
270
+
/* {!selectedTab
271
+
? (
272
+
<ul class="space-y-4 relative divide-zinc-200 dark:divide-zinc-800 divide-y w-fit">
273
+
{timelineItems.length
274
+
? (
275
+
timelineItems.map((item) => (
276
+
<Item item={item} key={item.itemUri} />
277
+
))
278
+
)
279
+
: <li>No activity yet.</li>}
280
+
</ul>
281
+
)
282
+
: null} */
283
+
}
175
284
</div>
176
285
</div>
177
286
);
+61
-8
src/components/Timeline.tsx
+61
-8
src/components/Timeline.tsx
···
4
4
import { TimelineItem as Item } from "./TimelineItem.tsx";
5
5
6
6
export function Timeline(
7
-
{ isLoggedIn, selectedTab, items }: Readonly<
8
-
{ isLoggedIn: boolean; selectedTab: string; items: TimelineItem[] }
7
+
{ isLoggedIn, selectedTab, items, actorProfiles, selectedGraph }: Readonly<
8
+
{
9
+
isLoggedIn: boolean;
10
+
selectedTab: string;
11
+
items: TimelineItem[];
12
+
actorProfiles: string[];
13
+
selectedGraph: string;
14
+
}
9
15
>,
10
16
) {
11
17
return (
···
17
23
<div class="flex sm:w-fit">
18
24
<button
19
25
type="button"
20
-
hx-get="/"
21
-
hx-target="body"
26
+
hx-get={`/?graph=${selectedGraph}`}
27
+
hx-target="#timeline-page"
22
28
hx-swap="outerHTML"
23
29
class={cn(
24
-
"flex-1 py-2 px-4 cursor-pointer font-semibold",
30
+
"flex-1 py-2 sm:min-w-[120px] px-4 cursor-pointer font-semibold",
25
31
!selectedTab &&
26
32
"bg-zinc-100 dark:bg-zinc-800 font-semibold",
27
33
)}
···
33
39
</button>
34
40
<button
35
41
type="button"
36
-
hx-get="/?tab=following"
42
+
hx-get={`/?tab=following&graph=${selectedGraph}`}
37
43
hx-target="#timeline-page"
38
44
hx-swap="outerHTML"
39
45
class={cn(
40
-
"flex-1 py-2 px-4 cursor-pointer font-semibold",
46
+
"flex-1 py-2 sm:min-w-[120px] px-4 cursor-pointer font-semibold",
41
47
selectedTab === "following" &&
42
48
"bg-zinc-100 dark:bg-zinc-800 font-semibold",
43
49
)}
···
51
57
</div>
52
58
</div>
53
59
<div id="tab-content" role="tabpanel">
60
+
{actorProfiles.length > 1 && selectedTab === "following"
61
+
? (
62
+
<form
63
+
hx-get="/"
64
+
hx-target="#timeline-page"
65
+
hx-swap="outerHTML"
66
+
hx-trigger="change from:#graph-filter"
67
+
class="mb-4 flex flex-col border-b border-zinc-200 dark:border-zinc-800 pb-4"
68
+
>
69
+
<label
70
+
htmlFor="graph-filter"
71
+
class="mb-1 font-medium sr-only"
72
+
>
73
+
Filter by AT Protocol Social Network
74
+
</label>
75
+
76
+
<input type="hidden" name="tab" value={selectedTab || ""} />
77
+
78
+
<select
79
+
id="graph-filter"
80
+
name="graph"
81
+
class="border rounded px-2 py-1 dark:bg-zinc-900 dark:border-zinc-700 max-w-md"
82
+
>
83
+
{actorProfiles.map((graph) => (
84
+
<option
85
+
value={graph}
86
+
key={graph}
87
+
selected={graph === selectedGraph}
88
+
>
89
+
{formatGraphName(graph)}
90
+
</option>
91
+
))}
92
+
</select>
93
+
</form>
94
+
)
95
+
: null}
54
96
<ul class="space-y-4 relative divide-zinc-200 dark:divide-zinc-800 divide-y w-fit">
55
-
{items.map((item) => <Item item={item} key={item.itemUri} />)}
97
+
{items.length > 0
98
+
? items.map((item) => <Item item={item} key={item.itemUri} />)
99
+
: (
100
+
<li class="text-center">
101
+
No galleries by people you follow on{" "}
102
+
{formatGraphName(selectedGraph)} yet.
103
+
</li>
104
+
)}
56
105
</ul>
57
106
</div>
58
107
</>
···
70
119
</div>
71
120
);
72
121
}
122
+
123
+
export function formatGraphName(graph: string): string {
124
+
return graph.charAt(0).toUpperCase() + graph.slice(1);
125
+
}
+38
-29
src/components/UploadPage.tsx
+38
-29
src/components/UploadPage.tsx
···
1
1
import { PhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
2
2
import { Button } from "@bigmoves/bff/components";
3
3
import { profileLink } from "../utils.ts";
4
+
import { Breadcrumb } from "./Breadcrumb.tsx";
4
5
import { PhotoPreview } from "./PhotoPreview.tsx";
5
6
6
7
export function UploadPage({
···
10
11
}: Readonly<{ handle: string; photos: PhotoView[]; returnTo?: string }>) {
11
12
return (
12
13
<div class="flex flex-col px-4 pt-4 mb-4 space-y-4">
13
-
<div class="flex">
14
-
<div class="flex-1">
15
-
{returnTo
16
-
? (
17
-
<a href={returnTo} class="hover:underline">
18
-
<i class="fa-solid fa-arrow-left mr-2" />
19
-
Back to gallery
20
-
</a>
21
-
)
22
-
: (
23
-
<a href={profileLink(handle)} class="hover:underline">
24
-
<i class="fa-solid fa-arrow-left mr-2" />
25
-
Back to profile
26
-
</a>
27
-
)}
28
-
</div>
29
-
</div>
30
-
<Button variant="primary" class="mb-4 w-full sm:w-fit" asChild>
31
-
<label>
32
-
<i class="fa fa-plus"></i> Add photos
33
-
<input
34
-
class="hidden"
35
-
type="file"
36
-
multiple
37
-
accept="image/*"
38
-
_="on change call Grain.uploadPhotos(me)"
39
-
/>
40
-
</label>
41
-
</Button>
14
+
<Breadcrumb
15
+
items={[
16
+
returnTo
17
+
? { label: "Gallery", href: returnTo }
18
+
: { label: "Profile", href: profileLink(handle) },
19
+
{ label: "Upload" },
20
+
]}
21
+
/>
42
22
<div>
43
23
Upload 10 photos at a time. Click{" "}
44
24
<button
···
52
32
</button>{" "}
53
33
to create a gallery or add to existing galleries once you're done!
54
34
</div>
35
+
<form
36
+
hx-encoding="multipart/form-data"
37
+
_="on change from #file-input call Grain.uploadPage.uploadPhotos(me)"
38
+
>
39
+
<Button variant="primary" class="mb-4 w-full sm:w-fit" asChild>
40
+
<label>
41
+
<i class="fa fa-plus"></i> Add photos
42
+
<input
43
+
id="file-input"
44
+
class="hidden"
45
+
type="file"
46
+
name="files"
47
+
multiple
48
+
accept="image/*"
49
+
/>
50
+
</label>
51
+
</Button>
52
+
53
+
<label class="block gap-2">
54
+
<input
55
+
id="parse-exif"
56
+
type="checkbox"
57
+
name="parseExif"
58
+
class="mr-2 accent-sky-600"
59
+
checked
60
+
/>
61
+
Include image metadata (EXIF)
62
+
</label>
63
+
</form>
55
64
<div
56
65
id="image-preview"
57
66
class="w-full h-full grid grid-cols-2 sm:grid-cols-5 gap-2"
+305
src/legal.tsx
+305
src/legal.tsx
···
1
+
import { ComponentChildren } from "preact";
2
+
import { Breadcrumb } from "./components/Breadcrumb.tsx";
3
+
4
+
type SectionProps = {
5
+
title: string;
6
+
children: ComponentChildren;
7
+
};
8
+
9
+
const Section = ({ title, children }: SectionProps) => (
10
+
<section className="mb-8">
11
+
<h2 className="text-xl font-bold mb-2 text-zinc-800 dark:text-zinc-100">
12
+
{title}
13
+
</h2>
14
+
<div className="space-y-2 text-zinc-700 dark:text-zinc-300 text-sm">
15
+
{children}
16
+
</div>
17
+
</section>
18
+
);
19
+
20
+
export function Terms() {
21
+
return (
22
+
<div className="px-4 py-4">
23
+
<Breadcrumb
24
+
items={[{ label: "support", href: "/support" }, { label: "terms" }]}
25
+
/>
26
+
<h1 className="text-3xl font-bold mb-6 text-zinc-900 dark:text-white">
27
+
Terms and Conditions
28
+
</h1>
29
+
<div className="mb-6 text-sm text-zinc-900 dark:text-white">
30
+
Last Updated: June 3, 2025
31
+
</div>
32
+
<Section title="Overview">
33
+
<p>
34
+
Grain is a photo sharing app built on the{" "}
35
+
<a
36
+
href="https://atproto.com/"
37
+
className="text-sky-500 hover:underline"
38
+
target="_blank"
39
+
rel="noopener noreferrer"
40
+
/>
41
+
AT Protocol . All data, including photos, galleries, favorites, and
42
+
metadata, is public and stored on the AT Protocol network. Users can
43
+
upload photos, create and favorite galleries, and view non-location
44
+
EXIF metadata.
45
+
</p>
46
+
<p>
47
+
Grain is an open source project. These Terms apply to your use of the
48
+
hosted version at{" "}
49
+
<code>grain.social</code>, not to self-hosted instances or forks of
50
+
the source code.
51
+
</p>
52
+
</Section>
53
+
54
+
<Section title="Account and Data Ownership">
55
+
<p>
56
+
Grain uses the AT Protocol, so users retain full control over their
57
+
data. We are an independent project and not affiliated with Bluesky or
58
+
the AT Protocol.
59
+
</p>
60
+
<p>
61
+
If you use a <code>grain.social</code>{" "}
62
+
handle, your data may be stored on our own self-hosted{" "}
63
+
<a
64
+
href="https://atproto.com/guides/glossary#pds-personal-data-server"
65
+
className="text-sky-500 hover:underline"
66
+
target="_blank"
67
+
rel="noopener noreferrer"
68
+
>
69
+
PDS (Personal Data Server)
70
+
</a>{" "}
71
+
in accordance with protocol standards.
72
+
</p>
73
+
</Section>
74
+
75
+
<Section title="Content">
76
+
<p>
77
+
You are responsible for any content you share. Do not upload content
78
+
you do not have rights to. All uploads are publicly visible and cannot
79
+
currently be set as private.
80
+
</p>
81
+
</Section>
82
+
83
+
<Section title="Analytics">
84
+
<p>
85
+
We use{" "}
86
+
<a
87
+
href="https://www.goatcounter.com/"
88
+
className="text-sky-500 hover:underline"
89
+
>
90
+
Goatcounter
91
+
</a>{" "}
92
+
for basic analytics. No personal data is collected, tracked, or sold.
93
+
</p>
94
+
</Section>
95
+
96
+
<Section title="Prohibited Conduct">
97
+
<p>
98
+
Do not upload illegal content, harass users, impersonate others, or
99
+
attempt to disrupt the network.
100
+
</p>
101
+
</Section>
102
+
103
+
<Section title="Disclaimers">
104
+
<p>
105
+
Grain is provided "as is." We do not guarantee uptime, data retention,
106
+
or uninterrupted access.
107
+
</p>
108
+
</Section>
109
+
110
+
<Section title="Termination">
111
+
<p>
112
+
We reserve the right to suspend or terminate your access to Grain at
113
+
any time, without prior notice, for conduct that we believe violates
114
+
these Terms, our community standards, or is harmful to other users or
115
+
the AT Protocol network. Terminated accounts may lose access to
116
+
uploaded content unless retained through the protocol’s data
117
+
persistence mechanisms.
118
+
</p>
119
+
</Section>
120
+
121
+
<Section title="Changes">
122
+
<p>
123
+
We may update these terms periodically. Continued use means acceptance
124
+
of any changes.
125
+
</p>
126
+
</Section>
127
+
128
+
<Section title="Contact">
129
+
<p>
130
+
For any questions about these Terms, your account, or issues with the
131
+
app, you can contact us at{" "}
132
+
<a
133
+
href="mailto:support@grain.social"
134
+
className="text-sky-500 hover:underline"
135
+
>
136
+
support@grain.social
137
+
</a>.
138
+
</p>
139
+
</Section>
140
+
</div>
141
+
);
142
+
}
143
+
144
+
export function PrivacyPolicy() {
145
+
return (
146
+
<div className="px-4 py-4">
147
+
<Breadcrumb
148
+
items={[{ label: "support", href: "/support" }, { label: "privacy" }]}
149
+
/>
150
+
<h1 className="text-3xl font-bold mb-6 text-zinc-900 dark:text-white">
151
+
Privacy Policy
152
+
</h1>
153
+
<div className="mb-6 text-sm text-zinc-900 dark:text-white">
154
+
Last Updated: June 3, 2025
155
+
</div>
156
+
<Section title="Data Storage and Access">
157
+
<p>
158
+
Your data is stored on the AT Protocol. If you use a{" "}
159
+
<code>grain.social</code>{" "}
160
+
handle, it may be stored on our PDS. We do not store or access data
161
+
beyond the protocol’s standard behavior.
162
+
</p>
163
+
</Section>
164
+
165
+
<Section title="Public Data">
166
+
<p>
167
+
All content on Grain is public. Private uploads are not currently
168
+
supported.
169
+
</p>
170
+
</Section>
171
+
172
+
{
173
+
/* Coming soon */
174
+
/* <Section title="EXIF Metadata">
175
+
<p>
176
+
We optionally collect and display EXIF metadata (excluding location)
177
+
from your photos. At upload time, you can choose whether to allow this
178
+
metadata to be collected. The metadata is stored according to standard
179
+
AT Protocol storage mechanisms and is not retained outside the
180
+
protocol or used for other purposes.
181
+
</p>
182
+
<p>
183
+
You can learn more about the types of metadata commonly embedded in
184
+
photos at{" "}
185
+
<a
186
+
href="https://exiv2.org/tags.html"
187
+
className="text-sky-500 hover:underline"
188
+
target="_blank"
189
+
rel="noopener noreferrer"
190
+
>
191
+
exiv2.org
192
+
</a>
193
+
.
194
+
</p>
195
+
</Section> */
196
+
}
197
+
198
+
<Section title="Analytics">
199
+
<p>
200
+
We use{" "}
201
+
<a
202
+
href="https://www.goatcounter.com/"
203
+
className="text-sky-500 hover:underline"
204
+
>
205
+
Goatcounter
206
+
</a>{" "}
207
+
for analytics. It is privacy-focused: no IP addresses, cookies, or
208
+
personal data is collected.
209
+
</p>
210
+
</Section>
211
+
212
+
<Section title="No Ads or Tracking">
213
+
<p>We do not serve ads, use third-party tracking, or sell user data.</p>
214
+
</Section>
215
+
216
+
<Section title="Children’s Privacy">
217
+
<p>Grain is not intended for users under 13 years of age.</p>
218
+
</Section>
219
+
220
+
<Section title="Changes to Policy">
221
+
<p>
222
+
This policy may be updated. Material changes will be communicated via
223
+
the app or site.
224
+
</p>
225
+
</Section>
226
+
227
+
<Section title="Contact">
228
+
<p>
229
+
For privacy questions, contact us at{" "}
230
+
<a
231
+
href="mailto:support@grain.social"
232
+
className="text-sky-500 hover:underline"
233
+
>
234
+
support@grain.social
235
+
</a>.
236
+
</p>
237
+
</Section>
238
+
</div>
239
+
);
240
+
}
241
+
242
+
export function CopyrightPolicy() {
243
+
return (
244
+
<div className="px-4 py-4">
245
+
<Breadcrumb
246
+
items={[{ label: "support", href: "/support" }, { label: "copyright" }]}
247
+
/>
248
+
<h1 className="text-3xl font-bold mb-6 text-zinc-900 dark:text-white">
249
+
Copyright Policy
250
+
</h1>
251
+
<div className="mb-6 text-sm text-zinc-900 dark:text-white">
252
+
Last Updated: June 3, 2025
253
+
</div>
254
+
<Section title="Copyright Infringement">
255
+
<p>
256
+
Grain respects the intellectual property rights of others and expects
257
+
users to do the same. If you believe your copyrighted work has been
258
+
used in a way that constitutes infringement, please notify us
259
+
promptly.
260
+
</p>
261
+
</Section>
262
+
263
+
<Section title="Notice Requirements">
264
+
<p>
265
+
Your infringement notice must include: (1) a description of the
266
+
copyrighted work, (2) the location of the infringing material, (3)
267
+
your contact information, (4) a statement that you believe in good
268
+
faith the use is not authorized, and (5) a statement, under penalty of
269
+
perjury, that the information is accurate.
270
+
</p>
271
+
</Section>
272
+
273
+
<Section title="DMCA Compliance">
274
+
<p>
275
+
Grain complies with the Digital Millennium Copyright Act (DMCA). If
276
+
you are a copyright holder and believe your rights have been violated,
277
+
you may file a DMCA notice with the required information to our
278
+
designated agent. We will promptly respond to all valid DMCA notices
279
+
and take appropriate action, including removal of the infringing
280
+
content and disabling access.
281
+
</p>
282
+
</Section>
283
+
284
+
<Section title="Repeat Infringers">
285
+
<p>
286
+
Accounts that repeatedly infringe copyright may be suspended or
287
+
removed in accordance with AT Protocol and Grain Social’s moderation
288
+
guidelines.
289
+
</p>
290
+
</Section>
291
+
292
+
<Section title="Contact">
293
+
<p>
294
+
To report infringement or submit a DMCA notice, contact us at{" "}
295
+
<a
296
+
href="mailto:support@grain.social"
297
+
className="text-sky-500 hover:underline"
298
+
>
299
+
support@grain.social
300
+
</a>.
301
+
</p>
302
+
</Section>
303
+
</div>
304
+
);
305
+
}
+149
-3
src/lib/actor.ts
+149
-3
src/lib/actor.ts
···
1
+
import { Record as BskyProfile } from "$lexicon/types/app/bsky/actor/profile.ts";
2
+
import { Record as TangledProfile } from "$lexicon/types/sh/tangled/actor/profile.ts";
1
3
import { ProfileView } from "$lexicon/types/social/grain/actor/defs.ts";
2
-
import { Record as Profile } from "$lexicon/types/social/grain/actor/profile.ts";
4
+
import { Record as GrainProfile } from "$lexicon/types/social/grain/actor/profile.ts";
5
+
import { Record as Favorite } from "$lexicon/types/social/grain/favorite.ts";
3
6
import { Record as Gallery } from "$lexicon/types/social/grain/gallery.ts";
4
7
import { Record as Photo } from "$lexicon/types/social/grain/photo.ts";
5
8
import { Record as PhotoExif } from "$lexicon/types/social/grain/photo/exif.ts";
···
7
10
import { BffContext, WithBffMeta } from "@bigmoves/bff";
8
11
import { galleryToView, getGalleryItemsAndPhotos } from "./gallery.ts";
9
12
import { photoToView } from "./photo.ts";
13
+
import type { SocialNetwork } from "./timeline.ts";
10
14
11
15
export function getActorProfile(did: string, ctx: BffContext) {
12
16
const actor = ctx.indexService.getActor(did);
13
17
if (!actor) return null;
14
-
const profileRecord = ctx.indexService.getRecord<WithBffMeta<Profile>>(
18
+
const profileRecord = ctx.indexService.getRecord<WithBffMeta<GrainProfile>>(
15
19
`at://${did}/social.grain.actor.profile/self`,
16
20
);
17
21
return profileRecord ? profileToView(profileRecord, actor.handle) : null;
18
22
}
19
23
20
24
export function profileToView(
21
-
record: WithBffMeta<Profile>,
25
+
record: WithBffMeta<GrainProfile>,
22
26
handle: string,
23
27
): Un$Typed<ProfileView> {
24
28
return {
···
34
38
35
39
export function getActorPhotos(handleOrDid: string, ctx: BffContext) {
36
40
let did: string;
41
+
37
42
if (handleOrDid.includes("did:")) {
38
43
did = handleOrDid;
39
44
} else {
···
41
46
if (!actor) return [];
42
47
did = actor.did;
43
48
}
49
+
44
50
const photos = ctx.indexService.getRecords<WithBffMeta<Photo>>(
45
51
"social.grain.photo",
46
52
{
···
66
72
67
73
export function getActorGalleries(handleOrDid: string, ctx: BffContext) {
68
74
let did: string;
75
+
69
76
if (handleOrDid.includes("did:")) {
70
77
did = handleOrDid;
71
78
} else {
···
73
80
if (!actor) return [];
74
81
did = actor.did;
75
82
}
83
+
76
84
const { items: galleries } = ctx.indexService.getRecords<
77
85
WithBffMeta<Gallery>
78
86
>("social.grain.gallery", {
79
87
where: [{ field: "did", equals: did }],
80
88
orderBy: [{ field: "createdAt", direction: "desc" }],
81
89
});
90
+
82
91
const galleryPhotosMap = getGalleryItemsAndPhotos(ctx, galleries);
83
92
const creator = getActorProfile(did, ctx);
93
+
84
94
if (!creator) return [];
95
+
85
96
return galleries.map((gallery) =>
86
97
galleryToView(gallery, creator, galleryPhotosMap.get(gallery.uri) ?? [])
87
98
);
88
99
}
100
+
101
+
export function getActorGalleryFavs(handleOrDid: string, ctx: BffContext) {
102
+
let did: string;
103
+
104
+
if (handleOrDid.includes("did:")) {
105
+
did = handleOrDid;
106
+
} else {
107
+
const actor = ctx.indexService.getActorByHandle(handleOrDid);
108
+
if (!actor) return [];
109
+
did = actor.did;
110
+
}
111
+
112
+
const { items: favRecords } = ctx.indexService.getRecords<
113
+
WithBffMeta<Favorite>
114
+
>(
115
+
"social.grain.favorite",
116
+
{
117
+
where: [{ field: "did", equals: did }],
118
+
orderBy: [{ field: "createdAt", direction: "desc" }],
119
+
},
120
+
);
121
+
122
+
if (!favRecords.length) return [];
123
+
124
+
const galleryUris = favRecords.map((fav) => fav.subject);
125
+
126
+
const { items: galleries } = ctx.indexService.getRecords<
127
+
WithBffMeta<Gallery>
128
+
>(
129
+
"social.grain.gallery",
130
+
{
131
+
where: [{ field: "uri", in: galleryUris }],
132
+
},
133
+
);
134
+
135
+
// Map gallery uri to gallery object for fast lookup
136
+
const galleryMap = new Map(galleries.map((g) => [g.uri, g]));
137
+
const galleryPhotosMap = getGalleryItemsAndPhotos(ctx, galleries);
138
+
const creators = new Map<string, ReturnType<typeof getActorProfile>>();
139
+
const uniqueDids = Array.from(
140
+
new Set(galleries.map((gallery) => gallery.did)),
141
+
);
142
+
143
+
const { items: profiles } = ctx.indexService.getRecords<
144
+
WithBffMeta<GrainProfile>
145
+
>(
146
+
"social.grain.actor.profile",
147
+
{
148
+
where: [{ field: "did", in: uniqueDids }],
149
+
},
150
+
);
151
+
152
+
for (const profile of profiles) {
153
+
const handle = ctx.indexService.getActor(profile.did)?.handle ?? "";
154
+
creators.set(profile.did, profileToView(profile, handle));
155
+
}
156
+
157
+
// Order galleries by the order of favRecords (favorited at)
158
+
return favRecords
159
+
.map((fav) => {
160
+
const gallery = galleryMap.get(fav.subject);
161
+
if (!gallery) return null;
162
+
const creator = creators.get(gallery.did);
163
+
if (!creator) return null;
164
+
return galleryToView(
165
+
gallery,
166
+
creator,
167
+
galleryPhotosMap.get(gallery.uri) ?? [],
168
+
);
169
+
})
170
+
.filter((g) => g !== null);
171
+
}
172
+
173
+
export function getActorProfiles(
174
+
handleOrDid: string,
175
+
ctx: BffContext,
176
+
): SocialNetwork[] {
177
+
let did: string;
178
+
179
+
if (handleOrDid.includes("did:")) {
180
+
did = handleOrDid;
181
+
} else {
182
+
const actor = ctx.indexService.getActorByHandle(handleOrDid);
183
+
if (!actor) return [];
184
+
did = actor.did;
185
+
}
186
+
187
+
const { items: grainProfiles } = ctx.indexService.getRecords<
188
+
WithBffMeta<GrainProfile>
189
+
>(
190
+
"social.grain.actor.profile",
191
+
{
192
+
where: {
193
+
AND: [
194
+
{ field: "did", equals: did },
195
+
{ field: "uri", contains: "self" },
196
+
],
197
+
},
198
+
},
199
+
);
200
+
201
+
const { items: tangledProfiles } = ctx.indexService.getRecords<
202
+
WithBffMeta<TangledProfile>
203
+
>(
204
+
"sh.tangled.actor.profile",
205
+
{
206
+
where: {
207
+
AND: [
208
+
{ field: "did", equals: did },
209
+
{ field: "uri", contains: "self" },
210
+
],
211
+
},
212
+
},
213
+
);
214
+
215
+
const { items: bskyProfiles } = ctx.indexService.getRecords<
216
+
WithBffMeta<BskyProfile>
217
+
>(
218
+
"app.bsky.actor.profile",
219
+
{
220
+
where: {
221
+
AND: [
222
+
{ field: "did", equals: did },
223
+
{ field: "uri", contains: "self" },
224
+
],
225
+
},
226
+
},
227
+
);
228
+
229
+
const profiles: SocialNetwork[] = [];
230
+
if (grainProfiles.length) profiles.push("grain");
231
+
if (bskyProfiles.length) profiles.push("bluesky");
232
+
if (tangledProfiles.length) profiles.push("tangled");
233
+
return profiles;
234
+
}
+74
-9
src/lib/follow.ts
+74
-9
src/lib/follow.ts
···
1
-
import { Record as BskyFollow } from "$lexicon/types/app/bsky/graph/follow.ts";
1
+
import { Record as GrainFollow } from "$lexicon/types/social/grain/graph/follow.ts";
2
2
import { BffContext, WithBffMeta } from "@bigmoves/bff";
3
+
import { getActorProfile } from "./actor.ts";
3
4
4
5
export function getFollow(
5
6
followeeDid: string,
···
8
9
) {
9
10
const {
10
11
items: [follow],
11
-
} = ctx.indexService.getRecords<WithBffMeta<BskyFollow>>(
12
-
"app.bsky.graph.follow",
12
+
} = ctx.indexService.getRecords<
13
+
WithBffMeta<GrainFollow>
14
+
>(
15
+
"social.grain.graph.follow",
13
16
{
14
-
where: [
15
-
{
17
+
where: {
18
+
AND: [{
16
19
field: "did",
17
20
equals: followerDid,
18
-
},
19
-
{
21
+
}, {
20
22
field: "subject",
21
23
equals: followeeDid,
22
-
},
23
-
],
24
+
}],
25
+
},
24
26
},
25
27
);
28
+
26
29
return follow;
27
30
}
31
+
32
+
export function getFollowers(
33
+
followeeDid: string,
34
+
ctx: BffContext,
35
+
): WithBffMeta<GrainFollow>[] {
36
+
const { items: followers } = ctx.indexService.getRecords<
37
+
WithBffMeta<GrainFollow>
38
+
>(
39
+
"social.grain.graph.follow",
40
+
{
41
+
orderBy: [{ field: "createdAt", direction: "desc" }],
42
+
where: [{
43
+
field: "subject",
44
+
equals: followeeDid,
45
+
}],
46
+
},
47
+
);
48
+
return followers;
49
+
}
50
+
51
+
export function getFollowing(
52
+
followerDid: string,
53
+
ctx: BffContext,
54
+
): WithBffMeta<GrainFollow>[] {
55
+
const { items: following } = ctx.indexService.getRecords<
56
+
WithBffMeta<GrainFollow>
57
+
>(
58
+
"social.grain.graph.follow",
59
+
{
60
+
orderBy: [{ field: "createdAt", direction: "desc" }],
61
+
where: [{
62
+
field: "did",
63
+
equals: followerDid,
64
+
}],
65
+
},
66
+
);
67
+
return following;
68
+
}
69
+
70
+
export function getFollowersWithProfiles(
71
+
followeeDid: string,
72
+
ctx: BffContext,
73
+
) {
74
+
const followers = getFollowers(followeeDid, ctx);
75
+
return followers
76
+
.map((follow) => getActorProfile(follow.did, ctx))
77
+
.filter((profile): profile is NonNullable<typeof profile> =>
78
+
profile != null
79
+
);
80
+
}
81
+
82
+
export function getFollowingWithProfiles(
83
+
followerDid: string,
84
+
ctx: BffContext,
85
+
) {
86
+
const following = getFollowing(followerDid, ctx);
87
+
return following
88
+
.map((follow) => getActorProfile(follow.subject, ctx))
89
+
.filter((profile): profile is NonNullable<typeof profile> =>
90
+
profile != null
91
+
);
92
+
}
+44
-7
src/lib/gallery.ts
+44
-7
src/lib/gallery.ts
···
11
11
isPhotoView,
12
12
PhotoView,
13
13
} from "$lexicon/types/social/grain/photo/defs.ts";
14
+
import { Record as PhotoExif } from "$lexicon/types/social/grain/photo/exif.ts";
14
15
import { Un$Typed } from "$lexicon/util.ts";
15
16
import { AtUri } from "@atproto/syntax";
16
17
import { BffContext, WithBffMeta } from "@bigmoves/bff";
17
18
import { getActorProfile } from "./actor.ts";
18
19
import { photoToView } from "./photo.ts";
19
20
21
+
type PhotoWithExif = WithBffMeta<Photo> & {
22
+
exif?: WithBffMeta<PhotoExif>;
23
+
};
24
+
20
25
export function getGalleryItemsAndPhotos(
21
26
ctx: BffContext,
22
27
galleries: WithBffMeta<Gallery>[],
23
-
): Map<string, WithBffMeta<Photo>[]> {
28
+
): Map<string, PhotoWithExif[]> {
24
29
const galleryUris = galleries.map(
25
30
(gallery) =>
26
31
`at://${gallery.did}/social.grain.gallery/${new AtUri(gallery.uri).rkey}`,
···
45
50
},
46
51
);
47
52
48
-
const photosMap = new Map<string, WithBffMeta<Photo>>();
53
+
const { items: photosExif } = ctx.indexService.getRecords<
54
+
WithBffMeta<PhotoExif>
55
+
>(
56
+
"social.grain.photo.exif",
57
+
{
58
+
where: [{ field: "photo", in: photoUris }],
59
+
},
60
+
);
61
+
62
+
const photosMap = new Map<string, PhotoWithExif>();
63
+
const exifMap = new Map<string, WithBffMeta<PhotoExif>>();
64
+
for (const exif of photosExif) {
65
+
exifMap.set(exif.photo, exif);
66
+
}
49
67
for (const photo of photos) {
50
-
photosMap.set(photo.uri, photo);
68
+
const exif = exifMap.get(photo.uri);
69
+
photosMap.set(photo.uri, exif ? { ...photo, exif } : photo);
51
70
}
52
71
53
-
const galleryPhotosMap = new Map<string, WithBffMeta<Photo>[]>();
72
+
const galleryPhotosMap = new Map<string, PhotoWithExif[]>();
54
73
for (const item of galleryItems) {
55
74
const galleryUri = item.gallery;
56
75
const photo = photosMap.get(item.item);
···
130
149
export function galleryToView(
131
150
record: WithBffMeta<Gallery>,
132
151
creator: Un$Typed<ProfileView>,
133
-
items: Photo[],
152
+
items: PhotoWithExif[],
134
153
): Un$Typed<GalleryView> {
135
154
return {
136
155
uri: record.uri,
···
147
166
function itemToView(
148
167
did: string,
149
168
item:
150
-
| WithBffMeta<Photo>
169
+
| PhotoWithExif
151
170
| {
152
171
$type: string;
153
172
},
154
173
): Un$Typed<PhotoView> | undefined {
155
174
if (isPhoto(item)) {
156
-
return photoToView(did, item);
175
+
return photoToView(did, item, item.exif);
157
176
}
158
177
return undefined;
159
178
}
179
+
180
+
export function getGalleryCameras(
181
+
gallery: GalleryView,
182
+
): string[] {
183
+
const photos = gallery.items?.filter(isPhotoView) ?? [];
184
+
const cameras = new Set<string>();
185
+
for (const photo of photos) {
186
+
if (photo.exif?.make) {
187
+
// Capitalize first letter of each word for make only, leave model raw
188
+
const make = photo.exif.make.charAt(0).toUpperCase() +
189
+
photo.exif.make.slice(1).toLowerCase();
190
+
const model = photo.exif.model ?? "";
191
+
console.log(make, model);
192
+
cameras.add(`${make} ${model}`.trim());
193
+
}
194
+
}
195
+
return Array.from(cameras);
196
+
}
+27
-14
src/lib/notifications.ts
+27
-14
src/lib/notifications.ts
···
1
1
import { ProfileView } from "$lexicon/types/social/grain/actor/defs.ts";
2
2
import { Record as Favorite } from "$lexicon/types/social/grain/favorite.ts";
3
+
import { Record as Follow } from "$lexicon/types/social/grain/graph/follow.ts";
3
4
import { NotificationView } from "$lexicon/types/social/grain/notification/defs.ts";
4
5
import { Un$Typed } from "$lexicon/util.ts";
5
6
import { ActorTable, BffContext, WithBffMeta } from "@bigmoves/bff";
6
7
import { getActorProfile } from "./actor.ts";
7
8
8
-
export type NotificationRecords = WithBffMeta<Favorite>;
9
+
export type NotificationRecords = WithBffMeta<Favorite | Follow>;
9
10
10
11
export function getNotifications(
11
12
currentUser: ActorTable,
···
13
14
) {
14
15
const { lastSeenNotifs } = currentUser;
15
16
const notifications = ctx.getNotifications<NotificationRecords>();
16
-
return notifications.map((notification) => {
17
-
const actor = ctx.indexService.getActor(notification.did);
18
-
const authorProfile = getActorProfile(notification.did, ctx);
19
-
if (!actor || !authorProfile) return null;
20
-
return notificationToView(
21
-
notification,
22
-
authorProfile,
23
-
lastSeenNotifs,
24
-
);
25
-
}).filter((view): view is Un$Typed<NotificationView> => Boolean(view));
17
+
return notifications
18
+
.filter(
19
+
(notification) =>
20
+
notification.$type === "social.grain.favorite" ||
21
+
notification.$type === "social.grain.graph.follow",
22
+
)
23
+
.map((notification) => {
24
+
const actor = ctx.indexService.getActor(notification.did);
25
+
const authorProfile = getActorProfile(notification.did, ctx);
26
+
if (!actor || !authorProfile) return null;
27
+
return notificationToView(
28
+
notification,
29
+
authorProfile,
30
+
lastSeenNotifs,
31
+
);
32
+
})
33
+
.filter((view): view is Un$Typed<NotificationView> => Boolean(view));
26
34
}
27
35
28
36
export function notificationToView(
···
30
38
author: Un$Typed<ProfileView>,
31
39
lastSeenNotifs: string | undefined,
32
40
): Un$Typed<NotificationView> {
33
-
const reason = record.$type === "social.grain.favorite"
34
-
? "gallery-favorite"
35
-
: "unknown";
41
+
let reason: string;
42
+
if (record.$type === "social.grain.favorite") {
43
+
reason = "gallery-favorite";
44
+
} else if (record.$type === "social.grain.graph.follow") {
45
+
reason = "follow";
46
+
} else {
47
+
reason = "unknown";
48
+
}
36
49
const reasonSubject = record.$type === "social.grain.favorite"
37
50
? record.subject
38
51
: undefined;
+88
-2
src/lib/photo.ts
+88
-2
src/lib/photo.ts
···
3
3
import { Record as PhotoExif } from "$lexicon/types/social/grain/photo/exif.ts";
4
4
import { $Typed } from "$lexicon/util.ts";
5
5
import { BffContext, WithBffMeta } from "@bigmoves/bff";
6
+
import { format, parseISO } from "date-fns";
6
7
import { PUBLIC_URL, USE_CDN } from "../env.ts";
7
8
8
9
export function getPhoto(
···
64
65
const deserializedExif = deserializeExif(exif);
65
66
return {
66
67
...deserializedExif,
68
+
fNumber: deserializedExif.fNumber
69
+
? formatAperture(deserializedExif.fNumber)
70
+
: undefined,
71
+
dateTimeOriginal: deserializedExif.dateTimeOriginal
72
+
? format(
73
+
parseISO(deserializedExif.dateTimeOriginal),
74
+
"MMM d, yyyy, h:mm a",
75
+
)
76
+
: undefined,
77
+
focalLengthIn35mmFormat: deserializedExif.focalLengthIn35mmFormat
78
+
? `${deserializedExif.focalLengthIn35mmFormat}mm`
79
+
: undefined,
80
+
exposureTime: deserializedExif.exposureTime !== undefined
81
+
? formatExposureTime(deserializedExif.exposureTime)
82
+
: undefined,
67
83
$type: "social.grain.photo.defs#exifView",
68
84
};
69
85
}
70
86
71
-
const EXIF_SCALE = 1000000;
87
+
function formatAperture(fNumber: number): string {
88
+
return `ƒ/${Number.isInteger(fNumber) ? fNumber : fNumber.toFixed(1)}`;
89
+
}
90
+
91
+
function formatExposureTime(seconds: number): string {
92
+
if (seconds >= 1) {
93
+
return `${seconds}s`;
94
+
}
95
+
96
+
const denominator = Math.round(1 / seconds);
97
+
return `1/${denominator}`;
98
+
}
99
+
100
+
const SCALE_FACTOR = 1000000;
72
101
73
102
export function deserializeExif(
74
103
exif: WithBffMeta<PhotoExif>,
75
-
scale: number = EXIF_SCALE,
104
+
scale: number = SCALE_FACTOR,
76
105
): WithBffMeta<PhotoExif> {
77
106
const deserialized: Partial<WithBffMeta<PhotoExif>> = {
78
107
$type: exif.$type,
···
99
128
100
129
return deserialized as WithBffMeta<PhotoExif>;
101
130
}
131
+
132
+
const exifDisplayNames: Record<string, string> = {
133
+
Make: "Make",
134
+
Model: "Model",
135
+
LensMake: "Lens Make",
136
+
LensModel: "Lens Model",
137
+
FNumber: "Aperture",
138
+
FocalLengthIn35mmFormat: "Focal Length",
139
+
ExposureTime: "Exposure Time",
140
+
ISO: "ISO",
141
+
Flash: "Flash",
142
+
DateTimeOriginal: "Date Taken",
143
+
};
144
+
145
+
const tagOrder = [
146
+
"Make",
147
+
"Model",
148
+
"LensMake",
149
+
"LensModel",
150
+
"FNumber",
151
+
"FocalLengthIn35mmFormat",
152
+
"ExposureTime",
153
+
"ISO",
154
+
"Flash",
155
+
"DateTimeOriginal",
156
+
];
157
+
158
+
export function getOrderedExifData(photo: PhotoView) {
159
+
const exif = photo.exif || {};
160
+
const entries = Object.entries(exif)
161
+
.filter(([key]) =>
162
+
tagOrder.some((tag) => tag.toLowerCase() === key.toLowerCase())
163
+
)
164
+
.map(([key, value]) => {
165
+
const tagKey = tagOrder.find(
166
+
(tag) => tag.toLowerCase() === key.toLowerCase(),
167
+
);
168
+
const displayKey = tagKey && exifDisplayNames[tagKey]
169
+
? exifDisplayNames[tagKey]
170
+
: key;
171
+
return { key, displayKey, value };
172
+
});
173
+
174
+
// Sort according to tagOrder, unknown tags go last in original order
175
+
return entries.sort((a, b) => {
176
+
const aIdx = tagOrder.findIndex(
177
+
(tag) => tag.toLowerCase() === a.key.toLowerCase(),
178
+
);
179
+
const bIdx = tagOrder.findIndex(
180
+
(tag) => tag.toLowerCase() === b.key.toLowerCase(),
181
+
);
182
+
if (aIdx === -1 && bIdx === -1) return 0;
183
+
if (aIdx === -1) return 1;
184
+
if (bIdx === -1) return -1;
185
+
return aIdx - bIdx;
186
+
});
187
+
}
+31
-118
src/lib/timeline.ts
+31
-118
src/lib/timeline.ts
···
1
1
import { Record as BskyFollow } from "$lexicon/types/app/bsky/graph/follow.ts";
2
+
import { Record as TangledFollow } from "$lexicon/types/sh/tangled/graph/follow.ts";
2
3
import { ProfileView } from "$lexicon/types/social/grain/actor/defs.ts";
3
-
import { Record as Favorite } from "$lexicon/types/social/grain/favorite.ts";
4
4
import { Record as Gallery } from "$lexicon/types/social/grain/gallery.ts";
5
5
import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
6
+
import { Record as GrainFollow } from "$lexicon/types/social/grain/graph/follow.ts";
6
7
import { Un$Typed } from "$lexicon/util.ts";
7
8
import { AtUri } from "@atproto/syntax";
8
9
import { BffContext, QueryOptions, WithBffMeta } from "@bigmoves/bff";
9
10
import { getActorProfile } from "./actor.ts";
10
11
import { galleryToView, getGalleryItemsAndPhotos } from "./gallery.ts";
11
12
12
-
type TimelineItemType = "gallery" | "favorite";
13
+
export type TimelineItemType = "gallery";
14
+
15
+
export type SocialNetwork = "bluesky" | "grain" | "tangled";
13
16
14
17
export type TimelineItem = {
15
18
createdAt: string;
···
34
37
? [{ field: "did", equals: options.actorDid }]
35
38
: undefined;
36
39
37
-
if (options?.followingDids && options.followingDids.size > 0) {
38
-
whereClause = [
39
-
...(whereClause ?? []),
40
-
{ field: "did", in: Array.from(options.followingDids) },
41
-
];
40
+
if (options?.followingDids) {
41
+
if (options.followingDids.size > 0) {
42
+
whereClause = [
43
+
...(whereClause ?? []),
44
+
{ field: "did", in: Array.from(options.followingDids) },
45
+
];
46
+
} else {
47
+
return [];
48
+
}
42
49
}
43
50
44
51
const { items: galleries } = ctx.indexService.getRecords<
···
77
84
return items;
78
85
}
79
86
80
-
function processFavs(
81
-
ctx: BffContext,
82
-
options?: TimelineOptions,
83
-
): TimelineItem[] {
84
-
const items: TimelineItem[] = [];
85
-
86
-
let whereClause: QueryOptions["where"] = options?.actorDid
87
-
? [{ field: "did", equals: options.actorDid }]
88
-
: undefined;
89
-
90
-
if (options?.followingDids && options.followingDids.size > 0) {
91
-
whereClause = [
92
-
...(whereClause ?? []),
93
-
{ field: "did", in: Array.from(options.followingDids) },
94
-
];
95
-
}
96
-
97
-
const { items: favs } = ctx.indexService.getRecords<WithBffMeta<Favorite>>(
98
-
"social.grain.favorite",
99
-
{
100
-
orderBy: [{ field: "createdAt", direction: "desc" }],
101
-
where: whereClause,
102
-
},
103
-
);
104
-
105
-
if (favs.length === 0) return items;
106
-
107
-
// Collect all gallery references from favorites
108
-
const galleryRefs = new Map<string, WithBffMeta<Gallery>>();
109
-
110
-
for (const favorite of favs) {
111
-
if (!favorite.subject) continue;
112
-
113
-
try {
114
-
const atUri = new AtUri(favorite.subject);
115
-
const galleryDid = atUri.hostname;
116
-
const galleryRkey = atUri.rkey;
117
-
const galleryUri =
118
-
`at://${galleryDid}/social.grain.gallery/${galleryRkey}`;
119
-
120
-
const gallery = ctx.indexService.getRecord<WithBffMeta<Gallery>>(
121
-
galleryUri,
122
-
);
123
-
if (gallery) {
124
-
galleryRefs.set(galleryUri, gallery);
125
-
}
126
-
} catch (e) {
127
-
console.error("Error processing favorite:", e);
128
-
}
129
-
}
130
-
131
-
const galleries = Array.from(galleryRefs.values());
132
-
const galleryPhotosMap = getGalleryItemsAndPhotos(ctx, galleries);
133
-
134
-
for (const favorite of favs) {
135
-
if (!favorite.subject) continue;
136
-
137
-
try {
138
-
const atUri = new AtUri(favorite.subject);
139
-
const galleryDid = atUri.hostname;
140
-
const galleryRkey = atUri.rkey;
141
-
const galleryUri =
142
-
`at://${galleryDid}/social.grain.gallery/${galleryRkey}`;
143
-
144
-
const gallery = galleryRefs.get(galleryUri);
145
-
if (!gallery) continue;
146
-
147
-
const galleryActor = ctx.indexService.getActor(galleryDid);
148
-
if (!galleryActor) continue;
149
-
const galleryProfile = getActorProfile(galleryActor.did, ctx);
150
-
if (!galleryProfile) continue;
151
-
152
-
const favActor = ctx.indexService.getActor(favorite.did);
153
-
if (!favActor) continue;
154
-
const favProfile = getActorProfile(favActor.did, ctx);
155
-
if (!favProfile) continue;
156
-
157
-
const galleryPhotos = galleryPhotosMap.get(galleryUri) || [];
158
-
const galleryView = galleryToView(gallery, galleryProfile, galleryPhotos);
159
-
160
-
items.push({
161
-
itemType: "favorite",
162
-
createdAt: favorite.createdAt,
163
-
itemUri: favorite.uri,
164
-
actor: favProfile,
165
-
gallery: galleryView,
166
-
});
167
-
} catch (e) {
168
-
console.error("Error processing favorite:", e);
169
-
continue;
170
-
}
171
-
}
172
-
173
-
return items;
174
-
}
175
-
176
87
function getTimelineItems(
177
88
ctx: BffContext,
178
89
options?: TimelineOptions,
179
90
): TimelineItem[] {
180
91
const galleryItems = processGalleries(ctx, options);
181
-
const favsItems = processFavs(ctx, options);
182
-
const timelineItems = [...galleryItems, ...favsItems];
183
-
184
-
return timelineItems.sort(
92
+
return galleryItems.sort(
185
93
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
186
94
);
187
95
}
188
96
189
-
function getFollowingDids(ctx: BffContext): Set<string> {
97
+
function getFollowingDids(type: SocialNetwork, ctx: BffContext): Set<string> {
190
98
if (!ctx.currentUser?.did) return new Set();
99
+
const typeToCollection: Record<SocialNetwork, string> = {
100
+
bluesky: "app.bsky.graph.follow",
101
+
grain: "social.grain.graph.follow",
102
+
tangled: "sh.tangled.graph.follow",
103
+
};
104
+
const collection = typeToCollection[type];
105
+
if (!collection) {
106
+
throw new Error(`Unsupported social graph type: ${type}`);
107
+
}
191
108
const { items: follows } = ctx.indexService.getRecords<
192
-
WithBffMeta<BskyFollow>
109
+
WithBffMeta<BskyFollow | GrainFollow | TangledFollow>
193
110
>(
194
-
"app.bsky.graph.follow",
111
+
collection,
195
112
{ where: [{ field: "did", equals: ctx.currentUser.did }] },
196
113
);
197
114
return new Set(follows.map((f) => f.subject).filter(Boolean));
···
199
116
200
117
export function getTimeline(
201
118
ctx: BffContext,
202
-
type: "timeline" | "following" = "timeline",
119
+
type: "timeline" | "following",
120
+
graph: SocialNetwork,
203
121
): TimelineItem[] {
204
122
let followingDids: Set<string> | undefined = undefined;
205
123
if (type === "following") {
206
-
followingDids = getFollowingDids(ctx);
124
+
followingDids = getFollowingDids(graph, ctx);
207
125
}
208
126
const galleryItems = processGalleries(ctx, { followingDids });
209
-
const favsItems = processFavs(
210
-
ctx,
211
-
followingDids ? { followingDids } : undefined,
212
-
);
213
-
const timelineItems = [...galleryItems, ...favsItems];
214
-
return timelineItems.sort(
127
+
return galleryItems.sort(
215
128
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
216
129
);
217
130
}
+21
-3
src/main.tsx
+21
-3
src/main.tsx
···
7
7
import * as actionHandlers from "./routes/actions.tsx";
8
8
import * as dialogHandlers from "./routes/dialogs.tsx";
9
9
import { handler as exploreHandler } from "./routes/explore.tsx";
10
+
import { handler as followersHandler } from "./routes/followers.tsx";
11
+
import { handler as followsHandler } from "./routes/follows.tsx";
10
12
import { handler as galleryHandler } from "./routes/gallery.tsx";
13
+
import * as legalHandlers from "./routes/legal.tsx";
11
14
import { handler as notificationsHandler } from "./routes/notifications.tsx";
12
15
import { handler as onboardHandler } from "./routes/onboard.tsx";
13
16
import { handler as profileHandler } from "./routes/profile.tsx";
14
17
import { handler as recordHandler } from "./routes/record.ts";
18
+
import { handler as supportHandler } from "./routes/support.tsx";
15
19
import { handler as timelineHandler } from "./routes/timeline.tsx";
16
20
import { handler as uploadHandler } from "./routes/upload.tsx";
17
21
import { appStateMiddleware, type State } from "./state.ts";
···
28
32
"social.grain.photo",
29
33
"social.grain.photo.exif",
30
34
"social.grain.favorite",
35
+
"social.grain.graph.follow",
31
36
],
32
37
externalCollections: [
33
38
"app.bsky.actor.profile",
34
39
"app.bsky.graph.follow",
40
+
"sh.tangled.actor.profile",
41
+
"sh.tangled.graph.follow",
35
42
],
36
43
jetstreamUrl: JETSTREAM.WEST_1,
37
44
lexicons,
···
55
62
route("/explore", exploreHandler),
56
63
route("/notifications", notificationsHandler),
57
64
route("/profile/:handle", profileHandler),
65
+
route("/profile/:handle/followers", followersHandler),
66
+
route("/profile/:handle/follows", followsHandler),
58
67
route("/profile/:handle/gallery/:rkey", galleryHandler),
59
68
route("/upload", uploadHandler),
60
69
route("/onboard", onboardHandler),
70
+
route("/support", supportHandler),
71
+
route("/support/privacy", legalHandlers.privacyHandler),
72
+
route("/support/terms", legalHandlers.termsHandler),
73
+
route("/support/copyright", legalHandlers.copyrightHandler),
74
+
route("/dialogs/create-account", dialogHandlers.createAccount),
61
75
route("/dialogs/gallery/new", dialogHandlers.createGallery),
62
76
route("/dialogs/gallery/:rkey", dialogHandlers.editGallery),
63
77
route("/dialogs/gallery/:rkey/sort", dialogHandlers.sortGallery),
···
68
82
route(
69
83
"/dialogs/photo/:rkey/exif",
70
84
dialogHandlers.photoExif,
85
+
),
86
+
route(
87
+
"/dialogs/photo/:rkey/exif-overlay",
88
+
dialogHandlers.photoExifOverlay,
71
89
),
72
90
route(
73
91
"/dialogs/photo-select/:galleryRkey",
74
92
dialogHandlers.galleryPhotoSelect,
75
93
),
76
94
route("/actions/update-seen", ["POST"], actionHandlers.updateSeen),
77
-
route("/actions/follow/:did", ["POST"], actionHandlers.follow),
95
+
route("/actions/follow/:followeeDid", ["POST"], actionHandlers.follow),
78
96
route(
79
97
"/actions/follow/:followeeDid/:rkey",
80
98
["DELETE"],
···
94
112
),
95
113
route("/actions/photo/:rkey", ["PUT"], actionHandlers.photoEdit),
96
114
route("/actions/photo/:rkey", ["DELETE"], actionHandlers.photoDelete),
115
+
route("/actions/photo", ["POST"], actionHandlers.uploadPhoto),
97
116
route("/actions/favorite", ["POST"], actionHandlers.galleryFavorite),
98
-
route("/actions/profile/update", ["POST"], actionHandlers.profileUpdate),
117
+
route("/actions/profile", ["PUT"], actionHandlers.profileUpdate),
99
118
route(
100
119
"/actions/gallery/:rkey/sort",
101
120
["POST"],
102
121
actionHandlers.gallerySort,
103
122
),
104
123
route("/actions/get-blob", ["GET"], actionHandlers.getBlob),
105
-
route("/actions/photo/upload", ["POST"], actionHandlers.uploadPhoto),
106
124
route("/:did/:collection/:rkey", recordHandler),
107
125
],
108
126
});
+62
-33
src/routes/actions.tsx
+62
-33
src/routes/actions.tsx
···
10
10
import { BffContext, RouteHandler, WithBffMeta } from "@bigmoves/bff";
11
11
import { FavoriteButton } from "../components/FavoriteButton.tsx";
12
12
import { FollowButton } from "../components/FollowButton.tsx";
13
-
import { PhotoButton } from "../components/PhotoButton.tsx";
13
+
import { GalleryInfo } from "../components/GalleryInfo.tsx";
14
+
import { GalleryLayout } from "../components/GalleryLayout.tsx";
14
15
import { PhotoPreview } from "../components/PhotoPreview.tsx";
15
16
import { PhotoSelectButton } from "../components/PhotoSelectButton.tsx";
17
+
import { getFollowers } from "../lib/follow.ts";
16
18
import { deleteGallery, getGallery, getGalleryFavs } from "../lib/gallery.ts";
17
-
import { photoToView } from "../lib/photo.ts";
19
+
import { getPhoto, photoToView } from "../lib/photo.ts";
18
20
import type { State } from "../state.ts";
19
21
import { galleryLink } from "../utils.ts";
20
22
···
34
36
ctx: BffContext<State>,
35
37
) => {
36
38
ctx.requireAuth();
37
-
const did = params.did;
38
-
if (!did) return ctx.next();
39
+
const followeeDid = params.followeeDid;
40
+
if (!followeeDid) return ctx.next();
39
41
const followUri = await ctx.createRecord<BskyFollow>(
40
-
"app.bsky.graph.follow",
42
+
"social.grain.graph.follow",
41
43
{
42
-
subject: did,
44
+
subject: followeeDid,
43
45
createdAt: new Date().toISOString(),
44
46
},
45
47
);
46
-
return ctx.html(<FollowButton followeeDid={did} followUri={followUri} />);
48
+
const followers = getFollowers(followeeDid, ctx);
49
+
return ctx.html(
50
+
<>
51
+
<div hx-swap-oob="innerHTML:#followers-count">
52
+
{followers.length}
53
+
</div>
54
+
<FollowButton followeeDid={followeeDid} followUri={followUri} />
55
+
</>,
56
+
);
47
57
};
48
58
49
59
export const unfollow: RouteHandler = async (
···
55
65
const followeeDid = params.followeeDid;
56
66
const rkey = params.rkey;
57
67
await ctx.deleteRecord(
58
-
`at://${did}/app.bsky.graph.follow/${rkey}`,
68
+
`at://${did}/social.grain.graph.follow/${rkey}`,
59
69
);
70
+
const followers = getFollowers(followeeDid, ctx);
60
71
return ctx.html(
61
-
<FollowButton followeeDid={followeeDid} followUri={undefined} />,
72
+
<>
73
+
<div hx-swap-oob="innerHTML:#followers-count">
74
+
{followers.length}
75
+
</div>
76
+
<FollowButton followeeDid={followeeDid} followUri={undefined} />
77
+
</>,
62
78
);
63
79
};
64
80
···
129
145
const galleryUri = `at://${did}/social.grain.gallery/${galleryRkey}`;
130
146
const photoUri = `at://${did}/social.grain.photo/${photoRkey}`;
131
147
const gallery = getGallery(did, galleryRkey, ctx);
132
-
const photo = ctx.indexService.getRecord<WithBffMeta<Photo>>(photoUri);
148
+
const photo = getPhoto(photoUri, ctx);
133
149
if (!gallery || !photo) return ctx.next();
134
150
if (
135
151
gallery.items
···
146
162
});
147
163
gallery.items = [
148
164
...(gallery.items ?? []),
149
-
photoToView(photo.did, photo),
165
+
photo,
150
166
];
151
167
return ctx.html(
152
168
<>
153
-
<div hx-swap-oob="beforeend:#masonry-container">
154
-
<PhotoButton
169
+
<div hx-swap-oob="beforeend:#gallery-container">
170
+
<GalleryLayout.Item
155
171
key={photo.cid}
156
-
photo={photoToView(photo.did, photo)}
172
+
photo={photo}
157
173
gallery={gallery}
158
174
/>
159
175
</div>
176
+
<div hx-swap-oob="outerHTML:#gallery-info">
177
+
<GalleryInfo gallery={gallery} />
178
+
</div>
160
179
<PhotoSelectButton
161
180
galleryUri={galleryUri}
162
181
itemUris={gallery.items?.filter(isPhotoView).map((item) => item.uri) ??
163
182
[]}
164
-
photo={photoToView(photo.did, photo)}
183
+
photo={photo}
165
184
/>
166
185
</>,
167
186
);
···
202
221
const gallery = getGallery(did, galleryRkey, ctx);
203
222
if (!gallery) return ctx.next();
204
223
return ctx.html(
205
-
<PhotoSelectButton
206
-
galleryUri={galleryUri}
207
-
itemUris={gallery.items?.filter(isPhotoView).map((item) => item.uri) ??
208
-
[]}
209
-
photo={photoToView(photo.did, photo)}
210
-
/>,
224
+
<>
225
+
<div hx-swap-oob="outerHTML:#gallery-info">
226
+
<GalleryInfo gallery={gallery} />
227
+
</div>
228
+
<PhotoSelectButton
229
+
galleryUri={galleryUri}
230
+
itemUris={gallery.items?.filter(isPhotoView).map((item) => item.uri) ??
231
+
[]}
232
+
photo={photoToView(photo.did, photo)}
233
+
/>
234
+
</>,
211
235
);
212
236
};
213
237
···
498
522
if (exifJsonString) {
499
523
try {
500
524
exif = JSON.parse(exifJsonString);
501
-
console.log("Parsed EXIF data:", exif);
502
525
} catch (e) {
503
526
console.error("Failed to parse EXIF data:", e);
504
527
}
···
530
553
createdAt: new Date().toISOString(),
531
554
});
532
555
533
-
const exifUri = await ctx.createRecord<PhotoExif>(
534
-
"social.grain.photo.exif",
535
-
{
536
-
photo: photoUri,
537
-
createdAt: new Date().toISOString(),
538
-
...exif,
539
-
},
540
-
);
556
+
let exifUri: string | undefined = undefined;
557
+
if (exif) {
558
+
exifUri = await ctx.createRecord<PhotoExif>(
559
+
"social.grain.photo.exif",
560
+
{
561
+
photo: photoUri,
562
+
createdAt: new Date().toISOString(),
563
+
...exif,
564
+
},
565
+
);
566
+
}
541
567
542
568
const photo = ctx.indexService.getRecord<WithBffMeta<Photo>>(photoUri);
543
569
if (!photo) {
544
570
return new Response("Photo not found after creation", { status: 404 });
545
571
}
546
572
547
-
const exifRecord = ctx.indexService.getRecord<WithBffMeta<PhotoExif>>(
548
-
exifUri,
549
-
);
573
+
let exifRecord: WithBffMeta<PhotoExif> | undefined = undefined;
574
+
if (exifUri) {
575
+
exifRecord = ctx.indexService.getRecord<WithBffMeta<PhotoExif>>(
576
+
exifUri,
577
+
);
578
+
}
550
579
551
580
return ctx.html(
552
581
<PhotoPreview
+25
src/routes/dialogs.tsx
+25
src/routes/dialogs.tsx
···
6
6
import { BffContext, RouteHandler, WithBffMeta } from "@bigmoves/bff";
7
7
import { wrap } from "popmotion";
8
8
import { AvatarDialog } from "../components/AvatarDialog.tsx";
9
+
import { CreateAccountDialog } from "../components/CreateAccountDialog.tsx";
10
+
import { ExifOverlayDialog } from "../components/ExifOverlayDialog.tsx";
9
11
import { GalleryCreateEditDialog } from "../components/GalleryCreateEditDialog.tsx";
10
12
import { GallerySortDialog } from "../components/GallerySortDialog.tsx";
11
13
import { PhotoAltDialog } from "../components/PhotoAltDialog.tsx";
···
146
148
);
147
149
};
148
150
151
+
export const photoExifOverlay: RouteHandler = (
152
+
_req,
153
+
params,
154
+
ctx: BffContext<State>,
155
+
) => {
156
+
const { did } = ctx.requireAuth();
157
+
const photoRkey = params.rkey;
158
+
const photoUri = `at://${did}/social.grain.photo/${photoRkey}`;
159
+
const photo = getPhoto(photoUri, ctx);
160
+
if (!photo) return ctx.next();
161
+
return ctx.html(
162
+
<ExifOverlayDialog photo={photo} />,
163
+
);
164
+
};
165
+
149
166
export const galleryPhotoSelect: RouteHandler = (
150
167
_req,
151
168
params,
···
169
186
/>,
170
187
);
171
188
};
189
+
190
+
export const createAccount: RouteHandler = (
191
+
_req,
192
+
_params,
193
+
ctx: BffContext<State>,
194
+
) => {
195
+
return ctx.html(<CreateAccountDialog />);
196
+
};
+44
src/routes/followers.tsx
+44
src/routes/followers.tsx
···
1
+
import { BffContext, RouteHandler } from "@bigmoves/bff";
2
+
import { Breadcrumb } from "../components/Breadcrumb.tsx";
3
+
import { FollowsList } from "../components/FollowsList.tsx";
4
+
import { Header } from "../components/Header.tsx";
5
+
import { getActorProfile } from "../lib/actor.ts";
6
+
import { getFollowersWithProfiles } from "../lib/follow.ts";
7
+
import { State } from "../state.ts";
8
+
import { profileLink } from "../utils.ts";
9
+
10
+
export const handler: RouteHandler = (
11
+
_req,
12
+
params,
13
+
ctx: BffContext<State>,
14
+
) => {
15
+
const handle = params.handle;
16
+
if (!handle) return ctx.next();
17
+
18
+
const actor = ctx.indexService.getActorByHandle(handle);
19
+
20
+
if (!actor) return ctx.next();
21
+
22
+
const profile = getActorProfile(actor?.did, ctx);
23
+
24
+
if (!actor) return ctx.next();
25
+
26
+
const followers = getFollowersWithProfiles(actor.did, ctx);
27
+
28
+
ctx.state.meta = [{ title: `People following @${handle} — Grain` }];
29
+
30
+
return ctx.render(
31
+
<div class="p-4">
32
+
<Breadcrumb
33
+
items={[{ label: "profile", href: profileLink(handle) }, {
34
+
label: "followers",
35
+
}]}
36
+
/>
37
+
<Header>{profile?.displayName}</Header>
38
+
<p class="mb-6 text-zinc-600 dark:text-zinc-500">
39
+
{followers.length ?? 0} followers
40
+
</p>
41
+
<FollowsList profiles={followers} />
42
+
</div>,
43
+
);
44
+
};
+44
src/routes/follows.tsx
+44
src/routes/follows.tsx
···
1
+
import { BffContext, RouteHandler } from "@bigmoves/bff";
2
+
import { Breadcrumb } from "../components/Breadcrumb.tsx";
3
+
import { FollowsList } from "../components/FollowsList.tsx";
4
+
import { Header } from "../components/Header.tsx";
5
+
import { getActorProfile } from "../lib/actor.ts";
6
+
import { getFollowingWithProfiles } from "../lib/follow.ts";
7
+
import { State } from "../state.ts";
8
+
import { profileLink } from "../utils.ts";
9
+
10
+
export const handler: RouteHandler = (
11
+
_req,
12
+
params,
13
+
ctx: BffContext<State>,
14
+
) => {
15
+
const handle = params.handle;
16
+
if (!handle) return ctx.next();
17
+
18
+
const actor = ctx.indexService.getActorByHandle(handle);
19
+
20
+
if (!actor) return ctx.next();
21
+
22
+
const profile = getActorProfile(actor?.did, ctx);
23
+
24
+
if (!actor) return ctx.next();
25
+
26
+
const following = getFollowingWithProfiles(actor.did, ctx);
27
+
28
+
ctx.state.meta = [{ title: `People followed by @${handle} — Grain` }];
29
+
30
+
return ctx.render(
31
+
<div class="p-4">
32
+
<Breadcrumb
33
+
items={[{ label: "profile", href: profileLink(handle) }, {
34
+
label: "following",
35
+
}]}
36
+
/>
37
+
<Header>{profile?.displayName}</Header>
38
+
<p class="mb-6 text-zinc-600 dark:text-zinc-500">
39
+
{following.length ?? 0} following
40
+
</p>
41
+
<FollowsList profiles={following} />
42
+
</div>,
43
+
);
44
+
};
+5
src/routes/gallery.tsx
+5
src/routes/gallery.tsx
···
17
17
const handle = params.handle;
18
18
const rkey = params.rkey;
19
19
const gallery = getGallery(handle, rkey, ctx);
20
+
20
21
if (!gallery) return ctx.next();
22
+
21
23
favs = getGalleryFavs(gallery.uri, ctx);
24
+
22
25
ctx.state.meta = [
23
26
{ title: `${(gallery.record as Gallery).title} — Grain` },
24
27
...getPageMeta(galleryLink(handle, rkey)),
25
28
...getGalleryMeta(gallery),
26
29
];
30
+
27
31
ctx.state.scripts = ["photo_dialog.js", "masonry.js", "sortable.js"];
32
+
28
33
return ctx.render(
29
34
<GalleryPage favs={favs} gallery={gallery} currentUserDid={did} />,
30
35
);
+36
src/routes/legal.tsx
+36
src/routes/legal.tsx
···
1
+
import { BffContext, RouteHandler } from "@bigmoves/bff";
2
+
import { CopyrightPolicy, PrivacyPolicy, Terms } from "../legal.tsx";
3
+
import type { State } from "../state.ts";
4
+
5
+
export const termsHandler: RouteHandler = (
6
+
_req,
7
+
_params,
8
+
ctx: BffContext<State>,
9
+
) => {
10
+
ctx.state.meta = [{ title: "Terms — Grain" }];
11
+
return ctx.render(
12
+
<Terms />,
13
+
);
14
+
};
15
+
16
+
export const privacyHandler: RouteHandler = (
17
+
_req,
18
+
_params,
19
+
ctx: BffContext<State>,
20
+
) => {
21
+
ctx.state.meta = [{ title: "Privacy Policy — Grain" }];
22
+
return ctx.render(
23
+
<PrivacyPolicy />,
24
+
);
25
+
};
26
+
27
+
export const copyrightHandler: RouteHandler = (
28
+
_req,
29
+
_params,
30
+
ctx: BffContext<State>,
31
+
) => {
32
+
ctx.state.meta = [{ title: "Copyright Policy — Grain" }];
33
+
return ctx.render(
34
+
<CopyrightPolicy />,
35
+
);
36
+
};
+49
-19
src/routes/profile.tsx
+49
-19
src/routes/profile.tsx
···
1
-
import { Record as BskyFollow } from "$lexicon/types/app/bsky/graph/follow.ts";
2
-
import { BffContext, RouteHandler, WithBffMeta } from "@bigmoves/bff";
3
-
import { ProfilePage } from "../components/ProfilePage.tsx";
4
-
import { getActorGalleries, getActorProfile } from "../lib/actor.ts";
5
-
import { getFollow } from "../lib/follow.ts";
6
-
import { getActorTimeline } from "../lib/timeline.ts";
1
+
import { BffContext, RouteHandler } from "@bigmoves/bff";
2
+
import { ProfilePage, ProfileTabs } from "../components/ProfilePage.tsx";
3
+
import {
4
+
getActorGalleries,
5
+
getActorGalleryFavs,
6
+
getActorProfile,
7
+
getActorProfiles,
8
+
} from "../lib/actor.ts";
9
+
import { getFollow, getFollowers, getFollowing } from "../lib/follow.ts";
10
+
import { type SocialNetwork } from "../lib/timeline.ts";
7
11
import { getPageMeta } from "../meta.ts";
8
12
import type { State } from "../state.ts";
9
13
import { profileLink } from "../utils.ts";
···
14
18
ctx: BffContext<State>,
15
19
) => {
16
20
const url = new URL(req.url);
17
-
const tab = url.searchParams.get("tab");
21
+
const tab = url.searchParams.get("tab") as ProfileTabs;
18
22
const handle = params.handle;
19
-
const timelineItems = getActorTimeline(handle, ctx);
20
-
const galleries = getActorGalleries(handle, ctx);
21
23
const actor = ctx.indexService.getActorByHandle(handle);
24
+
const isHxRequest = req.headers.get("hx-request") !== null;
25
+
const render = isHxRequest ? ctx.html : ctx.render;
26
+
22
27
if (!actor) return ctx.next();
28
+
23
29
const profile = getActorProfile(actor.did, ctx);
30
+
const galleries = getActorGalleries(handle, ctx);
31
+
const followers = getFollowers(actor.did, ctx);
32
+
const following = getFollowing(actor.did, ctx);
33
+
24
34
if (!profile) return ctx.next();
25
-
let follow: WithBffMeta<BskyFollow> | undefined;
35
+
36
+
let followUri: string | undefined;
37
+
let actorProfiles: SocialNetwork[] = [];
38
+
let userProfiles: SocialNetwork[] = [];
39
+
26
40
if (ctx.currentUser) {
27
-
follow = getFollow(profile.did, ctx.currentUser.did, ctx);
41
+
followUri = getFollow(profile.did, ctx.currentUser.did, ctx)?.uri;
42
+
actorProfiles = getActorProfiles(ctx.currentUser.did, ctx);
28
43
}
44
+
45
+
userProfiles = getActorProfiles(handle, ctx);
46
+
29
47
ctx.state.meta = [
30
48
{
31
49
title: profile.displayName
···
34
52
},
35
53
...getPageMeta(profileLink(handle)),
36
54
];
55
+
37
56
ctx.state.scripts = ["photo_manip.js", "profile_dialog.js"];
38
-
if (tab) {
39
-
return ctx.html(
57
+
58
+
if (tab === "favs") {
59
+
const galleryFavs = getActorGalleryFavs(handle, ctx);
60
+
return render(
40
61
<ProfilePage
41
-
followUri={follow?.uri}
62
+
followersCount={followers.length}
63
+
followingCount={following.length}
64
+
userProfiles={userProfiles}
65
+
actorProfiles={actorProfiles}
66
+
followUri={followUri}
42
67
loggedInUserDid={ctx.currentUser?.did}
43
-
timelineItems={timelineItems}
44
68
profile={profile}
45
-
selectedTab={tab}
69
+
selectedTab="favs"
46
70
galleries={galleries}
71
+
galleryFavs={galleryFavs}
47
72
/>,
48
73
);
49
74
}
50
-
return ctx.render(
75
+
return render(
51
76
<ProfilePage
52
-
followUri={follow?.uri}
77
+
followersCount={followers.length}
78
+
followingCount={following.length}
79
+
userProfiles={userProfiles}
80
+
actorProfiles={actorProfiles}
81
+
followUri={followUri}
53
82
loggedInUserDid={ctx.currentUser?.did}
54
-
timelineItems={timelineItems}
55
83
profile={profile}
84
+
selectedTab="galleries"
85
+
galleries={galleries}
56
86
/>,
57
87
);
58
88
};
+33
src/routes/support.tsx
+33
src/routes/support.tsx
···
1
+
import { RouteHandler } from "@bigmoves/bff";
2
+
3
+
export const handler: RouteHandler = (_req, _params, ctx) => {
4
+
ctx.state.meta = [{ title: "Support — Grain" }];
5
+
return ctx.render(
6
+
<div className="px-4 py-4">
7
+
<h1 className="text-3xl font-bold mb-4 text-zinc-900 dark:text-white">
8
+
Support
9
+
</h1>
10
+
<p className="mb-4 text-zinc-700 dark:text-zinc-300">
11
+
For help, questions, or to report issues, please email us at{" "}
12
+
<a
13
+
href="mailto:support@grain.social"
14
+
className="text-sky-500 hover:underline"
15
+
>
16
+
support@grain.social
17
+
</a>.
18
+
</p>
19
+
<p className="mb-2 text-zinc-700 dark:text-zinc-300">
20
+
You can also review our{" "}
21
+
<a href="/support/terms" className="text-sky-500 hover:underline">
22
+
Terms
23
+
</a>,{" "}
24
+
<a href="/support/privacy" className="text-sky-500 hover:underline">
25
+
Privacy Policy
26
+
</a>, and{" "}
27
+
<a href="/support/copyright" className="text-sky-500 hover:underline">
28
+
Copyright Policy
29
+
</a>.
30
+
</p>
31
+
</div>,
32
+
);
33
+
};
+58
-25
src/routes/timeline.tsx
+58
-25
src/routes/timeline.tsx
···
1
1
import { BffContext, RouteHandler } from "@bigmoves/bff";
2
2
import { getCookies, setCookie } from "@std/http";
3
3
import { Timeline } from "../components/Timeline.tsx";
4
-
import { getTimeline } from "../lib/timeline.ts";
4
+
import { getActorProfiles } from "../lib/actor.ts";
5
+
import { getTimeline, SocialNetwork } from "../lib/timeline.ts";
5
6
import { getPageMeta } from "../meta.ts";
6
7
import type { State } from "../state.ts";
7
8
···
12
13
) => {
13
14
const url = new URL(req.url);
14
15
const tabSearchParam = url.searchParams.get("tab") || "";
15
-
const cookieState = getCookieState(req.headers);
16
+
const graphSearchParam = url.searchParams.get("graph") as SocialNetwork ||
17
+
"grain";
18
+
const cookieState = getCookieState(ctx?.currentUser?.did, req.headers);
19
+
const isHxRequest = req.headers.get("hx-request") !== null;
20
+
const render = isHxRequest ? ctx.html : ctx.render;
21
+
16
22
let tab;
23
+
let graph: SocialNetwork = "grain";
17
24
let headers: Record<string, string> = {};
18
25
26
+
const actorProfiles = getActorProfiles(ctx?.currentUser?.did ?? "", ctx);
27
+
19
28
if (!ctx.currentUser) {
20
29
tab = "";
21
-
} else if (!req.headers.get("hx-request")) {
30
+
} else if (!isHxRequest) {
22
31
tab = cookieState.lastSelectedHomeFeed || "";
32
+
graph = cookieState.lastSelectedFollowGraph || "grain";
23
33
} else {
24
34
tab = tabSearchParam || "";
35
+
graph = graphSearchParam;
25
36
headers = setCookieState(url.hostname, {
37
+
did: ctx.currentUser.did,
26
38
lastSelectedHomeFeed: tab,
39
+
lastSelectedFollowGraph: graph,
27
40
});
28
41
}
29
42
43
+
if (!graph && actorProfiles.length > 0) {
44
+
graph = actorProfiles[0];
45
+
}
46
+
30
47
const items = getTimeline(
31
48
ctx,
32
49
tab === "following" ? "following" : "timeline",
50
+
graph,
33
51
);
34
52
35
53
if (tab === "following") {
36
-
if (!req.headers.get("hx-request")) {
37
-
ctx.state.meta = [{ title: "Following — Grain" }, ...getPageMeta("")];
38
-
return ctx.render(
39
-
<Timeline
40
-
isLoggedIn={!!ctx.currentUser}
41
-
selectedTab={tab}
42
-
items={items}
43
-
/>,
44
-
headers,
45
-
);
46
-
}
47
-
return ctx.html(
54
+
ctx.state.meta = [{ title: "Following — Grain" }, ...getPageMeta("")];
55
+
return render(
48
56
<Timeline
49
57
isLoggedIn={!!ctx.currentUser}
50
58
selectedTab={tab}
51
59
items={items}
60
+
selectedGraph={graph}
61
+
actorProfiles={actorProfiles}
52
62
/>,
53
63
headers,
54
64
);
···
56
66
57
67
ctx.state.meta = [{ title: "Timeline — Grain" }, ...getPageMeta("")];
58
68
59
-
return ctx.render(
60
-
<Timeline isLoggedIn={!!ctx.currentUser} selectedTab={tab} items={items} />,
69
+
return render(
70
+
<Timeline
71
+
isLoggedIn={!!ctx.currentUser}
72
+
selectedTab={tab}
73
+
items={items}
74
+
selectedGraph={graph}
75
+
actorProfiles={actorProfiles}
76
+
/>,
61
77
headers,
62
78
);
63
79
};
64
80
65
81
type GrainStorageState = {
82
+
did?: string;
66
83
lastSelectedHomeFeed?: string;
67
-
};
68
-
69
-
const defaultGrainStorageState: GrainStorageState = {
70
-
lastSelectedHomeFeed: undefined,
84
+
lastSelectedFollowGraph?: SocialNetwork;
71
85
};
72
86
73
87
function setCookieState(
···
92
106
}
93
107
94
108
function getCookieState(
109
+
did: string | undefined,
95
110
headers: Headers,
96
111
): GrainStorageState {
97
112
const cookies = getCookies(headers);
98
113
if (!cookies.grain_storage) {
99
-
return defaultGrainStorageState;
114
+
return createDefaultCookieState(did);
100
115
}
101
116
const grainStorage = atob(cookies.grain_storage);
102
117
if (grainStorage) {
103
118
try {
104
-
return JSON.parse(grainStorage);
119
+
const parsed = JSON.parse(grainStorage);
120
+
if (parsed.did && parsed.did !== did) {
121
+
// If the did in the cookie doesn't match the current user, reset the state
122
+
return createDefaultCookieState(did);
123
+
}
124
+
if (!parsed.did && did) {
125
+
return createDefaultCookieState(did);
126
+
}
127
+
return parsed as GrainStorageState;
105
128
} catch {
106
-
return defaultGrainStorageState;
129
+
return createDefaultCookieState(did);
107
130
}
108
131
}
109
-
return defaultGrainStorageState;
132
+
return createDefaultCookieState(did);
133
+
}
134
+
135
+
function createDefaultCookieState(
136
+
did: string | undefined,
137
+
): GrainStorageState {
138
+
return {
139
+
did,
140
+
lastSelectedHomeFeed: "",
141
+
lastSelectedFollowGraph: "grain",
142
+
};
110
143
}
+241
src/static/gallery_layout.ts
+241
src/static/gallery_layout.ts
···
1
+
type LayoutMode = "justified" | "masonry";
2
+
3
+
type GalleryItem = HTMLElement & {
4
+
dataset: {
5
+
width: string;
6
+
height: string;
7
+
[key: string]: string;
8
+
};
9
+
};
10
+
11
+
interface GalleryLayoutOptions {
12
+
containerSelector?: string;
13
+
layoutMode?: LayoutMode;
14
+
spacing?: number;
15
+
masonryBreakpoint?: number;
16
+
}
17
+
18
+
/**
19
+
* GalleryLayout class for flexible photo gallery layouts (masonry/justified).
20
+
*
21
+
* Example usage with the GalleryLayout compositional component:
22
+
*
23
+
* // In your JSX component:
24
+
* <GalleryLayout
25
+
* layoutButtons={
26
+
* <>
27
+
* <GalleryLayout.ModeButton mode="justified" />
28
+
* <GalleryLayout.ModeButton mode="masonry" />
29
+
* </>
30
+
* }
31
+
* >
32
+
* <GalleryLayout.Container>
33
+
* {photos.map(photo => (
34
+
* <GalleryLayout.Item key={photo.cid} photo={photo} gallery={gallery} />
35
+
* ))}
36
+
* </GalleryLayout.Container>
37
+
* </GalleryLayout>
38
+
*
39
+
* // In your static JS/TS:
40
+
* import { GalleryLayout } from "../static/gallery_layout.ts";
41
+
* const galleryLayout = new GalleryLayout({
42
+
* containerSelector: "#gallery-container",
43
+
* layoutMode: "justified",
44
+
* spacing: 8,
45
+
* masonryBreakpoint: 640,
46
+
* });
47
+
* galleryLayout.init();
48
+
*/
49
+
export class GalleryLayout {
50
+
private observerInitialized = false;
51
+
private layoutMode: LayoutMode;
52
+
private containerSelector: string;
53
+
private spacing: number;
54
+
private masonryBreakpoint: number;
55
+
56
+
constructor(options: GalleryLayoutOptions = {}) {
57
+
this.layoutMode = options.layoutMode ?? "justified";
58
+
this.containerSelector = options.containerSelector ?? "#gallery-container";
59
+
this.spacing = options.spacing ?? 8;
60
+
this.masonryBreakpoint = options.masonryBreakpoint ?? 640;
61
+
}
62
+
63
+
public setLayoutMode(mode: LayoutMode) {
64
+
this.layoutMode = mode;
65
+
this.computeLayout();
66
+
}
67
+
68
+
public computeLayout(): void {
69
+
if (this.layoutMode === "masonry") {
70
+
this.computeMasonry();
71
+
} else {
72
+
this.computeJustified();
73
+
}
74
+
}
75
+
76
+
public computeMasonry(): void {
77
+
const container = document.querySelector<HTMLElement>(
78
+
this.containerSelector,
79
+
);
80
+
if (!container) return;
81
+
82
+
const spacing = this.spacing;
83
+
const containerWidth = container.offsetWidth;
84
+
85
+
if (containerWidth === 0) {
86
+
requestAnimationFrame(() => this.computeMasonry());
87
+
return;
88
+
}
89
+
90
+
const columns = containerWidth < this.masonryBreakpoint ? 1 : 3;
91
+
const columnWidth = (containerWidth + spacing) / columns - spacing;
92
+
const columnHeights: number[] = new Array(columns).fill(0);
93
+
const tiles = container.querySelectorAll<HTMLElement>(".gallery-item");
94
+
95
+
tiles.forEach((tile) => {
96
+
const imgW = parseFloat((tile as GalleryItem).dataset.width);
97
+
const imgH = parseFloat((tile as GalleryItem).dataset.height);
98
+
if (!imgW || !imgH) return;
99
+
100
+
const aspectRatio = imgH / imgW;
101
+
const renderedHeight = aspectRatio * columnWidth;
102
+
103
+
let shortestIndex = 0;
104
+
for (let i = 1; i < columns; i++) {
105
+
if (columnHeights[i] < columnHeights[shortestIndex]) {
106
+
shortestIndex = i;
107
+
}
108
+
}
109
+
110
+
const left = (columnWidth + spacing) * shortestIndex;
111
+
const top = columnHeights[shortestIndex];
112
+
113
+
Object.assign(tile.style, {
114
+
position: "absolute",
115
+
width: `${columnWidth}px`,
116
+
height: `${renderedHeight}px`,
117
+
left: `${left}px`,
118
+
top: `${top}px`,
119
+
});
120
+
121
+
columnHeights[shortestIndex] = top + renderedHeight + spacing;
122
+
});
123
+
124
+
container.style.height = `${Math.max(...columnHeights)}px`;
125
+
}
126
+
127
+
public computeJustified(): void {
128
+
const container = document.querySelector<HTMLElement>(
129
+
this.containerSelector,
130
+
);
131
+
if (!container) return;
132
+
133
+
const spacing = this.spacing;
134
+
const containerWidth = container.offsetWidth;
135
+
136
+
if (containerWidth === 0) {
137
+
requestAnimationFrame(() => this.computeJustified());
138
+
return;
139
+
}
140
+
141
+
const tiles = Array.from(
142
+
container.querySelectorAll<HTMLElement>(".gallery-item"),
143
+
);
144
+
let currentRow: Array<
145
+
{ tile: HTMLElement; aspectRatio: number; imgW: number; imgH: number }
146
+
> = [];
147
+
let rowAspectRatioSum = 0;
148
+
let yOffset = 0;
149
+
150
+
// Clear all styles before layout
151
+
tiles.forEach((tile) => {
152
+
Object.assign(tile.style, {
153
+
position: "absolute",
154
+
left: "0px",
155
+
top: "0px",
156
+
width: "auto",
157
+
height: "auto",
158
+
});
159
+
});
160
+
161
+
for (let i = 0; i < tiles.length; i++) {
162
+
const tile = tiles[i];
163
+
const imgW = parseFloat((tile as GalleryItem).dataset.width);
164
+
const imgH = parseFloat((tile as GalleryItem).dataset.height);
165
+
if (!imgW || !imgH) continue;
166
+
167
+
const aspectRatio = imgW / imgH;
168
+
currentRow.push({ tile, aspectRatio, imgW, imgH });
169
+
rowAspectRatioSum += aspectRatio;
170
+
171
+
// Estimate if row is "full" enough
172
+
const estimatedRowHeight =
173
+
(containerWidth - (currentRow.length - 1) * spacing) /
174
+
rowAspectRatioSum;
175
+
176
+
// If height is reasonable or we're at the end, render the row
177
+
if (estimatedRowHeight < 300 || i === tiles.length - 1) {
178
+
let xOffset = 0;
179
+
180
+
for (const item of currentRow) {
181
+
const width = estimatedRowHeight * item.aspectRatio;
182
+
Object.assign(item.tile.style, {
183
+
position: "absolute",
184
+
top: `${yOffset}px`,
185
+
left: `${xOffset}px`,
186
+
width: `${width}px`,
187
+
height: `${estimatedRowHeight}px`,
188
+
});
189
+
xOffset += width + spacing;
190
+
}
191
+
192
+
yOffset += estimatedRowHeight + spacing;
193
+
currentRow = [];
194
+
rowAspectRatioSum = 0;
195
+
}
196
+
}
197
+
198
+
container.style.position = "relative";
199
+
container.style.height = `${yOffset}px`;
200
+
}
201
+
202
+
public observe(): void {
203
+
if (this.observerInitialized) return;
204
+
this.observerInitialized = true;
205
+
206
+
const container = document.querySelector<HTMLElement>(
207
+
this.containerSelector,
208
+
);
209
+
if (!container) return;
210
+
211
+
// Observe parent resize
212
+
if (typeof ResizeObserver !== "undefined") {
213
+
const resizeObserver = new ResizeObserver(() => this.computeLayout());
214
+
if (container.parentElement) {
215
+
resizeObserver.observe(container.parentElement);
216
+
}
217
+
}
218
+
219
+
// Observe inner content changes (tiles being added/removed)
220
+
const mutationObserver = new MutationObserver(() => {
221
+
this.computeLayout();
222
+
});
223
+
224
+
mutationObserver.observe(container, {
225
+
childList: true,
226
+
subtree: true,
227
+
});
228
+
}
229
+
230
+
public init(options: GalleryLayoutOptions = {}): void {
231
+
document.addEventListener("DOMContentLoaded", () => {
232
+
const container = document.querySelector(
233
+
options.containerSelector ?? "#gallery-container",
234
+
);
235
+
if (container) {
236
+
this.computeLayout();
237
+
this.observe();
238
+
}
239
+
});
240
+
}
241
+
}
+46
src/static/mod.ts
+46
src/static/mod.ts
···
1
+
import htmx from "htmx.org";
2
+
import _hyperscript from "hyperscript.org";
3
+
import Sortable from "sortablejs";
4
+
import { GalleryLayout } from "./gallery_layout.ts";
5
+
import { PhotoDialog } from "./photo_dialog.ts";
6
+
import * as PhotoManip from "./photo_manip.ts";
7
+
import { ProfileDialog } from "./profile_dialog.ts";
8
+
import { UploadPage } from "./upload_page.ts";
9
+
10
+
const galleryLayout = new GalleryLayout({ layoutMode: "justified" });
11
+
galleryLayout.init();
12
+
13
+
htmx.onLoad(function (element) {
14
+
PhotoDialog.maybeInitForElement(element);
15
+
16
+
if (element.id === "gallery-sort-dialog") {
17
+
const sortables = element.querySelectorAll(".sortable");
18
+
for (const sortable of Array.from(sortables)) {
19
+
new Sortable(sortable, {
20
+
animation: 150,
21
+
});
22
+
}
23
+
}
24
+
});
25
+
26
+
_hyperscript.browserInit();
27
+
28
+
type GrainGlobal = typeof globalThis & {
29
+
htmx: typeof htmx;
30
+
_hyperscript: typeof _hyperscript;
31
+
Grain: {
32
+
uploadPage?: UploadPage;
33
+
profileDialog?: ProfileDialog;
34
+
galleryLayout?: GalleryLayout;
35
+
photoManip?: typeof PhotoManip;
36
+
};
37
+
};
38
+
39
+
const g = globalThis as GrainGlobal;
40
+
g.htmx = g.htmx ?? htmx;
41
+
g._hyperscript = g._hyperscript ?? _hyperscript;
42
+
g.Grain = g.Grain ?? {};
43
+
g.Grain.uploadPage = new UploadPage();
44
+
g.Grain.profileDialog = new ProfileDialog();
45
+
g.Grain.galleryLayout = galleryLayout;
46
+
g.Grain.photoManip = PhotoManip;
+94
src/static/photo_dialog.ts
+94
src/static/photo_dialog.ts
···
1
+
/**
2
+
* PhotoDialog class to handle touch events for swiping left or right
3
+
* on a photo dialog element.
4
+
*
5
+
* Example usage (see src/static/mod.ts):
6
+
*
7
+
* htmx.onLoad(function (element) {
8
+
* PhotoDialog.maybeInitForElement(element);
9
+
* // ...
10
+
* });
11
+
*
12
+
* This will automatically initialize swipe event listeners and observation
13
+
* when an element with id "photo-dialog" is loaded into the DOM.
14
+
*
15
+
* Example for htmx+JSX:
16
+
*
17
+
* <div
18
+
* hx-get={photoDialogLink(gallery, nextImage)}
19
+
* hx-trigger="keyup[key=='ArrowRight'] from:body, swipeleft from:body"
20
+
* hx-target="#photo-dialog"
21
+
* hx-swap="innerHTML"
22
+
* />
23
+
*
24
+
* This pattern allows htmx to swap the photo dialog content and trigger swipe events
25
+
* that are handled by PhotoDialog.
26
+
*/
27
+
export class PhotoDialog {
28
+
private startX = 0;
29
+
private readonly threshold = 50;
30
+
private observer: MutationObserver;
31
+
private static initialized = false;
32
+
public static id = "photo-dialog";
33
+
34
+
constructor() {
35
+
this.observer = new MutationObserver(this.handleMutation.bind(this));
36
+
}
37
+
38
+
public connect(): void {
39
+
this.onTouchStart = this.onTouchStart.bind(this);
40
+
this.onTouchEnd = this.onTouchEnd.bind(this);
41
+
console.log("PhotoDialog: Connected touch event handlers.");
42
+
}
43
+
44
+
public static maybeInitForElement(element: Element): void {
45
+
if (element.id !== PhotoDialog.id) return;
46
+
if (PhotoDialog.initialized) return;
47
+
const dialog = new PhotoDialog();
48
+
dialog.connect();
49
+
if (document && document.body) {
50
+
document.body.addEventListener("touchstart", dialog.onTouchStart);
51
+
document.body.addEventListener("touchend", dialog.onTouchEnd);
52
+
dialog.observe();
53
+
PhotoDialog.initialized = true;
54
+
} else {
55
+
console.warn(
56
+
"document.body not available for PhotoDialog event listeners",
57
+
);
58
+
}
59
+
}
60
+
61
+
public onTouchStart(e: TouchEvent): void {
62
+
this.startX = e.touches[0].clientX;
63
+
}
64
+
65
+
public onTouchEnd(e: TouchEvent): void {
66
+
const endX = e.changedTouches[0].clientX;
67
+
const diffX = endX - this.startX;
68
+
if (Math.abs(diffX) > this.threshold) {
69
+
const direction = diffX > 0 ? "swiperight" : "swipeleft";
70
+
(e.target as HTMLElement).dispatchEvent(
71
+
new CustomEvent(direction, { bubbles: true }),
72
+
);
73
+
}
74
+
}
75
+
76
+
public observe(): void {
77
+
this.observer.observe(document.body, { childList: true, subtree: true });
78
+
}
79
+
80
+
public disconnect(): void {
81
+
this.observer.disconnect();
82
+
document.body.removeEventListener("touchstart", this.onTouchStart);
83
+
document.body.removeEventListener("touchend", this.onTouchEnd);
84
+
PhotoDialog.initialized = false;
85
+
}
86
+
87
+
private handleMutation(): void {
88
+
const modal = document.getElementById(PhotoDialog.id);
89
+
if (!modal) {
90
+
console.log("PhotoDialog not found, removing event listeners");
91
+
this.disconnect();
92
+
}
93
+
}
94
+
}
+69
src/static/profile_dialog.ts
+69
src/static/profile_dialog.ts
···
1
+
import htmx from "htmx.org";
2
+
import { dataURLToBlob, doResize, readFileAsDataURL } from "./photo_manip.ts";
3
+
4
+
export class ProfileDialog {
5
+
public handleAvatarImageSelect(fileInput: HTMLInputElement): void {
6
+
const file = fileInput.files?.[0];
7
+
if (file) {
8
+
readFileAsDataURL(file).then((dataUrl) => {
9
+
const previewImg = document.createElement("img");
10
+
if (typeof dataUrl === "string") {
11
+
previewImg.src = dataUrl;
12
+
} else {
13
+
console.error(
14
+
"Expected dataUrl to be a string, got:",
15
+
typeof dataUrl,
16
+
);
17
+
previewImg.src = "";
18
+
}
19
+
previewImg.className = "rounded-full w-full h-full object-cover";
20
+
previewImg.alt = "Avatar preview";
21
+
22
+
const imagePreview = fileInput.closest("form")?.querySelector(
23
+
"#image-preview",
24
+
);
25
+
if (imagePreview) {
26
+
imagePreview.innerHTML = "";
27
+
imagePreview.appendChild(previewImg);
28
+
}
29
+
});
30
+
}
31
+
}
32
+
33
+
public async updateProfile(formElement: HTMLFormElement): Promise<void> {
34
+
const formData = new FormData(formElement);
35
+
36
+
const avatarFile = formData.get("file");
37
+
if (
38
+
avatarFile instanceof File && avatarFile?.type?.startsWith?.("image/")
39
+
) {
40
+
try {
41
+
const dataUrl = await readFileAsDataURL(avatarFile);
42
+
43
+
if (!dataUrl || typeof dataUrl !== "string") {
44
+
console.error("Failed to read file as data URL");
45
+
return;
46
+
}
47
+
48
+
const resized = await doResize(dataUrl, {
49
+
width: 2000,
50
+
height: 2000,
51
+
maxSize: 1000 * 1000, // 1MB
52
+
mode: "contain",
53
+
});
54
+
55
+
const blob = dataURLToBlob(resized.path);
56
+
formData.set("file", blob, avatarFile.name);
57
+
} catch (err) {
58
+
console.error("Error resizing image:", err);
59
+
formData.delete("file");
60
+
}
61
+
}
62
+
63
+
htmx.ajax("PUT", "/actions/profile", {
64
+
swap: "none",
65
+
values: Object.fromEntries(formData),
66
+
source: formElement,
67
+
});
68
+
}
69
+
}
+125
src/static/upload_page.ts
+125
src/static/upload_page.ts
···
1
+
import exifr from "exifr";
2
+
import htmx from "htmx.org";
3
+
import { dataURLToBlob, doResize, readFileAsDataURL } from "./photo_manip.ts";
4
+
import { tags as supportedTags } from "./tags.ts";
5
+
6
+
export class UploadPage {
7
+
public async uploadPhotos(formElement: HTMLFormElement): Promise<void> {
8
+
const formData = new FormData(formElement);
9
+
const fileList = formData.getAll("files") as File[] ?? [];
10
+
const parseExif = formData.get("parseExif") === "on";
11
+
12
+
if (fileList.length > 10) {
13
+
alert("You can only upload 10 photos at a time");
14
+
return;
15
+
}
16
+
17
+
const uploadPromises = fileList.map(async (file) => {
18
+
let fileDataUri: string | ArrayBuffer | null;
19
+
let tags: Exif | undefined = undefined;
20
+
let resized;
21
+
22
+
try {
23
+
fileDataUri = await readFileAsDataURL(file);
24
+
if (fileDataUri === null || typeof fileDataUri !== "string") {
25
+
console.error("File data URL is not a string:", fileDataUri);
26
+
alert("Error reading file.");
27
+
return;
28
+
}
29
+
} catch (err) {
30
+
console.error("Error reading file as Data URL:", err);
31
+
alert("Error reading file.");
32
+
return;
33
+
}
34
+
35
+
if (parseExif) {
36
+
try {
37
+
const rawTags = await exifr.parse(file, { pick: supportedTags });
38
+
console.log("EXIF tags:", await exifr.parse(file));
39
+
tags = normalizeExif(rawTags);
40
+
} catch (err) {
41
+
console.error("Error reading EXIF data:", err);
42
+
}
43
+
}
44
+
45
+
try {
46
+
resized = await doResize(fileDataUri, {
47
+
width: 2000,
48
+
height: 2000,
49
+
maxSize: 1000 * 1000, // 1MB
50
+
mode: "contain",
51
+
});
52
+
} catch (err) {
53
+
console.error("Error resizing image:", err);
54
+
alert("Error resizing image.");
55
+
return;
56
+
}
57
+
58
+
const blob = dataURLToBlob(resized.path);
59
+
60
+
const fd = new FormData();
61
+
fd.append("file", blob, file.name);
62
+
fd.append("width", String(resized.width));
63
+
fd.append("height", String(resized.height));
64
+
65
+
if (tags) {
66
+
fd.append("exif", JSON.stringify(tags));
67
+
}
68
+
69
+
htmx.ajax("POST", "/actions/photo", {
70
+
"swap": "afterbegin",
71
+
"target": "#image-preview",
72
+
"values": Object.fromEntries(fd),
73
+
"source": formElement,
74
+
});
75
+
});
76
+
77
+
await Promise.all(uploadPromises);
78
+
79
+
// Clear the file input after upload
80
+
(formElement.querySelector("input[type='file']") as HTMLInputElement)!
81
+
.value = "";
82
+
}
83
+
}
84
+
85
+
const SCALE_FACTOR = 1000000;
86
+
87
+
type Exif = Record<
88
+
string,
89
+
number | string | boolean | Array<number | string> | undefined | Date
90
+
>;
91
+
92
+
function normalizeExif(
93
+
exif: Exif,
94
+
scale: number = SCALE_FACTOR,
95
+
): Exif {
96
+
const normalized: Record<
97
+
string,
98
+
number | string | boolean | Array<number | string> | undefined
99
+
> = {};
100
+
101
+
for (const [key, value] of Object.entries(exif)) {
102
+
const camelKey = key[0].toLowerCase() + key.slice(1);
103
+
104
+
if (typeof value === "number") {
105
+
normalized[camelKey] = Math.round(value * scale);
106
+
} else if (Array.isArray(value)) {
107
+
normalized[camelKey] = value.map((v) =>
108
+
typeof v === "number" ? Math.round(v * scale) : v
109
+
);
110
+
} else if (value instanceof Date) {
111
+
normalized[camelKey] = value.toISOString();
112
+
} else if (typeof value === "string") {
113
+
normalized[camelKey] = value;
114
+
} else if (typeof value === "boolean") {
115
+
normalized[camelKey] = value;
116
+
} else if (value === undefined) {
117
+
normalized[camelKey] = undefined;
118
+
} else {
119
+
// fallback for unknown types
120
+
normalized[camelKey] = String(value);
121
+
}
122
+
}
123
+
124
+
return normalized;
125
+
}
+10
src/utils.ts
+10
src/utils.ts
···
32
32
return `/profile/${handle}`;
33
33
}
34
34
35
+
export function followersLink(handle: string) {
36
+
return `/profile/${handle}/followers`;
37
+
}
38
+
39
+
export function followingLink(handle: string) {
40
+
return `/profile/${handle}/follows`;
41
+
}
42
+
35
43
export function galleryLink(handle: string, galleryRkey: string) {
36
44
return `/profile/${handle}/gallery/${galleryRkey}`;
37
45
}
···
68
76
externalCollections: [
69
77
"app.bsky.actor.profile",
70
78
"app.bsky.graph.follow",
79
+
"sh.tangled.actor.profile",
80
+
"sh.tangled.graph.follow",
71
81
],
72
82
repos: [actor.did],
73
83
});
+26
static/app.esm.js
+26
static/app.esm.js
···
1
+
var $l=Object.create;var rs=Object.defineProperty;var Jl=Object.getOwnPropertyDescriptor;var Zl=Object.getOwnPropertyNames;var eu=Object.getPrototypeOf,tu=Object.prototype.hasOwnProperty;var ns=(Qr=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(Qr,{get:(Gr,Wr)=>(typeof require<"u"?require:Gr)[Wr]}):Qr)(function(Qr){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+Qr+'" is not supported')});var Qs=(Qr,Gr)=>()=>(Gr||Qr((Gr={exports:{}}).exports,Gr),Gr.exports),ru=(Qr,Gr)=>{for(var Wr in Gr)rs(Qr,Wr,{get:Gr[Wr],enumerable:!0})},nu=(Qr,Gr,Wr,Kr)=>{if(Gr&&typeof Gr=="object"||typeof Gr=="function")for(let Yr of Zl(Gr))!tu.call(Qr,Yr)&&Yr!==Wr&&rs(Qr,Yr,{get:()=>Gr[Yr],enumerable:!(Kr=Jl(Gr,Yr))||Kr.enumerable});return Qr};var po=(Qr,Gr,Wr)=>(Wr=Qr!=null?$l(eu(Qr)):{},nu(Gr||!Qr||!Qr.__esModule?rs(Wr,"default",{value:Qr,enumerable:!0}):Wr,Qr));var mo=Qs((exports,module)=>{(function(Qr,Gr){typeof define=="function"&&define.amd?define([],Gr):typeof module=="object"&&module.exports?module.exports=Gr():Qr.htmx=Qr.htmx||Gr()})(typeof self<"u"?self:exports,function(){return function(){"use strict";var Q={onLoad:F,process:zt,on:de,off:ge,trigger:ce,ajax:Nr,find:C,findAll:f,closest:v,values:function(Qr,Gr){var Wr=dr(Qr,Gr||"post");return Wr.values},remove:_,addClass:z,removeClass:n,toggleClass:$,takeClass:W,defineExtension:Ur,removeExtension:Br,logAll:V,logNone:j,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,allowScriptTags:!0,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:!1,scrollBehavior:"smooth",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get"],selfRequestsOnly:!1,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null},parseInterval:d,_:t,createEventSource:function(Qr){return new EventSource(Qr,{withCredentials:!0})},createWebSocket:function(Qr){var Gr=new WebSocket(Qr,[]);return Gr.binaryType=Q.config.wsBinaryType,Gr},version:"1.9.12"},r={addTriggerHandler:Lt,bodyContains:se,canAccessLocalStorage:U,findThisElement:xe,filterValues:yr,hasAttribute:o,getAttributeValue:te,getClosestAttributeValue:ne,getClosestMatch:c,getExpressionVars:Hr,getHeaders:xr,getInputValues:dr,getInternalData:ae,getSwapSpecification:wr,getTriggerSpecs:it,getTarget:ye,makeFragment:l,mergeObjects:le,makeSettleInfo:T,oobSwap:Ee,querySelectorExt:ue,selectAndSwap:je,settleImmediately:nr,shouldCancel:ut,triggerEvent:ce,triggerErrorEvent:fe,withExtensions:R},w=["get","post","put","delete","patch"],i=w.map(function(Qr){return"[hx-"+Qr+"], [data-hx-"+Qr+"]"}).join(", "),S=e("head"),q=e("title"),H=e("svg",!0);function e(Qr,Gr){return new RegExp("<"+Qr+"(\\s[^>]*>|>)([\\s\\S]*?)<\\/"+Qr+">",Gr?"gim":"im")}function d(Qr){if(Qr==null)return;let Gr=NaN;return Qr.slice(-2)=="ms"?Gr=parseFloat(Qr.slice(0,-2)):Qr.slice(-1)=="s"?Gr=parseFloat(Qr.slice(0,-1))*1e3:Qr.slice(-1)=="m"?Gr=parseFloat(Qr.slice(0,-1))*1e3*60:Gr=parseFloat(Qr),isNaN(Gr)?void 0:Gr}function ee(Qr,Gr){return Qr.getAttribute&&Qr.getAttribute(Gr)}function o(Qr,Gr){return Qr.hasAttribute&&(Qr.hasAttribute(Gr)||Qr.hasAttribute("data-"+Gr))}function te(Qr,Gr){return ee(Qr,Gr)||ee(Qr,"data-"+Gr)}function u(Qr){return Qr.parentElement}function re(){return document}function c(Qr,Gr){for(;Qr&&!Gr(Qr);)Qr=u(Qr);return Qr||null}function L(Qr,Gr,Wr){var Kr=te(Gr,Wr),Yr=te(Gr,"hx-disinherit");return Qr!==Gr&&Yr&&(Yr==="*"||Yr.split(" ").indexOf(Wr)>=0)?"unset":Kr}function ne(Qr,Gr){var Wr=null;if(c(Qr,function(Kr){return Wr=L(Qr,Kr,Gr)}),Wr!=="unset")return Wr}function h(Qr,Gr){var Wr=Qr.matches||Qr.matchesSelector||Qr.msMatchesSelector||Qr.mozMatchesSelector||Qr.webkitMatchesSelector||Qr.oMatchesSelector;return Wr&&Wr.call(Qr,Gr)}function A(Qr){var Gr=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,Wr=Gr.exec(Qr);return Wr?Wr[1].toLowerCase():""}function s(Qr,Gr){for(var Wr=new DOMParser,Kr=Wr.parseFromString(Qr,"text/html"),Yr=Kr.body;Gr>0;)Gr--,Yr=Yr.firstChild;return Yr==null&&(Yr=re().createDocumentFragment()),Yr}function N(Qr){return/<body/.test(Qr)}function l(Qr){var Gr=!N(Qr),Wr=A(Qr),Kr=Qr;if(Wr==="head"&&(Kr=Kr.replace(S,"")),Q.config.useTemplateFragments&&Gr){var Yr=s("<body><template>"+Kr+"</template></body>",0),Zr=Yr.querySelector("template").content;return Q.config.allowScriptTags?oe(Zr.querySelectorAll("script"),function(tn){Q.config.inlineScriptNonce&&(tn.nonce=Q.config.inlineScriptNonce),tn.htmxExecuted=navigator.userAgent.indexOf("Firefox")===-1}):oe(Zr.querySelectorAll("script"),function(tn){_(tn)}),Zr}switch(Wr){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return s("<table>"+Kr+"</table>",1);case"col":return s("<table><colgroup>"+Kr+"</colgroup></table>",2);case"tr":return s("<table><tbody>"+Kr+"</tbody></table>",2);case"td":case"th":return s("<table><tbody><tr>"+Kr+"</tr></tbody></table>",3);case"script":case"style":return s("<div>"+Kr+"</div>",1);default:return s(Kr,0)}}function ie(Qr){Qr&&Qr()}function I(Qr,Gr){return Object.prototype.toString.call(Qr)==="[object "+Gr+"]"}function k(Qr){return I(Qr,"Function")}function P(Qr){return I(Qr,"Object")}function ae(Qr){var Gr="htmx-internal-data",Wr=Qr[Gr];return Wr||(Wr=Qr[Gr]={}),Wr}function M(Qr){var Gr=[];if(Qr)for(var Wr=0;Wr<Qr.length;Wr++)Gr.push(Qr[Wr]);return Gr}function oe(Qr,Gr){if(Qr)for(var Wr=0;Wr<Qr.length;Wr++)Gr(Qr[Wr])}function X(Qr){var Gr=Qr.getBoundingClientRect(),Wr=Gr.top,Kr=Gr.bottom;return Wr<window.innerHeight&&Kr>=0}function se(Qr){return Qr.getRootNode&&Qr.getRootNode()instanceof window.ShadowRoot?re().body.contains(Qr.getRootNode().host):re().body.contains(Qr)}function D(Qr){return Qr.trim().split(/\s+/)}function le(Qr,Gr){for(var Wr in Gr)Gr.hasOwnProperty(Wr)&&(Qr[Wr]=Gr[Wr]);return Qr}function E(Qr){try{return JSON.parse(Qr)}catch(Gr){return b(Gr),null}}function U(){var Qr="htmx:localStorageTest";try{return localStorage.setItem(Qr,Qr),localStorage.removeItem(Qr),!0}catch{return!1}}function B(Qr){try{var Gr=new URL(Qr);return Gr&&(Qr=Gr.pathname+Gr.search),/^\/$/.test(Qr)||(Qr=Qr.replace(/\/+$/,"")),Qr}catch{return Qr}}function t(e){return Tr(re().body,function(){return eval(e)})}function F(Qr){var Gr=Q.on("htmx:load",function(Wr){Qr(Wr.detail.elt)});return Gr}function V(){Q.logger=function(Qr,Gr,Wr){console&&console.log(Gr,Qr,Wr)}}function j(){Q.logger=null}function C(Qr,Gr){return Gr?Qr.querySelector(Gr):C(re(),Qr)}function f(Qr,Gr){return Gr?Qr.querySelectorAll(Gr):f(re(),Qr)}function _(Qr,Gr){Qr=p(Qr),Gr?setTimeout(function(){_(Qr),Qr=null},Gr):Qr.parentElement.removeChild(Qr)}function z(Qr,Gr,Wr){Qr=p(Qr),Wr?setTimeout(function(){z(Qr,Gr),Qr=null},Wr):Qr.classList&&Qr.classList.add(Gr)}function n(Qr,Gr,Wr){Qr=p(Qr),Wr?setTimeout(function(){n(Qr,Gr),Qr=null},Wr):Qr.classList&&(Qr.classList.remove(Gr),Qr.classList.length===0&&Qr.removeAttribute("class"))}function $(Qr,Gr){Qr=p(Qr),Qr.classList.toggle(Gr)}function W(Qr,Gr){Qr=p(Qr),oe(Qr.parentElement.children,function(Wr){n(Wr,Gr)}),z(Qr,Gr)}function v(Qr,Gr){if(Qr=p(Qr),Qr.closest)return Qr.closest(Gr);do if(Qr==null||h(Qr,Gr))return Qr;while(Qr=Qr&&u(Qr));return null}function g(Qr,Gr){return Qr.substring(0,Gr.length)===Gr}function G(Qr,Gr){return Qr.substring(Qr.length-Gr.length)===Gr}function J(Qr){var Gr=Qr.trim();return g(Gr,"<")&&G(Gr,"/>")?Gr.substring(1,Gr.length-2):Gr}function Z(Qr,Gr){return Gr.indexOf("closest ")===0?[v(Qr,J(Gr.substr(8)))]:Gr.indexOf("find ")===0?[C(Qr,J(Gr.substr(5)))]:Gr==="next"?[Qr.nextElementSibling]:Gr.indexOf("next ")===0?[K(Qr,J(Gr.substr(5)))]:Gr==="previous"?[Qr.previousElementSibling]:Gr.indexOf("previous ")===0?[Y(Qr,J(Gr.substr(9)))]:Gr==="document"?[document]:Gr==="window"?[window]:Gr==="body"?[document.body]:re().querySelectorAll(J(Gr))}var K=function(Qr,Gr){for(var Wr=re().querySelectorAll(Gr),Kr=0;Kr<Wr.length;Kr++){var Yr=Wr[Kr];if(Yr.compareDocumentPosition(Qr)===Node.DOCUMENT_POSITION_PRECEDING)return Yr}},Y=function(Qr,Gr){for(var Wr=re().querySelectorAll(Gr),Kr=Wr.length-1;Kr>=0;Kr--){var Yr=Wr[Kr];if(Yr.compareDocumentPosition(Qr)===Node.DOCUMENT_POSITION_FOLLOWING)return Yr}};function ue(Qr,Gr){return Gr?Z(Qr,Gr)[0]:Z(re().body,Qr)[0]}function p(Qr){return I(Qr,"String")?C(Qr):Qr}function ve(Qr,Gr,Wr){return k(Gr)?{target:re().body,event:Qr,listener:Gr}:{target:p(Qr),event:Gr,listener:Wr}}function de(Qr,Gr,Wr){jr(function(){var Yr=ve(Qr,Gr,Wr);Yr.target.addEventListener(Yr.event,Yr.listener)});var Kr=k(Gr);return Kr?Gr:Wr}function ge(Qr,Gr,Wr){return jr(function(){var Kr=ve(Qr,Gr,Wr);Kr.target.removeEventListener(Kr.event,Kr.listener)}),k(Gr)?Gr:Wr}var pe=re().createElement("output");function me(Qr,Gr){var Wr=ne(Qr,Gr);if(Wr){if(Wr==="this")return[xe(Qr,Gr)];var Kr=Z(Qr,Wr);return Kr.length===0?(b('The selector "'+Wr+'" on '+Gr+" returned no matches!"),[pe]):Kr}}function xe(Qr,Gr){return c(Qr,function(Wr){return te(Wr,Gr)!=null})}function ye(Qr){var Gr=ne(Qr,"hx-target");if(Gr)return Gr==="this"?xe(Qr,"hx-target"):ue(Qr,Gr);var Wr=ae(Qr);return Wr.boosted?re().body:Qr}function be(Qr){for(var Gr=Q.config.attributesToSettle,Wr=0;Wr<Gr.length;Wr++)if(Qr===Gr[Wr])return!0;return!1}function we(Qr,Gr){oe(Qr.attributes,function(Wr){!Gr.hasAttribute(Wr.name)&&be(Wr.name)&&Qr.removeAttribute(Wr.name)}),oe(Gr.attributes,function(Wr){be(Wr.name)&&Qr.setAttribute(Wr.name,Wr.value)})}function Se(Qr,Gr){for(var Wr=Fr(Gr),Kr=0;Kr<Wr.length;Kr++){var Yr=Wr[Kr];try{if(Yr.isInlineSwap(Qr))return!0}catch(Zr){b(Zr)}}return Qr==="outerHTML"}function Ee(Qr,Gr,Wr){var Kr="#"+ee(Gr,"id"),Yr="outerHTML";Qr==="true"||(Qr.indexOf(":")>0?(Yr=Qr.substr(0,Qr.indexOf(":")),Kr=Qr.substr(Qr.indexOf(":")+1,Qr.length)):Yr=Qr);var Zr=re().querySelectorAll(Kr);return Zr?(oe(Zr,function(tn){var ln,dn=Gr.cloneNode(!0);ln=re().createDocumentFragment(),ln.appendChild(dn),Se(Yr,tn)||(ln=dn);var yn={shouldSwap:!0,target:tn,fragment:ln};ce(tn,"htmx:oobBeforeSwap",yn)&&(tn=yn.target,yn.shouldSwap&&Fe(Yr,tn,tn,ln,Wr),oe(Wr.elts,function(wn){ce(wn,"htmx:oobAfterSwap",yn)}))}),Gr.parentNode.removeChild(Gr)):(Gr.parentNode.removeChild(Gr),fe(re().body,"htmx:oobErrorNoTarget",{content:Gr})),Qr}function Ce(Qr,Gr,Wr){var Kr=ne(Qr,"hx-select-oob");if(Kr)for(var Yr=Kr.split(","),Zr=0;Zr<Yr.length;Zr++){var tn=Yr[Zr].split(":",2),ln=tn[0].trim();ln.indexOf("#")===0&&(ln=ln.substring(1));var dn=tn[1]||"true",yn=Gr.querySelector("#"+ln);yn&&Ee(dn,yn,Wr)}oe(f(Gr,"[hx-swap-oob], [data-hx-swap-oob]"),function(wn){var kn=te(wn,"hx-swap-oob");kn!=null&&Ee(kn,wn,Wr)})}function Re(Qr){oe(f(Qr,"[hx-preserve], [data-hx-preserve]"),function(Gr){var Wr=te(Gr,"id"),Kr=re().getElementById(Wr);Kr!=null&&Gr.parentNode.replaceChild(Kr,Gr)})}function Te(Qr,Gr,Wr){oe(Gr.querySelectorAll("[id]"),function(Kr){var Yr=ee(Kr,"id");if(Yr&&Yr.length>0){var Zr=Yr.replace("'","\\'"),tn=Kr.tagName.replace(":","\\:"),ln=Qr.querySelector(tn+"[id='"+Zr+"']");if(ln&&ln!==Qr){var dn=Kr.cloneNode();we(Kr,ln),Wr.tasks.push(function(){we(Kr,dn)})}}})}function Oe(Qr){return function(){n(Qr,Q.config.addedClass),zt(Qr),Nt(Qr),qe(Qr),ce(Qr,"htmx:load")}}function qe(Qr){var Gr="[autofocus]",Wr=h(Qr,Gr)?Qr:Qr.querySelector(Gr);Wr?.focus()}function a(Qr,Gr,Wr,Kr){for(Te(Qr,Wr,Kr);Wr.childNodes.length>0;){var Yr=Wr.firstChild;z(Yr,Q.config.addedClass),Qr.insertBefore(Yr,Gr),Yr.nodeType!==Node.TEXT_NODE&&Yr.nodeType!==Node.COMMENT_NODE&&Kr.tasks.push(Oe(Yr))}}function He(Qr,Gr){for(var Wr=0;Wr<Qr.length;)Gr=(Gr<<5)-Gr+Qr.charCodeAt(Wr++)|0;return Gr}function Le(Qr){var Gr=0;if(Qr.attributes)for(var Wr=0;Wr<Qr.attributes.length;Wr++){var Kr=Qr.attributes[Wr];Kr.value&&(Gr=He(Kr.name,Gr),Gr=He(Kr.value,Gr))}return Gr}function Ae(Qr){var Gr=ae(Qr);if(Gr.onHandlers){for(var Wr=0;Wr<Gr.onHandlers.length;Wr++){let Kr=Gr.onHandlers[Wr];Qr.removeEventListener(Kr.event,Kr.listener)}delete Gr.onHandlers}}function Ne(Qr){var Gr=ae(Qr);Gr.timeout&&clearTimeout(Gr.timeout),Gr.webSocket&&Gr.webSocket.close(),Gr.sseEventSource&&Gr.sseEventSource.close(),Gr.listenerInfos&&oe(Gr.listenerInfos,function(Wr){Wr.on&&Wr.on.removeEventListener(Wr.trigger,Wr.listener)}),Ae(Qr),oe(Object.keys(Gr),function(Wr){delete Gr[Wr]})}function m(Qr){ce(Qr,"htmx:beforeCleanupElement"),Ne(Qr),Qr.children&&oe(Qr.children,function(Gr){m(Gr)})}function Ie(Qr,Gr,Wr){if(Qr.tagName==="BODY")return Ue(Qr,Gr,Wr);var Kr,Yr=Qr.previousSibling;for(a(u(Qr),Qr,Gr,Wr),Yr==null?Kr=u(Qr).firstChild:Kr=Yr.nextSibling,Wr.elts=Wr.elts.filter(function(Zr){return Zr!=Qr});Kr&&Kr!==Qr;)Kr.nodeType===Node.ELEMENT_NODE&&Wr.elts.push(Kr),Kr=Kr.nextElementSibling;m(Qr),u(Qr).removeChild(Qr)}function ke(Qr,Gr,Wr){return a(Qr,Qr.firstChild,Gr,Wr)}function Pe(Qr,Gr,Wr){return a(u(Qr),Qr,Gr,Wr)}function Me(Qr,Gr,Wr){return a(Qr,null,Gr,Wr)}function Xe(Qr,Gr,Wr){return a(u(Qr),Qr.nextSibling,Gr,Wr)}function De(Qr,Gr,Wr){return m(Qr),u(Qr).removeChild(Qr)}function Ue(Qr,Gr,Wr){var Kr=Qr.firstChild;if(a(Qr,Kr,Gr,Wr),Kr){for(;Kr.nextSibling;)m(Kr.nextSibling),Qr.removeChild(Kr.nextSibling);m(Kr),Qr.removeChild(Kr)}}function Be(Qr,Gr,Wr){var Kr=Wr||ne(Qr,"hx-select");if(Kr){var Yr=re().createDocumentFragment();oe(Gr.querySelectorAll(Kr),function(Zr){Yr.appendChild(Zr)}),Gr=Yr}return Gr}function Fe(Qr,Gr,Wr,Kr,Yr){switch(Qr){case"none":return;case"outerHTML":Ie(Wr,Kr,Yr);return;case"afterbegin":ke(Wr,Kr,Yr);return;case"beforebegin":Pe(Wr,Kr,Yr);return;case"beforeend":Me(Wr,Kr,Yr);return;case"afterend":Xe(Wr,Kr,Yr);return;case"delete":De(Wr,Kr,Yr);return;default:for(var Zr=Fr(Gr),tn=0;tn<Zr.length;tn++){var ln=Zr[tn];try{var dn=ln.handleSwap(Qr,Wr,Kr,Yr);if(dn){if(typeof dn.length<"u")for(var yn=0;yn<dn.length;yn++){var wn=dn[yn];wn.nodeType!==Node.TEXT_NODE&&wn.nodeType!==Node.COMMENT_NODE&&Yr.tasks.push(Oe(wn))}return}}catch(kn){b(kn)}}Qr==="innerHTML"?Ue(Wr,Kr,Yr):Fe(Q.config.defaultSwapStyle,Gr,Wr,Kr,Yr)}}function Ve(Qr){if(Qr.indexOf("<title")>-1){var Gr=Qr.replace(H,""),Wr=Gr.match(q);if(Wr)return Wr[2]}}function je(Qr,Gr,Wr,Kr,Yr,Zr){Yr.title=Ve(Kr);var tn=l(Kr);if(tn)return Ce(Wr,tn,Yr),tn=Be(Wr,tn,Zr),Re(tn),Fe(Qr,Wr,Gr,tn,Yr)}function _e(Qr,Gr,Wr){var Kr=Qr.getResponseHeader(Gr);if(Kr.indexOf("{")===0){var Yr=E(Kr);for(var Zr in Yr)if(Yr.hasOwnProperty(Zr)){var tn=Yr[Zr];P(tn)||(tn={value:tn}),ce(Wr,Zr,tn)}}else for(var ln=Kr.split(","),dn=0;dn<ln.length;dn++)ce(Wr,ln[dn].trim(),[])}var ze=/\s/,x=/[\s,]/,$e=/[_$a-zA-Z]/,We=/[_$a-zA-Z0-9]/,Ge=['"',"'","/"],Je=/[^\s]/,Ze=/[{(]/,Ke=/[})]/;function Ye(Qr){for(var Gr=[],Wr=0;Wr<Qr.length;){if($e.exec(Qr.charAt(Wr))){for(var Kr=Wr;We.exec(Qr.charAt(Wr+1));)Wr++;Gr.push(Qr.substr(Kr,Wr-Kr+1))}else if(Ge.indexOf(Qr.charAt(Wr))!==-1){var Yr=Qr.charAt(Wr),Kr=Wr;for(Wr++;Wr<Qr.length&&Qr.charAt(Wr)!==Yr;)Qr.charAt(Wr)==="\\"&&Wr++,Wr++;Gr.push(Qr.substr(Kr,Wr-Kr+1))}else{var Zr=Qr.charAt(Wr);Gr.push(Zr)}Wr++}return Gr}function Qe(Qr,Gr,Wr){return $e.exec(Qr.charAt(0))&&Qr!=="true"&&Qr!=="false"&&Qr!=="this"&&Qr!==Wr&&Gr!=="."}function et(Qr,Gr,Wr){if(Gr[0]==="["){Gr.shift();for(var Kr=1,Yr=" return (function("+Wr+"){ return (",Zr=null;Gr.length>0;){var tn=Gr[0];if(tn==="]"){if(Kr--,Kr===0){Zr===null&&(Yr=Yr+"true"),Gr.shift(),Yr+=")})";try{var ln=Tr(Qr,function(){return Function(Yr)()},function(){return!0});return ln.source=Yr,ln}catch(dn){return fe(re().body,"htmx:syntax:error",{error:dn,source:Yr}),null}}}else tn==="["&&Kr++;Qe(tn,Zr,Wr)?Yr+="(("+Wr+"."+tn+") ? ("+Wr+"."+tn+") : (window."+tn+"))":Yr=Yr+tn,Zr=Gr.shift()}}}function y(Qr,Gr){for(var Wr="";Qr.length>0&&!Gr.test(Qr[0]);)Wr+=Qr.shift();return Wr}function tt(Qr){var Gr;return Qr.length>0&&Ze.test(Qr[0])?(Qr.shift(),Gr=y(Qr,Ke).trim(),Qr.shift()):Gr=y(Qr,x),Gr}var rt="input, textarea, select";function nt(Qr,Gr,Wr){var Kr=[],Yr=Ye(Gr);do{y(Yr,Je);var Zr=Yr.length,tn=y(Yr,/[,\[\s]/);if(tn!=="")if(tn==="every"){var ln={trigger:"every"};y(Yr,Je),ln.pollInterval=d(y(Yr,/[,\[\s]/)),y(Yr,Je);var dn=et(Qr,Yr,"event");dn&&(ln.eventFilter=dn),Kr.push(ln)}else if(tn.indexOf("sse:")===0)Kr.push({trigger:"sse",sseEvent:tn.substr(4)});else{var yn={trigger:tn},dn=et(Qr,Yr,"event");for(dn&&(yn.eventFilter=dn);Yr.length>0&&Yr[0]!==",";){y(Yr,Je);var wn=Yr.shift();if(wn==="changed")yn.changed=!0;else if(wn==="once")yn.once=!0;else if(wn==="consume")yn.consume=!0;else if(wn==="delay"&&Yr[0]===":")Yr.shift(),yn.delay=d(y(Yr,x));else if(wn==="from"&&Yr[0]===":"){if(Yr.shift(),Ze.test(Yr[0]))var kn=tt(Yr);else{var kn=y(Yr,x);if(kn==="closest"||kn==="find"||kn==="next"||kn==="previous"){Yr.shift();var An=tt(Yr);An.length>0&&(kn+=" "+An)}}yn.from=kn}else wn==="target"&&Yr[0]===":"?(Yr.shift(),yn.target=tt(Yr)):wn==="throttle"&&Yr[0]===":"?(Yr.shift(),yn.throttle=d(y(Yr,x))):wn==="queue"&&Yr[0]===":"?(Yr.shift(),yn.queue=y(Yr,x)):wn==="root"&&Yr[0]===":"?(Yr.shift(),yn[wn]=tt(Yr)):wn==="threshold"&&Yr[0]===":"?(Yr.shift(),yn[wn]=y(Yr,x)):fe(Qr,"htmx:syntax:error",{token:Yr.shift()})}Kr.push(yn)}Yr.length===Zr&&fe(Qr,"htmx:syntax:error",{token:Yr.shift()}),y(Yr,Je)}while(Yr[0]===","&&Yr.shift());return Wr&&(Wr[Gr]=Kr),Kr}function it(Qr){var Gr=te(Qr,"hx-trigger"),Wr=[];if(Gr){var Kr=Q.config.triggerSpecsCache;Wr=Kr&&Kr[Gr]||nt(Qr,Gr,Kr)}return Wr.length>0?Wr:h(Qr,"form")?[{trigger:"submit"}]:h(Qr,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:h(Qr,rt)?[{trigger:"change"}]:[{trigger:"click"}]}function at(Qr){ae(Qr).cancelled=!0}function ot(Qr,Gr,Wr){var Kr=ae(Qr);Kr.timeout=setTimeout(function(){se(Qr)&&Kr.cancelled!==!0&&(ct(Wr,Qr,Wt("hx:poll:trigger",{triggerSpec:Wr,target:Qr}))||Gr(Qr),ot(Qr,Gr,Wr))},Wr.pollInterval)}function st(Qr){return location.hostname===Qr.hostname&&ee(Qr,"href")&&ee(Qr,"href").indexOf("#")!==0}function lt(Qr,Gr,Wr){if(Qr.tagName==="A"&&st(Qr)&&(Qr.target===""||Qr.target==="_self")||Qr.tagName==="FORM"){Gr.boosted=!0;var Kr,Yr;if(Qr.tagName==="A")Kr="get",Yr=ee(Qr,"href");else{var Zr=ee(Qr,"method");Kr=Zr?Zr.toLowerCase():"get",Yr=ee(Qr,"action")}Wr.forEach(function(tn){ht(Qr,function(ln,dn){if(v(ln,Q.config.disableSelector)){m(ln);return}he(Kr,Yr,ln,dn)},Gr,tn,!0)})}}function ut(Qr,Gr){return!!((Qr.type==="submit"||Qr.type==="click")&&(Gr.tagName==="FORM"||h(Gr,'input[type="submit"], button')&&v(Gr,"form")!==null||Gr.tagName==="A"&&Gr.href&&(Gr.getAttribute("href")==="#"||Gr.getAttribute("href").indexOf("#")!==0)))}function ft(Qr,Gr){return ae(Qr).boosted&&Qr.tagName==="A"&&Gr.type==="click"&&(Gr.ctrlKey||Gr.metaKey)}function ct(Qr,Gr,Wr){var Kr=Qr.eventFilter;if(Kr)try{return Kr.call(Gr,Wr)!==!0}catch(Yr){return fe(re().body,"htmx:eventFilter:error",{error:Yr,source:Kr.source}),!0}return!1}function ht(Qr,Gr,Wr,Kr,Yr){var Zr=ae(Qr),tn;Kr.from?tn=Z(Qr,Kr.from):tn=[Qr],Kr.changed&&tn.forEach(function(ln){var dn=ae(ln);dn.lastValue=ln.value}),oe(tn,function(ln){var dn=function(yn){if(!se(Qr)){ln.removeEventListener(Kr.trigger,dn);return}if(!ft(Qr,yn)&&((Yr||ut(yn,Qr))&&yn.preventDefault(),!ct(Kr,Qr,yn))){var wn=ae(yn);if(wn.triggerSpec=Kr,wn.handledFor==null&&(wn.handledFor=[]),wn.handledFor.indexOf(Qr)<0){if(wn.handledFor.push(Qr),Kr.consume&&yn.stopPropagation(),Kr.target&&yn.target&&!h(yn.target,Kr.target))return;if(Kr.once){if(Zr.triggeredOnce)return;Zr.triggeredOnce=!0}if(Kr.changed){var kn=ae(ln);if(kn.lastValue===ln.value)return;kn.lastValue=ln.value}if(Zr.delayed&&clearTimeout(Zr.delayed),Zr.throttle)return;Kr.throttle>0?Zr.throttle||(Gr(Qr,yn),Zr.throttle=setTimeout(function(){Zr.throttle=null},Kr.throttle)):Kr.delay>0?Zr.delayed=setTimeout(function(){Gr(Qr,yn)},Kr.delay):(ce(Qr,"htmx:trigger"),Gr(Qr,yn))}}};Wr.listenerInfos==null&&(Wr.listenerInfos=[]),Wr.listenerInfos.push({trigger:Kr.trigger,listener:dn,on:ln}),ln.addEventListener(Kr.trigger,dn)})}var vt=!1,dt=null;function gt(){dt||(dt=function(){vt=!0},window.addEventListener("scroll",dt),setInterval(function(){vt&&(vt=!1,oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(Qr){pt(Qr)}))},200))}function pt(Qr){if(!o(Qr,"data-hx-revealed")&&X(Qr)){Qr.setAttribute("data-hx-revealed","true");var Gr=ae(Qr);Gr.initHash?ce(Qr,"revealed"):Qr.addEventListener("htmx:afterProcessNode",function(Wr){ce(Qr,"revealed")},{once:!0})}}function mt(Qr,Gr,Wr){for(var Kr=D(Wr),Yr=0;Yr<Kr.length;Yr++){var Zr=Kr[Yr].split(/:(.+)/);Zr[0]==="connect"&&xt(Qr,Zr[1],0),Zr[0]==="send"&&bt(Qr)}}function xt(Qr,Gr,Wr){if(se(Qr)){if(Gr.indexOf("/")==0){var Kr=location.hostname+(location.port?":"+location.port:"");location.protocol=="https:"?Gr="wss://"+Kr+Gr:location.protocol=="http:"&&(Gr="ws://"+Kr+Gr)}var Yr=Q.createWebSocket(Gr);Yr.onerror=function(Zr){fe(Qr,"htmx:wsError",{error:Zr,socket:Yr}),yt(Qr)},Yr.onclose=function(Zr){if([1006,1012,1013].indexOf(Zr.code)>=0){var tn=wt(Wr);setTimeout(function(){xt(Qr,Gr,Wr+1)},tn)}},Yr.onopen=function(Zr){Wr=0},ae(Qr).webSocket=Yr,Yr.addEventListener("message",function(Zr){if(!yt(Qr)){var tn=Zr.data;R(Qr,function(An){tn=An.transformResponse(tn,null,Qr)});for(var ln=T(Qr),dn=l(tn),yn=M(dn.children),wn=0;wn<yn.length;wn++){var kn=yn[wn];Ee(te(kn,"hx-swap-oob")||"true",kn,ln)}nr(ln.tasks)}})}}function yt(Qr){if(!se(Qr))return ae(Qr).webSocket.close(),!0}function bt(Qr){var Gr=c(Qr,function(Wr){return ae(Wr).webSocket!=null});Gr?Qr.addEventListener(it(Qr)[0].trigger,function(Wr){var Kr=ae(Gr).webSocket,Yr=xr(Qr,Gr),Zr=dr(Qr,"post"),tn=Zr.errors,ln=Zr.values,dn=Hr(Qr),yn=le(ln,dn),wn=yr(yn,Qr);if(wn.HEADERS=Yr,tn&&tn.length>0){ce(Qr,"htmx:validation:halted",tn);return}Kr.send(JSON.stringify(wn)),ut(Wr,Qr)&&Wr.preventDefault()}):fe(Qr,"htmx:noWebSocketSourceError")}function wt(Qr){var Gr=Q.config.wsReconnectDelay;if(typeof Gr=="function")return Gr(Qr);if(Gr==="full-jitter"){var Wr=Math.min(Qr,6),Kr=1e3*Math.pow(2,Wr);return Kr*Math.random()}b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function St(Qr,Gr,Wr){for(var Kr=D(Wr),Yr=0;Yr<Kr.length;Yr++){var Zr=Kr[Yr].split(/:(.+)/);Zr[0]==="connect"&&Et(Qr,Zr[1]),Zr[0]==="swap"&&Ct(Qr,Zr[1])}}function Et(Qr,Gr){var Wr=Q.createEventSource(Gr);Wr.onerror=function(Kr){fe(Qr,"htmx:sseError",{error:Kr,source:Wr}),Tt(Qr)},ae(Qr).sseEventSource=Wr}function Ct(Qr,Gr){var Wr=c(Qr,Ot);if(Wr){var Kr=ae(Wr).sseEventSource,Yr=function(Zr){if(!Tt(Wr)){if(!se(Qr)){Kr.removeEventListener(Gr,Yr);return}var tn=Zr.data;R(Qr,function(wn){tn=wn.transformResponse(tn,null,Qr)});var ln=wr(Qr),dn=ye(Qr),yn=T(Qr);je(ln.swapStyle,dn,Qr,tn,yn),nr(yn.tasks),ce(Qr,"htmx:sseMessage",Zr)}};ae(Qr).sseListener=Yr,Kr.addEventListener(Gr,Yr)}else fe(Qr,"htmx:noSSESourceError")}function Rt(Qr,Gr,Wr){var Kr=c(Qr,Ot);if(Kr){var Yr=ae(Kr).sseEventSource,Zr=function(){Tt(Kr)||(se(Qr)?Gr(Qr):Yr.removeEventListener(Wr,Zr))};ae(Qr).sseListener=Zr,Yr.addEventListener(Wr,Zr)}else fe(Qr,"htmx:noSSESourceError")}function Tt(Qr){if(!se(Qr))return ae(Qr).sseEventSource.close(),!0}function Ot(Qr){return ae(Qr).sseEventSource!=null}function qt(Qr,Gr,Wr,Kr){var Yr=function(){Wr.loaded||(Wr.loaded=!0,Gr(Qr))};Kr>0?setTimeout(Yr,Kr):Yr()}function Ht(Qr,Gr,Wr){var Kr=!1;return oe(w,function(Yr){if(o(Qr,"hx-"+Yr)){var Zr=te(Qr,"hx-"+Yr);Kr=!0,Gr.path=Zr,Gr.verb=Yr,Wr.forEach(function(tn){Lt(Qr,tn,Gr,function(ln,dn){if(v(ln,Q.config.disableSelector)){m(ln);return}he(Yr,Zr,ln,dn)})})}}),Kr}function Lt(Qr,Gr,Wr,Kr){if(Gr.sseEvent)Rt(Qr,Kr,Gr.sseEvent);else if(Gr.trigger==="revealed")gt(),ht(Qr,Kr,Wr,Gr),pt(Qr);else if(Gr.trigger==="intersect"){var Yr={};Gr.root&&(Yr.root=ue(Qr,Gr.root)),Gr.threshold&&(Yr.threshold=parseFloat(Gr.threshold));var Zr=new IntersectionObserver(function(tn){for(var ln=0;ln<tn.length;ln++){var dn=tn[ln];if(dn.isIntersecting){ce(Qr,"intersect");break}}},Yr);Zr.observe(Qr),ht(Qr,Kr,Wr,Gr)}else Gr.trigger==="load"?ct(Gr,Qr,Wt("load",{elt:Qr}))||qt(Qr,Kr,Wr,Gr.delay):Gr.pollInterval>0?(Wr.polling=!0,ot(Qr,Kr,Gr)):ht(Qr,Kr,Wr,Gr)}function At(Qr){if(!Qr.htmxExecuted&&Q.config.allowScriptTags&&(Qr.type==="text/javascript"||Qr.type==="module"||Qr.type==="")){var Gr=re().createElement("script");oe(Qr.attributes,function(Kr){Gr.setAttribute(Kr.name,Kr.value)}),Gr.textContent=Qr.textContent,Gr.async=!1,Q.config.inlineScriptNonce&&(Gr.nonce=Q.config.inlineScriptNonce);var Wr=Qr.parentElement;try{Wr.insertBefore(Gr,Qr)}catch(Kr){b(Kr)}finally{Qr.parentElement&&Qr.parentElement.removeChild(Qr)}}}function Nt(Qr){h(Qr,"script")&&At(Qr),oe(f(Qr,"script"),function(Gr){At(Gr)})}function It(Qr){var Gr=Qr.attributes;if(!Gr)return!1;for(var Wr=0;Wr<Gr.length;Wr++){var Kr=Gr[Wr].name;if(g(Kr,"hx-on:")||g(Kr,"data-hx-on:")||g(Kr,"hx-on-")||g(Kr,"data-hx-on-"))return!0}return!1}function kt(Qr){var Gr=null,Wr=[];if(It(Qr)&&Wr.push(Qr),document.evaluate)for(var Kr=document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]',Qr);Gr=Kr.iterateNext();)Wr.push(Gr);else if(typeof Qr.getElementsByTagName=="function")for(var Yr=Qr.getElementsByTagName("*"),Zr=0;Zr<Yr.length;Zr++)It(Yr[Zr])&&Wr.push(Yr[Zr]);return Wr}function Pt(Qr){if(Qr.querySelectorAll){var Gr=", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]",Wr=Qr.querySelectorAll(i+Gr+", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws], [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]");return Wr}else return[]}function Mt(Qr){var Gr=v(Qr.target,"button, input[type='submit']"),Wr=Dt(Qr);Wr&&(Wr.lastButtonClicked=Gr)}function Xt(Qr){var Gr=Dt(Qr);Gr&&(Gr.lastButtonClicked=null)}function Dt(Qr){var Gr=v(Qr.target,"button, input[type='submit']");if(Gr){var Wr=p("#"+ee(Gr,"form"))||v(Gr,"form");if(Wr)return ae(Wr)}}function Ut(Qr){Qr.addEventListener("click",Mt),Qr.addEventListener("focusin",Mt),Qr.addEventListener("focusout",Xt)}function Bt(Qr){for(var Gr=Ye(Qr),Wr=0,Kr=0;Kr<Gr.length;Kr++){let Yr=Gr[Kr];Yr==="{"?Wr++:Yr==="}"&&Wr--}return Wr}function Ft(Qr,Gr,Wr){var Kr=ae(Qr);Array.isArray(Kr.onHandlers)||(Kr.onHandlers=[]);var Yr,Zr=function(tn){return Tr(Qr,function(){Yr||(Yr=new Function("event",Wr)),Yr.call(Qr,tn)})};Qr.addEventListener(Gr,Zr),Kr.onHandlers.push({event:Gr,listener:Zr})}function Vt(Qr){var Gr=te(Qr,"hx-on");if(Gr){for(var Wr={},Kr=Gr.split(`
2
+
`),Yr=null,Zr=0;Kr.length>0;){var tn=Kr.shift(),ln=tn.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);Zr===0&&ln?(tn.split(":"),Yr=ln[1].slice(0,-1),Wr[Yr]=ln[2]):Wr[Yr]+=tn,Zr+=Bt(tn)}for(var dn in Wr)Ft(Qr,dn,Wr[dn])}}function jt(Qr){Ae(Qr);for(var Gr=0;Gr<Qr.attributes.length;Gr++){var Wr=Qr.attributes[Gr].name,Kr=Qr.attributes[Gr].value;if(g(Wr,"hx-on")||g(Wr,"data-hx-on")){var Yr=Wr.indexOf("-on")+3,Zr=Wr.slice(Yr,Yr+1);if(Zr==="-"||Zr===":"){var tn=Wr.slice(Yr+1);g(tn,":")?tn="htmx"+tn:g(tn,"-")?tn="htmx:"+tn.slice(1):g(tn,"htmx-")&&(tn="htmx:"+tn.slice(5)),Ft(Qr,tn,Kr)}}}}function _t(Qr){if(v(Qr,Q.config.disableSelector)){m(Qr);return}var Gr=ae(Qr);if(Gr.initHash!==Le(Qr)){Ne(Qr),Gr.initHash=Le(Qr),Vt(Qr),ce(Qr,"htmx:beforeProcessNode"),Qr.value&&(Gr.lastValue=Qr.value);var Wr=it(Qr),Kr=Ht(Qr,Gr,Wr);Kr||(ne(Qr,"hx-boost")==="true"?lt(Qr,Gr,Wr):o(Qr,"hx-trigger")&&Wr.forEach(function(tn){Lt(Qr,tn,Gr,function(){})})),(Qr.tagName==="FORM"||ee(Qr,"type")==="submit"&&o(Qr,"form"))&&Ut(Qr);var Yr=te(Qr,"hx-sse");Yr&&St(Qr,Gr,Yr);var Zr=te(Qr,"hx-ws");Zr&&mt(Qr,Gr,Zr),ce(Qr,"htmx:afterProcessNode")}}function zt(Qr){if(Qr=p(Qr),v(Qr,Q.config.disableSelector)){m(Qr);return}_t(Qr),oe(Pt(Qr),function(Gr){_t(Gr)}),oe(kt(Qr),jt)}function $t(Qr){return Qr.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}function Wt(Qr,Gr){var Wr;return window.CustomEvent&&typeof window.CustomEvent=="function"?Wr=new CustomEvent(Qr,{bubbles:!0,cancelable:!0,detail:Gr}):(Wr=re().createEvent("CustomEvent"),Wr.initCustomEvent(Qr,!0,!0,Gr)),Wr}function fe(Qr,Gr,Wr){ce(Qr,Gr,le({error:Gr},Wr))}function Gt(Qr){return Qr==="htmx:afterProcessNode"}function R(Qr,Gr){oe(Fr(Qr),function(Wr){try{Gr(Wr)}catch(Kr){b(Kr)}})}function b(Qr){console.error?console.error(Qr):console.log&&console.log("ERROR: ",Qr)}function ce(Qr,Gr,Wr){Qr=p(Qr),Wr==null&&(Wr={}),Wr.elt=Qr;var Kr=Wt(Gr,Wr);Q.logger&&!Gt(Gr)&&Q.logger(Qr,Gr,Wr),Wr.error&&(b(Wr.error),ce(Qr,"htmx:error",{errorInfo:Wr}));var Yr=Qr.dispatchEvent(Kr),Zr=$t(Gr);if(Yr&&Zr!==Gr){var tn=Wt(Zr,Kr.detail);Yr=Yr&&Qr.dispatchEvent(tn)}return R(Qr,function(ln){Yr=Yr&&ln.onEvent(Gr,Kr)!==!1&&!Kr.defaultPrevented}),Yr}var Jt=location.pathname+location.search;function Zt(){var Qr=re().querySelector("[hx-history-elt],[data-hx-history-elt]");return Qr||re().body}function Kt(Qr,Gr,Wr,Kr){if(U()){if(Q.config.historyCacheSize<=0){localStorage.removeItem("htmx-history-cache");return}Qr=B(Qr);for(var Yr=E(localStorage.getItem("htmx-history-cache"))||[],Zr=0;Zr<Yr.length;Zr++)if(Yr[Zr].url===Qr){Yr.splice(Zr,1);break}var tn={url:Qr,content:Gr,title:Wr,scroll:Kr};for(ce(re().body,"htmx:historyItemCreated",{item:tn,cache:Yr}),Yr.push(tn);Yr.length>Q.config.historyCacheSize;)Yr.shift();for(;Yr.length>0;)try{localStorage.setItem("htmx-history-cache",JSON.stringify(Yr));break}catch(ln){fe(re().body,"htmx:historyCacheError",{cause:ln,cache:Yr}),Yr.shift()}}}function Yt(Qr){if(!U())return null;Qr=B(Qr);for(var Gr=E(localStorage.getItem("htmx-history-cache"))||[],Wr=0;Wr<Gr.length;Wr++)if(Gr[Wr].url===Qr)return Gr[Wr];return null}function Qt(Qr){var Gr=Q.config.requestClass,Wr=Qr.cloneNode(!0);return oe(f(Wr,"."+Gr),function(Kr){n(Kr,Gr)}),Wr.innerHTML}function er(){var Qr=Zt(),Gr=Jt||location.pathname+location.search,Wr;try{Wr=re().querySelector('[hx-history="false" i],[data-hx-history="false" i]')}catch{Wr=re().querySelector('[hx-history="false"],[data-hx-history="false"]')}Wr||(ce(re().body,"htmx:beforeHistorySave",{path:Gr,historyElt:Qr}),Kt(Gr,Qt(Qr),re().title,window.scrollY)),Q.config.historyEnabled&&history.replaceState({htmx:!0},re().title,window.location.href)}function tr(Qr){Q.config.getCacheBusterParam&&(Qr=Qr.replace(/org\.htmx\.cache-buster=[^&]*&?/,""),(G(Qr,"&")||G(Qr,"?"))&&(Qr=Qr.slice(0,-1))),Q.config.historyEnabled&&history.pushState({htmx:!0},"",Qr),Jt=Qr}function rr(Qr){Q.config.historyEnabled&&history.replaceState({htmx:!0},"",Qr),Jt=Qr}function nr(Qr){oe(Qr,function(Gr){Gr.call()})}function ir(Qr){var Gr=new XMLHttpRequest,Wr={path:Qr,xhr:Gr};ce(re().body,"htmx:historyCacheMiss",Wr),Gr.open("GET",Qr,!0),Gr.setRequestHeader("HX-Request","true"),Gr.setRequestHeader("HX-History-Restore-Request","true"),Gr.setRequestHeader("HX-Current-URL",re().location.href),Gr.onload=function(){if(this.status>=200&&this.status<400){ce(re().body,"htmx:historyCacheMissLoad",Wr);var Kr=l(this.response);Kr=Kr.querySelector("[hx-history-elt],[data-hx-history-elt]")||Kr;var Yr=Zt(),Zr=T(Yr),tn=Ve(this.response);if(tn){var ln=C("title");ln?ln.innerHTML=tn:window.document.title=tn}Ue(Yr,Kr,Zr),nr(Zr.tasks),Jt=Qr,ce(re().body,"htmx:historyRestore",{path:Qr,cacheMiss:!0,serverResponse:this.response})}else fe(re().body,"htmx:historyCacheMissLoadError",Wr)},Gr.send()}function ar(Qr){er(),Qr=Qr||location.pathname+location.search;var Gr=Yt(Qr);if(Gr){var Wr=l(Gr.content),Kr=Zt(),Yr=T(Kr);Ue(Kr,Wr,Yr),nr(Yr.tasks),document.title=Gr.title,setTimeout(function(){window.scrollTo(0,Gr.scroll)},0),Jt=Qr,ce(re().body,"htmx:historyRestore",{path:Qr,item:Gr})}else Q.config.refreshOnHistoryMiss?window.location.reload(!0):ir(Qr)}function or(Qr){var Gr=me(Qr,"hx-indicator");return Gr==null&&(Gr=[Qr]),oe(Gr,function(Wr){var Kr=ae(Wr);Kr.requestCount=(Kr.requestCount||0)+1,Wr.classList.add.call(Wr.classList,Q.config.requestClass)}),Gr}function sr(Qr){var Gr=me(Qr,"hx-disabled-elt");return Gr==null&&(Gr=[]),oe(Gr,function(Wr){var Kr=ae(Wr);Kr.requestCount=(Kr.requestCount||0)+1,Wr.setAttribute("disabled","")}),Gr}function lr(Qr,Gr){oe(Qr,function(Wr){var Kr=ae(Wr);Kr.requestCount=(Kr.requestCount||0)-1,Kr.requestCount===0&&Wr.classList.remove.call(Wr.classList,Q.config.requestClass)}),oe(Gr,function(Wr){var Kr=ae(Wr);Kr.requestCount=(Kr.requestCount||0)-1,Kr.requestCount===0&&Wr.removeAttribute("disabled")})}function ur(Qr,Gr){for(var Wr=0;Wr<Qr.length;Wr++){var Kr=Qr[Wr];if(Kr.isSameNode(Gr))return!0}return!1}function fr(Qr){return Qr.name===""||Qr.name==null||Qr.disabled||v(Qr,"fieldset[disabled]")||Qr.type==="button"||Qr.type==="submit"||Qr.tagName==="image"||Qr.tagName==="reset"||Qr.tagName==="file"?!1:Qr.type==="checkbox"||Qr.type==="radio"?Qr.checked:!0}function cr(Qr,Gr,Wr){if(Qr!=null&&Gr!=null){var Kr=Wr[Qr];Kr===void 0?Wr[Qr]=Gr:Array.isArray(Kr)?Array.isArray(Gr)?Wr[Qr]=Kr.concat(Gr):Kr.push(Gr):Array.isArray(Gr)?Wr[Qr]=[Kr].concat(Gr):Wr[Qr]=[Kr,Gr]}}function hr(Qr,Gr,Wr,Kr,Yr){if(!(Kr==null||ur(Qr,Kr))){if(Qr.push(Kr),fr(Kr)){var Zr=ee(Kr,"name"),tn=Kr.value;Kr.multiple&&Kr.tagName==="SELECT"&&(tn=M(Kr.querySelectorAll("option:checked")).map(function(dn){return dn.value})),Kr.files&&(tn=M(Kr.files)),cr(Zr,tn,Gr),Yr&&vr(Kr,Wr)}if(h(Kr,"form")){var ln=Kr.elements;oe(ln,function(dn){hr(Qr,Gr,Wr,dn,Yr)})}}}function vr(Qr,Gr){Qr.willValidate&&(ce(Qr,"htmx:validation:validate"),Qr.checkValidity()||(Gr.push({elt:Qr,message:Qr.validationMessage,validity:Qr.validity}),ce(Qr,"htmx:validation:failed",{message:Qr.validationMessage,validity:Qr.validity})))}function dr(Qr,Gr){var Wr=[],Kr={},Yr={},Zr=[],tn=ae(Qr);tn.lastButtonClicked&&!se(tn.lastButtonClicked)&&(tn.lastButtonClicked=null);var ln=h(Qr,"form")&&Qr.noValidate!==!0||te(Qr,"hx-validate")==="true";if(tn.lastButtonClicked&&(ln=ln&&tn.lastButtonClicked.formNoValidate!==!0),Gr!=="get"&&hr(Wr,Yr,Zr,v(Qr,"form"),ln),hr(Wr,Kr,Zr,Qr,ln),tn.lastButtonClicked||Qr.tagName==="BUTTON"||Qr.tagName==="INPUT"&&ee(Qr,"type")==="submit"){var dn=tn.lastButtonClicked||Qr,yn=ee(dn,"name");cr(yn,dn.value,Yr)}var wn=me(Qr,"hx-include");return oe(wn,function(kn){hr(Wr,Kr,Zr,kn,ln),h(kn,"form")||oe(kn.querySelectorAll(rt),function(An){hr(Wr,Kr,Zr,An,ln)})}),Kr=le(Kr,Yr),{errors:Zr,values:Kr}}function gr(Qr,Gr,Wr){Qr!==""&&(Qr+="&"),String(Wr)==="[object Object]"&&(Wr=JSON.stringify(Wr));var Kr=encodeURIComponent(Wr);return Qr+=encodeURIComponent(Gr)+"="+Kr,Qr}function pr(Qr){var Gr="";for(var Wr in Qr)if(Qr.hasOwnProperty(Wr)){var Kr=Qr[Wr];Array.isArray(Kr)?oe(Kr,function(Yr){Gr=gr(Gr,Wr,Yr)}):Gr=gr(Gr,Wr,Kr)}return Gr}function mr(Qr){var Gr=new FormData;for(var Wr in Qr)if(Qr.hasOwnProperty(Wr)){var Kr=Qr[Wr];Array.isArray(Kr)?oe(Kr,function(Yr){Gr.append(Wr,Yr)}):Gr.append(Wr,Kr)}return Gr}function xr(Qr,Gr,Wr){var Kr={"HX-Request":"true","HX-Trigger":ee(Qr,"id"),"HX-Trigger-Name":ee(Qr,"name"),"HX-Target":te(Gr,"id"),"HX-Current-URL":re().location.href};return Rr(Qr,"hx-headers",!1,Kr),Wr!==void 0&&(Kr["HX-Prompt"]=Wr),ae(Qr).boosted&&(Kr["HX-Boosted"]="true"),Kr}function yr(Qr,Gr){var Wr=ne(Gr,"hx-params");if(Wr){if(Wr==="none")return{};if(Wr==="*")return Qr;if(Wr.indexOf("not ")===0)return oe(Wr.substr(4).split(","),function(Yr){Yr=Yr.trim(),delete Qr[Yr]}),Qr;var Kr={};return oe(Wr.split(","),function(Yr){Yr=Yr.trim(),Kr[Yr]=Qr[Yr]}),Kr}else return Qr}function br(Qr){return ee(Qr,"href")&&ee(Qr,"href").indexOf("#")>=0}function wr(Qr,Gr){var Wr=Gr||ne(Qr,"hx-swap"),Kr={swapStyle:ae(Qr).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ae(Qr).boosted&&!br(Qr)&&(Kr.show="top"),Wr){var Yr=D(Wr);if(Yr.length>0)for(var Zr=0;Zr<Yr.length;Zr++){var tn=Yr[Zr];if(tn.indexOf("swap:")===0)Kr.swapDelay=d(tn.substr(5));else if(tn.indexOf("settle:")===0)Kr.settleDelay=d(tn.substr(7));else if(tn.indexOf("transition:")===0)Kr.transition=tn.substr(11)==="true";else if(tn.indexOf("ignoreTitle:")===0)Kr.ignoreTitle=tn.substr(12)==="true";else if(tn.indexOf("scroll:")===0){var ln=tn.substr(7),dn=ln.split(":"),yn=dn.pop(),wn=dn.length>0?dn.join(":"):null;Kr.scroll=yn,Kr.scrollTarget=wn}else if(tn.indexOf("show:")===0){var kn=tn.substr(5),dn=kn.split(":"),An=dn.pop(),wn=dn.length>0?dn.join(":"):null;Kr.show=An,Kr.showTarget=wn}else if(tn.indexOf("focus-scroll:")===0){var Gn=tn.substr(13);Kr.focusScroll=Gn=="true"}else Zr==0?Kr.swapStyle=tn:b("Unknown modifier in hx-swap: "+tn)}}return Kr}function Sr(Qr){return ne(Qr,"hx-encoding")==="multipart/form-data"||h(Qr,"form")&&ee(Qr,"enctype")==="multipart/form-data"}function Er(Qr,Gr,Wr){var Kr=null;return R(Gr,function(Yr){Kr==null&&(Kr=Yr.encodeParameters(Qr,Wr,Gr))}),Kr??(Sr(Gr)?mr(Wr):pr(Wr))}function T(Qr){return{tasks:[],elts:[Qr]}}function Cr(Qr,Gr){var Wr=Qr[0],Kr=Qr[Qr.length-1];if(Gr.scroll){var Yr=null;Gr.scrollTarget&&(Yr=ue(Wr,Gr.scrollTarget)),Gr.scroll==="top"&&(Wr||Yr)&&(Yr=Yr||Wr,Yr.scrollTop=0),Gr.scroll==="bottom"&&(Kr||Yr)&&(Yr=Yr||Kr,Yr.scrollTop=Yr.scrollHeight)}if(Gr.show){var Yr=null;if(Gr.showTarget){var Zr=Gr.showTarget;Gr.showTarget==="window"&&(Zr="body"),Yr=ue(Wr,Zr)}Gr.show==="top"&&(Wr||Yr)&&(Yr=Yr||Wr,Yr.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})),Gr.show==="bottom"&&(Kr||Yr)&&(Yr=Yr||Kr,Yr.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior}))}}function Rr(Qr,Gr,Wr,Kr){if(Kr==null&&(Kr={}),Qr==null)return Kr;var Yr=te(Qr,Gr);if(Yr){var Zr=Yr.trim(),tn=Wr;if(Zr==="unset")return null;Zr.indexOf("javascript:")===0?(Zr=Zr.substr(11),tn=!0):Zr.indexOf("js:")===0&&(Zr=Zr.substr(3),tn=!0),Zr.indexOf("{")!==0&&(Zr="{"+Zr+"}");var ln;tn?ln=Tr(Qr,function(){return Function("return ("+Zr+")")()},{}):ln=E(Zr);for(var dn in ln)ln.hasOwnProperty(dn)&&Kr[dn]==null&&(Kr[dn]=ln[dn])}return Rr(u(Qr),Gr,Wr,Kr)}function Tr(Qr,Gr,Wr){return Q.config.allowEval?Gr():(fe(Qr,"htmx:evalDisallowedError"),Wr)}function Or(Qr,Gr){return Rr(Qr,"hx-vars",!0,Gr)}function qr(Qr,Gr){return Rr(Qr,"hx-vals",!1,Gr)}function Hr(Qr){return le(Or(Qr),qr(Qr))}function Lr(Qr,Gr,Wr){if(Wr!==null)try{Qr.setRequestHeader(Gr,Wr)}catch{Qr.setRequestHeader(Gr,encodeURIComponent(Wr)),Qr.setRequestHeader(Gr+"-URI-AutoEncoded","true")}}function Ar(Qr){if(Qr.responseURL&&typeof URL<"u")try{var Gr=new URL(Qr.responseURL);return Gr.pathname+Gr.search}catch{fe(re().body,"htmx:badResponseUrl",{url:Qr.responseURL})}}function O(Qr,Gr){return Gr.test(Qr.getAllResponseHeaders())}function Nr(Qr,Gr,Wr){return Qr=Qr.toLowerCase(),Wr?Wr instanceof Element||I(Wr,"String")?he(Qr,Gr,null,null,{targetOverride:p(Wr),returnPromise:!0}):he(Qr,Gr,p(Wr.source),Wr.event,{handler:Wr.handler,headers:Wr.headers,values:Wr.values,targetOverride:p(Wr.target),swapOverride:Wr.swap,select:Wr.select,returnPromise:!0}):he(Qr,Gr,null,null,{returnPromise:!0})}function Ir(Qr){for(var Gr=[];Qr;)Gr.push(Qr),Qr=Qr.parentElement;return Gr}function kr(Qr,Gr,Wr){var Kr,Yr;if(typeof URL=="function"){Yr=new URL(Gr,document.location.href);var Zr=document.location.origin;Kr=Zr===Yr.origin}else Yr=Gr,Kr=g(Gr,document.location.origin);return Q.config.selfRequestsOnly&&!Kr?!1:ce(Qr,"htmx:validateUrl",le({url:Yr,sameHost:Kr},Wr))}function he(Qr,Gr,Wr,Kr,Yr,Zr){var tn=null,ln=null;if(Yr=Yr??{},Yr.returnPromise&&typeof Promise<"u")var dn=new Promise(function(gn,En){tn=gn,ln=En});Wr==null&&(Wr=re().body);var yn=Yr.handler||Mr,wn=Yr.select||null;if(!se(Wr))return ie(tn),dn;var kn=Yr.targetOverride||ye(Wr);if(kn==null||kn==pe)return fe(Wr,"htmx:targetError",{target:te(Wr,"hx-target")}),ie(ln),dn;var An=ae(Wr),Gn=An.lastButtonClicked;if(Gn){var jn=ee(Gn,"formaction");jn!=null&&(Gr=jn);var Vn=ee(Gn,"formmethod");Vn!=null&&Vn.toLowerCase()!=="dialog"&&(Qr=Vn)}var ti=ne(Wr,"hx-confirm");if(Zr===void 0){var Ti=function(gn){return he(Qr,Gr,Wr,Kr,Yr,!!gn)},fi={target:kn,elt:Wr,path:Gr,verb:Qr,triggeringEvent:Kr,etc:Yr,issueRequest:Ti,question:ti};if(ce(Wr,"htmx:confirm",fi)===!1)return ie(tn),dn}var oi=Wr,Jn=ne(Wr,"hx-sync"),mi=null,ai=!1;if(Jn){var Ui=Jn.split(":"),gi=Ui[0].trim();if(gi==="this"?oi=xe(Wr,"hx-sync"):oi=ue(Wr,gi),Jn=(Ui[1]||"drop").trim(),An=ae(oi),Jn==="drop"&&An.xhr&&An.abortable!==!0)return ie(tn),dn;if(Jn==="abort"){if(An.xhr)return ie(tn),dn;ai=!0}else if(Jn==="replace")ce(oi,"htmx:abort");else if(Jn.indexOf("queue")===0){var ri=Jn.split(" ");mi=(ri[1]||"last").trim()}}if(An.xhr)if(An.abortable)ce(oi,"htmx:abort");else{if(mi==null){if(Kr){var xn=ae(Kr);xn&&xn.triggerSpec&&xn.triggerSpec.queue&&(mi=xn.triggerSpec.queue)}mi==null&&(mi="last")}return An.queuedRequests==null&&(An.queuedRequests=[]),mi==="first"&&An.queuedRequests.length===0?An.queuedRequests.push(function(){he(Qr,Gr,Wr,Kr,Yr)}):mi==="all"?An.queuedRequests.push(function(){he(Qr,Gr,Wr,Kr,Yr)}):mi==="last"&&(An.queuedRequests=[],An.queuedRequests.push(function(){he(Qr,Gr,Wr,Kr,Yr)})),ie(tn),dn}var en=new XMLHttpRequest;An.xhr=en,An.abortable=ai;var an=function(){if(An.xhr=null,An.abortable=!1,An.queuedRequests!=null&&An.queuedRequests.length>0){var gn=An.queuedRequests.shift();gn()}},pn=ne(Wr,"hx-prompt");if(pn){var mn=prompt(pn);if(mn===null||!ce(Wr,"htmx:prompt",{prompt:mn,target:kn}))return ie(tn),an(),dn}if(ti&&!Zr&&!confirm(ti))return ie(tn),an(),dn;var Sn=xr(Wr,kn,mn);Qr!=="get"&&!Sr(Wr)&&(Sn["Content-Type"]="application/x-www-form-urlencoded"),Yr.headers&&(Sn=le(Sn,Yr.headers));var vn=dr(Wr,Qr),bn=vn.errors,hn=vn.values;Yr.values&&(hn=le(hn,Yr.values));var On=Hr(Wr),Pn=le(hn,On),Nn=yr(Pn,Wr);Q.config.getCacheBusterParam&&Qr==="get"&&(Nn["org.htmx.cache-buster"]=ee(kn,"id")||"true"),(Gr==null||Gr==="")&&(Gr=re().location.href);var Dn=Rr(Wr,"hx-request"),Ln=ae(Wr).boosted,Fn=Q.config.methodsThatUseUrlParams.indexOf(Qr)>=0,Mn={boosted:Ln,useUrlParams:Fn,parameters:Nn,unfilteredParameters:Pn,headers:Sn,target:kn,verb:Qr,errors:bn,withCredentials:Yr.credentials||Dn.credentials||Q.config.withCredentials,timeout:Yr.timeout||Dn.timeout||Q.config.timeout,path:Gr,triggeringEvent:Kr};if(!ce(Wr,"htmx:configRequest",Mn))return ie(tn),an(),dn;if(Gr=Mn.path,Qr=Mn.verb,Sn=Mn.headers,Nn=Mn.parameters,bn=Mn.errors,Fn=Mn.useUrlParams,bn&&bn.length>0)return ce(Wr,"htmx:validation:halted",Mn),ie(tn),an(),dn;var Hn=Gr.split("#"),Wn=Hn[0],zn=Hn[1],nn=Gr;if(Fn){nn=Wn;var on=Object.keys(Nn).length!==0;on&&(nn.indexOf("?")<0?nn+="?":nn+="&",nn+=pr(Nn),zn&&(nn+="#"+zn))}if(!kr(Wr,nn,Mn))return fe(Wr,"htmx:invalidPath",Mn),ie(ln),dn;if(en.open(Qr.toUpperCase(),nn,!0),en.overrideMimeType("text/html"),en.withCredentials=Mn.withCredentials,en.timeout=Mn.timeout,!Dn.noHeaders){for(var Jr in Sn)if(Sn.hasOwnProperty(Jr)){var rn=Sn[Jr];Lr(en,Jr,rn)}}var cn={xhr:en,target:kn,requestConfig:Mn,etc:Yr,boosted:Ln,select:wn,pathInfo:{requestPath:Gr,finalRequestPath:nn,anchor:zn}};if(en.onload=function(){try{var gn=Ir(Wr);if(cn.pathInfo.responsePath=Ar(en),yn(Wr,cn),lr(sn,fn),ce(Wr,"htmx:afterRequest",cn),ce(Wr,"htmx:afterOnLoad",cn),!se(Wr)){for(var En=null;gn.length>0&&En==null;){var Tn=gn.shift();se(Tn)&&(En=Tn)}En&&(ce(En,"htmx:afterRequest",cn),ce(En,"htmx:afterOnLoad",cn))}ie(tn),an()}catch(Rn){throw fe(Wr,"htmx:onLoadError",le({error:Rn},cn)),Rn}},en.onerror=function(){lr(sn,fn),fe(Wr,"htmx:afterRequest",cn),fe(Wr,"htmx:sendError",cn),ie(ln),an()},en.onabort=function(){lr(sn,fn),fe(Wr,"htmx:afterRequest",cn),fe(Wr,"htmx:sendAbort",cn),ie(ln),an()},en.ontimeout=function(){lr(sn,fn),fe(Wr,"htmx:afterRequest",cn),fe(Wr,"htmx:timeout",cn),ie(ln),an()},!ce(Wr,"htmx:beforeRequest",cn))return ie(tn),an(),dn;var sn=or(Wr),fn=sr(Wr);oe(["loadstart","loadend","progress","abort"],function(gn){oe([en,en.upload],function(En){En.addEventListener(gn,function(Tn){ce(Wr,"htmx:xhr:"+gn,{lengthComputable:Tn.lengthComputable,loaded:Tn.loaded,total:Tn.total})})})}),ce(Wr,"htmx:beforeSend",cn);var un=Fn?null:Er(en,Wr,Nn);return en.send(un),dn}function Pr(Qr,Gr){var Wr=Gr.xhr,Kr=null,Yr=null;if(O(Wr,/HX-Push:/i)?(Kr=Wr.getResponseHeader("HX-Push"),Yr="push"):O(Wr,/HX-Push-Url:/i)?(Kr=Wr.getResponseHeader("HX-Push-Url"),Yr="push"):O(Wr,/HX-Replace-Url:/i)&&(Kr=Wr.getResponseHeader("HX-Replace-Url"),Yr="replace"),Kr)return Kr==="false"?{}:{type:Yr,path:Kr};var Zr=Gr.pathInfo.finalRequestPath,tn=Gr.pathInfo.responsePath,ln=ne(Qr,"hx-push-url"),dn=ne(Qr,"hx-replace-url"),yn=ae(Qr).boosted,wn=null,kn=null;return ln?(wn="push",kn=ln):dn?(wn="replace",kn=dn):yn&&(wn="push",kn=tn||Zr),kn?kn==="false"?{}:(kn==="true"&&(kn=tn||Zr),Gr.pathInfo.anchor&&kn.indexOf("#")===-1&&(kn=kn+"#"+Gr.pathInfo.anchor),{type:wn,path:kn}):{}}function Mr(Qr,Gr){var Wr=Gr.xhr,Kr=Gr.target,Yr=Gr.etc,Zr=Gr.requestConfig,tn=Gr.select;if(ce(Qr,"htmx:beforeOnLoad",Gr)){if(O(Wr,/HX-Trigger:/i)&&_e(Wr,"HX-Trigger",Qr),O(Wr,/HX-Location:/i)){er();var ln=Wr.getResponseHeader("HX-Location"),dn;ln.indexOf("{")===0&&(dn=E(ln),ln=dn.path,delete dn.path),Nr("GET",ln,dn).then(function(){tr(ln)});return}var yn=O(Wr,/HX-Refresh:/i)&&Wr.getResponseHeader("HX-Refresh")==="true";if(O(Wr,/HX-Redirect:/i)){location.href=Wr.getResponseHeader("HX-Redirect"),yn&&location.reload();return}if(yn){location.reload();return}O(Wr,/HX-Retarget:/i)&&(Wr.getResponseHeader("HX-Retarget")==="this"?Gr.target=Qr:Gr.target=ue(Qr,Wr.getResponseHeader("HX-Retarget")));var wn=Pr(Qr,Gr),kn=Wr.status>=200&&Wr.status<400&&Wr.status!==204,An=Wr.response,Gn=Wr.status>=400,jn=Q.config.ignoreTitle,Vn=le({shouldSwap:kn,serverResponse:An,isError:Gn,ignoreTitle:jn},Gr);if(ce(Kr,"htmx:beforeSwap",Vn)){if(Kr=Vn.target,An=Vn.serverResponse,Gn=Vn.isError,jn=Vn.ignoreTitle,Gr.target=Kr,Gr.failed=Gn,Gr.successful=!Gn,Vn.shouldSwap){Wr.status===286&&at(Qr),R(Qr,function(gi){An=gi.transformResponse(An,Wr,Qr)}),wn.type&&er();var ti=Yr.swapOverride;O(Wr,/HX-Reswap:/i)&&(ti=Wr.getResponseHeader("HX-Reswap"));var dn=wr(Qr,ti);dn.hasOwnProperty("ignoreTitle")&&(jn=dn.ignoreTitle),Kr.classList.add(Q.config.swappingClass);var Ti=null,fi=null,oi=function(){try{var gi=document.activeElement,ri={};try{ri={elt:gi,start:gi?gi.selectionStart:null,end:gi?gi.selectionEnd:null}}catch{}var xn;tn&&(xn=tn),O(Wr,/HX-Reselect:/i)&&(xn=Wr.getResponseHeader("HX-Reselect")),wn.type&&(ce(re().body,"htmx:beforeHistoryUpdate",le({history:wn},Gr)),wn.type==="push"?(tr(wn.path),ce(re().body,"htmx:pushedIntoHistory",{path:wn.path})):(rr(wn.path),ce(re().body,"htmx:replacedInHistory",{path:wn.path})));var en=T(Kr);if(je(dn.swapStyle,Kr,Qr,An,en,xn),ri.elt&&!se(ri.elt)&&ee(ri.elt,"id")){var an=document.getElementById(ee(ri.elt,"id")),pn={preventScroll:dn.focusScroll!==void 0?!dn.focusScroll:!Q.config.defaultFocusScroll};if(an){if(ri.start&&an.setSelectionRange)try{an.setSelectionRange(ri.start,ri.end)}catch{}an.focus(pn)}}if(Kr.classList.remove(Q.config.swappingClass),oe(en.elts,function(vn){vn.classList&&vn.classList.add(Q.config.settlingClass),ce(vn,"htmx:afterSwap",Gr)}),O(Wr,/HX-Trigger-After-Swap:/i)){var mn=Qr;se(Qr)||(mn=re().body),_e(Wr,"HX-Trigger-After-Swap",mn)}var Sn=function(){if(oe(en.tasks,function(On){On.call()}),oe(en.elts,function(On){On.classList&&On.classList.remove(Q.config.settlingClass),ce(On,"htmx:afterSettle",Gr)}),Gr.pathInfo.anchor){var vn=re().getElementById(Gr.pathInfo.anchor);vn&&vn.scrollIntoView({block:"start",behavior:"auto"})}if(en.title&&!jn){var bn=C("title");bn?bn.innerHTML=en.title:window.document.title=en.title}if(Cr(en.elts,dn),O(Wr,/HX-Trigger-After-Settle:/i)){var hn=Qr;se(Qr)||(hn=re().body),_e(Wr,"HX-Trigger-After-Settle",hn)}ie(Ti)};dn.settleDelay>0?setTimeout(Sn,dn.settleDelay):Sn()}catch(vn){throw fe(Qr,"htmx:swapError",Gr),ie(fi),vn}},Jn=Q.config.globalViewTransitions;if(dn.hasOwnProperty("transition")&&(Jn=dn.transition),Jn&&ce(Qr,"htmx:beforeTransition",Gr)&&typeof Promise<"u"&&document.startViewTransition){var mi=new Promise(function(gi,ri){Ti=gi,fi=ri}),ai=oi;oi=function(){document.startViewTransition(function(){return ai(),mi})}}dn.swapDelay>0?setTimeout(oi,dn.swapDelay):oi()}Gn&&fe(Qr,"htmx:responseError",le({error:"Response Status Error Code "+Wr.status+" from "+Gr.pathInfo.requestPath},Gr))}}}var Xr={};function Dr(){return{init:function(Qr){return null},onEvent:function(Qr,Gr){return!0},transformResponse:function(Qr,Gr,Wr){return Qr},isInlineSwap:function(Qr){return!1},handleSwap:function(Qr,Gr,Wr,Kr){return!1},encodeParameters:function(Qr,Gr,Wr){return null}}}function Ur(Qr,Gr){Gr.init&&Gr.init(r),Xr[Qr]=le(Dr(),Gr)}function Br(Qr){delete Xr[Qr]}function Fr(Qr,Gr,Wr){if(Qr==null)return Gr;Gr==null&&(Gr=[]),Wr==null&&(Wr=[]);var Kr=te(Qr,"hx-ext");return Kr&&oe(Kr.split(","),function(Yr){if(Yr=Yr.replace(/ /g,""),Yr.slice(0,7)=="ignore:"){Wr.push(Yr.slice(7));return}if(Wr.indexOf(Yr)<0){var Zr=Xr[Yr];Zr&&Gr.indexOf(Zr)<0&&Gr.push(Zr)}}),Fr(u(Qr),Gr,Wr)}var Vr=!1;re().addEventListener("DOMContentLoaded",function(){Vr=!0});function jr(Qr){Vr||re().readyState==="complete"?Qr():re().addEventListener("DOMContentLoaded",Qr)}function _r(){Q.config.includeIndicatorStyles!==!1&&re().head.insertAdjacentHTML("beforeend","<style> ."+Q.config.indicatorClass+"{opacity:0} ."+Q.config.requestClass+" ."+Q.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} ."+Q.config.requestClass+"."+Q.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} </style>")}function zr(){var Qr=re().querySelector('meta[name="htmx-config"]');return Qr?E(Qr.content):null}function $r(){var Qr=zr();Qr&&(Q.config=le(Q.config,Qr))}return jr(function(){$r(),_r();var Qr=re().body;zt(Qr);var Gr=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");Qr.addEventListener("htmx:abort",function(Kr){var Yr=Kr.target,Zr=ae(Yr);Zr&&Zr.xhr&&Zr.xhr.abort()});let Wr=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(Kr){Kr.state&&Kr.state.htmx?(ar(),oe(Gr,function(Yr){ce(Yr,"htmx:restored",{document:re(),triggerEvent:ce})})):Wr&&Wr(Kr)},setTimeout(function(){ce(Qr,"htmx:load",{}),Qr=null},0)}),Q}()})});var Ys=Qs((go,Ks)=>{(function(Qr,Gr){let Wr=Gr(Qr);typeof go=="object"&&typeof go.nodeName!="string"?Ks.exports=Wr:(Qr._hyperscript=Wr,"document"in Qr&&Qr._hyperscript.browserInit())})(typeof self<"u"?self:go,Qr=>{"use strict";let Gr={dynamicResolvers:[function(xn,en){if(xn==="Fixed")return Number(en).toFixed();if(xn.indexOf("Fixed:")===0){let an=xn.split(":")[1];return Number(en).toFixed(parseInt(an))}}],String:function(xn){return xn.toString?xn.toString():""+xn},Int:function(xn){return parseInt(xn)},Float:function(xn){return parseFloat(xn)},Number:function(xn){return Number(xn)},Date:function(xn){return new Date(xn)},Array:function(xn){return Array.from(xn)},JSON:function(xn){return JSON.stringify(xn)},Object:function(xn){return xn instanceof String&&(xn=xn.toString()),typeof xn=="string"?JSON.parse(xn):Object.assign({},xn)}},Wr={attributes:"_, script, data-script",defaultTransition:"all 500ms ease-in",disableSelector:"[disable-scripting], [data-disable-scripting]",hideShowStrategies:{},conversions:Gr};class Kr{static OP_TABLE={"+":"PLUS","-":"MINUS","*":"MULTIPLY","/":"DIVIDE",".":"PERIOD","..":"ELLIPSIS","\\":"BACKSLASH",":":"COLON","%":"PERCENT","|":"PIPE","!":"EXCLAMATION","?":"QUESTION","#":"POUND","&":"AMPERSAND",$:"DOLLAR",";":"SEMI",",":"COMMA","(":"L_PAREN",")":"R_PAREN","<":"L_ANG",">":"R_ANG","<=":"LTE_ANG",">=":"GTE_ANG","==":"EQ","===":"EQQ","!=":"NEQ","!==":"NEQQ","{":"L_BRACE","}":"R_BRACE","[":"L_BRACKET","]":"R_BRACKET","=":"EQUALS","~":"TILDE"};static isValidCSSClassChar(en){return Kr.isAlpha(en)||Kr.isNumeric(en)||en==="-"||en==="_"||en===":"}static isValidCSSIDChar(en){return Kr.isAlpha(en)||Kr.isNumeric(en)||en==="-"||en==="_"||en===":"}static isWhitespace(en){return en===" "||en===" "||Kr.isNewline(en)}static positionString(en){return"[Line: "+en.line+", Column: "+en.column+"]"}static isNewline(en){return en==="\r"||en===`
3
+
`}static isNumeric(en){return en>="0"&&en<="9"}static isAlpha(en){return en>="a"&&en<="z"||en>="A"&&en<="Z"}static isIdentifierChar(en,an){return en==="_"||en==="$"}static isReservedChar(en){return en==="`"||en==="^"}static isValidSingleQuoteStringStart(en){if(en.length>0){var an=en[en.length-1];if(an.type==="IDENTIFIER"||an.type==="CLASS_REF"||an.type==="ID_REF"||an.op&&(an.value===">"||an.value===")"))return!1}return!0}static tokenize(en,an){var pn=[],mn=en,Sn=0,vn=0,bn=1,hn="<START>",On=0;function Pn(){return an&&On===0}for(;Sn<mn.length;)if(un()==="-"&&gn()==="-"&&(Kr.isWhitespace(En(2))||En(2)===""||En(2)==="-")||un()==="/"&&gn()==="/"&&(Kr.isWhitespace(En(2))||En(2)===""||En(2)==="/"))Ln();else if(un()==="/"&&gn()==="*"&&(Kr.isWhitespace(En(2))||En(2)===""||En(2)==="*"))Fn();else if(Kr.isWhitespace(un()))pn.push(Bn());else if(!Rn()&&un()==="."&&(Kr.isAlpha(gn())||gn()==="{"||gn()==="-"))pn.push(Mn());else if(!Rn()&&un()==="#"&&(Kr.isAlpha(gn())||gn()==="{"))pn.push(nn());else if(un()==="["&&gn()==="@")pn.push(Hn());else if(un()==="@")pn.push(Wn());else if(un()==="*"&&Kr.isAlpha(gn()))pn.push(zn());else if(Pn()&&(Kr.isAlpha(un())||un()==="\\"))pn.push(on());else if(!Pn()&&(Kr.isAlpha(un())||Kr.isIdentifierChar(un())))pn.push(Jr());else if(Kr.isNumeric(un()))pn.push(rn());else if(!Pn()&&(un()==='"'||un()==="`"))pn.push(sn());else if(!Pn()&&un()==="'")Kr.isValidSingleQuoteStringStart(pn)?pn.push(sn()):pn.push(cn());else if(Kr.OP_TABLE[un()])hn==="$"&&un()==="{"&&On++,un()==="}"&&On--,pn.push(cn());else if(Pn()||Kr.isReservedChar(un()))pn.push(Dn("RESERVED",Tn()));else if(Sn<mn.length)throw Error("Unknown token: "+un()+" ");return new Yr(pn,[],mn);function Nn(Cn,In){var _n=Dn(Cn,In);return _n.op=!0,_n}function Dn(Cn,In){return{type:Cn,value:In||"",start:Sn,end:Sn+1,column:vn,line:bn}}function Ln(){for(;un()&&!Kr.isNewline(un());)Tn();Tn()}function Fn(){for(;un()&&!(un()==="*"&&gn()==="/");)Tn();Tn(),Tn()}function Mn(){var Cn=Dn("CLASS_REF"),In=Tn();if(un()==="{"){for(Cn.template=!0,In+=Tn();un()&&un()!=="}";)In+=Tn();if(un()!=="}")throw Error("Unterminated class reference");In+=Tn()}else for(;Kr.isValidCSSClassChar(un())||un()==="\\";)un()==="\\"&&Tn(),In+=Tn();return Cn.value=In,Cn.end=Sn,Cn}function Hn(){for(var Cn=Dn("ATTRIBUTE_REF"),In=Tn();Sn<mn.length&&un()!=="]";)In+=Tn();return un()==="]"&&(In+=Tn()),Cn.value=In,Cn.end=Sn,Cn}function Wn(){for(var Cn=Dn("ATTRIBUTE_REF"),In=Tn();Kr.isValidCSSIDChar(un());)In+=Tn();if(un()==="="){if(In+=Tn(),un()==='"'||un()==="'"){let _n=sn();In+=_n.value}else if(Kr.isAlpha(un())||Kr.isNumeric(un())||Kr.isIdentifierChar(un())){let _n=Jr();In+=_n.value}}return Cn.value=In,Cn.end=Sn,Cn}function zn(){for(var Cn=Dn("STYLE_REF"),In=Tn();Kr.isAlpha(un())||un()==="-";)In+=Tn();return Cn.value=In,Cn.end=Sn,Cn}function nn(){var Cn=Dn("ID_REF"),In=Tn();if(un()==="{"){for(Cn.template=!0,In+=Tn();un()&&un()!=="}";)In+=Tn();if(un()!=="}")throw Error("Unterminated id reference");Tn()}else for(;Kr.isValidCSSIDChar(un());)In+=Tn();return Cn.value=In,Cn.end=Sn,Cn}function on(){var Cn=Dn("IDENTIFIER"),In=Tn(),_n=In==="\\";for(_n&&(In="");(Kr.isAlpha(un())||Kr.isNumeric(un())||Kr.isIdentifierChar(un())||un()==="\\"||un()==="{"||un()==="}")&&!(un()==="$"&&_n===!1);)un()==="\\"?(_n=!0,Tn()):(_n=!1,In+=Tn());return un()==="!"&&In==="beep"&&(In+=Tn()),Cn.value=In,Cn.end=Sn,Cn}function Jr(){for(var Cn=Dn("IDENTIFIER"),In=Tn();Kr.isAlpha(un())||Kr.isNumeric(un())||Kr.isIdentifierChar(un());)In+=Tn();return un()==="!"&&In==="beep"&&(In+=Tn()),Cn.value=In,Cn.end=Sn,Cn}function rn(){for(var Cn=Dn("NUMBER"),In=Tn();Kr.isNumeric(un());)In+=Tn();for(un()==="."&&Kr.isNumeric(gn())&&(In+=Tn());Kr.isNumeric(un());)In+=Tn();for((un()==="e"||un()==="E")&&(Kr.isNumeric(gn())?In+=Tn():gn()==="-"&&(In+=Tn(),In+=Tn()));Kr.isNumeric(un());)In+=Tn();return Cn.value=In,Cn.end=Sn,Cn}function cn(){for(var Cn=Nn(),In=Tn();un()&&Kr.OP_TABLE[In+un()];)In+=Tn();return Cn.type=Kr.OP_TABLE[In],Cn.value=In,Cn.end=Sn,Cn}function sn(){var Cn=Dn("STRING"),In=Tn();Cn.template=In==="`";for(var _n="";un()&&un()!==In;)if(un()==="\\"){Tn();let Un=Tn();if(Un==="b")_n+="\b";else if(Un==="f")_n+="\f";else if(Un==="n")_n+=`
4
+
`;else if(Un==="r")_n+="\r";else if(Un==="t")_n+=" ";else if(Un==="v")_n+="\v";else if(Cn.template&&Un==="$")_n+="\\$";else if(Un==="x"){let ni=fn();if(Number.isNaN(ni))throw Error("Invalid hexadecimal escape at "+Kr.positionString(Cn));_n+=String.fromCharCode(ni)}else _n+=Un}else _n+=Tn();if(un()!==In)throw Error("Unterminated string at "+Kr.positionString(Cn));return Tn(),Cn.value=_n,Cn.end=Sn,Cn}function fn(){if(!un())return NaN;let In=16*Number.parseInt(Tn(),16);return un()?(In+=Number.parseInt(Tn(),16),In):NaN}function un(){return mn.charAt(Sn)}function gn(){return mn.charAt(Sn+1)}function En(Cn=1){return mn.charAt(Sn+Cn)}function Tn(){return hn=un(),Sn++,vn++,hn}function Rn(){return Kr.isAlpha(hn)||Kr.isNumeric(hn)||hn===")"||hn==='"'||hn==="'"||hn==="`"||hn==="}"||hn==="]"}function Bn(){for(var Cn=Dn("WHITESPACE"),In="";un()&&Kr.isWhitespace(un());)Kr.isNewline(un())&&(vn=0,bn++),In+=Tn();return Cn.value=In,Cn.end=Sn,Cn}}tokenize(en,an){return Kr.tokenize(en,an)}}class Yr{constructor(en,an,pn){this.tokens=en,this.consumed=an,this.source=pn,this.consumeWhitespace()}get list(){return this.tokens}_lastConsumed=null;consumeWhitespace(){for(;this.token(0,!0).type==="WHITESPACE";)this.consumed.push(this.tokens.shift())}raiseError(en,an){Zr.raiseParseError(en,an)}requireOpToken(en){var an=this.matchOpToken(en);if(an)return an;this.raiseError(this,"Expected '"+en+"' but found '"+this.currentToken().value+"'")}matchAnyOpToken(en,an,pn){for(var mn=0;mn<arguments.length;mn++){var Sn=arguments[mn],vn=this.matchOpToken(Sn);if(vn)return vn}}matchAnyToken(en,an,pn){for(var mn=0;mn<arguments.length;mn++){var Sn=arguments[mn],vn=this.matchToken(Sn);if(vn)return vn}}matchOpToken(en){if(this.currentToken()&&this.currentToken().op&&this.currentToken().value===en)return this.consumeToken()}requireTokenType(en,an,pn,mn){var Sn=this.matchTokenType(en,an,pn,mn);if(Sn)return Sn;this.raiseError(this,"Expected one of "+JSON.stringify([en,an,pn]))}matchTokenType(en,an,pn,mn){if(this.currentToken()&&this.currentToken().type&&[en,an,pn,mn].indexOf(this.currentToken().type)>=0)return this.consumeToken()}requireToken(en,an){var pn=this.matchToken(en,an);if(pn)return pn;this.raiseError(this,"Expected '"+en+"' but found '"+this.currentToken().value+"'")}peekToken(en,an,pn){if(an=an||0,pn=pn||"IDENTIFIER",this.tokens[an]&&this.tokens[an].value===en&&this.tokens[an].type===pn)return this.tokens[an]}matchToken(en,an){if(this.follows.indexOf(en)===-1&&(an=an||"IDENTIFIER",this.currentToken()&&this.currentToken().value===en&&this.currentToken().type===an))return this.consumeToken()}consumeToken(){var en=this.tokens.shift();return this.consumed.push(en),this._lastConsumed=en,this.consumeWhitespace(),en}consumeUntil(en,an){for(var pn=[],mn=this.token(0,!0);(an==null||mn.type!==an)&&(en==null||mn.value!==en)&&mn.type!=="EOF";){var Sn=this.tokens.shift();this.consumed.push(Sn),pn.push(mn),mn=this.token(0,!0)}return this.consumeWhitespace(),pn}lastWhitespace(){return this.consumed[this.consumed.length-1]&&this.consumed[this.consumed.length-1].type==="WHITESPACE"?this.consumed[this.consumed.length-1].value:""}consumeUntilWhitespace(){return this.consumeUntil(null,"WHITESPACE")}hasMore(){return this.tokens.length>0}token(en,an){var pn,mn=0;do{if(!an)for(;this.tokens[mn]&&this.tokens[mn].type==="WHITESPACE";)mn++;pn=this.tokens[mn],en--,mn++}while(en>-1);return pn||{type:"EOF",value:"<<<EOF>>>"}}currentToken(){return this.token(0)}lastMatch(){return this._lastConsumed}static sourceFor=function(){return this.programSource.substring(this.startToken.start,this.endToken.end)};static lineFor=function(){return this.programSource.split(`
5
+
`)[this.startToken.line-1]};follows=[];pushFollow(en){this.follows.push(en)}popFollow(){this.follows.pop()}clearFollows(){var en=this.follows;return this.follows=[],en}restoreFollows(en){this.follows=en}}class Zr{constructor(en){this.runtime=en,this.possessivesDisabled=!1,this.addGrammarElement("feature",function(an,pn,mn){if(mn.matchOpToken("(")){var Sn=an.requireElement("feature",mn);return mn.requireOpToken(")"),Sn}var vn=an.FEATURES[mn.currentToken().value||""];if(vn)return vn(an,pn,mn)}),this.addGrammarElement("command",function(an,pn,mn){if(mn.matchOpToken("(")){let bn=an.requireElement("command",mn);return mn.requireOpToken(")"),bn}var Sn=an.COMMANDS[mn.currentToken().value||""];let vn;return Sn?vn=Sn(an,pn,mn):mn.currentToken().type==="IDENTIFIER"&&(vn=an.parseElement("pseudoCommand",mn)),vn&&an.parseElement("indirectStatement",mn,vn)}),this.addGrammarElement("commandList",function(an,pn,mn){if(mn.hasMore()){var Sn=an.parseElement("command",mn);if(Sn){mn.matchToken("then");let vn=an.parseElement("commandList",mn);return vn&&(Sn.next=vn),Sn}}return{type:"emptyCommandListCommand",op:function(vn){return pn.findNext(this,vn)},execute:function(vn){return pn.unifiedExec(this,vn)}}}),this.addGrammarElement("leaf",function(an,pn,mn){var Sn=an.parseAnyOf(an.LEAF_EXPRESSIONS,mn);return Sn??an.parseElement("symbol",mn)}),this.addGrammarElement("indirectExpression",function(an,pn,mn,Sn){for(var vn=0;vn<an.INDIRECT_EXPRESSIONS.length;vn++){var bn=an.INDIRECT_EXPRESSIONS[vn];Sn.endToken=mn.lastMatch();var hn=an.parseElement(bn,mn,Sn);if(hn)return hn}return Sn}),this.addGrammarElement("indirectStatement",function(an,pn,mn,Sn){if(mn.matchToken("unless")){Sn.endToken=mn.lastMatch();var vn=an.requireElement("expression",mn),bn={type:"unlessStatementModifier",args:[vn],op:function(hn,On){return On?this.next:Sn},execute:function(hn){return pn.unifiedExec(this,hn)}};return Sn.parent=bn,bn}return Sn}),this.addGrammarElement("primaryExpression",function(an,pn,mn){var Sn=an.parseElement("leaf",mn);if(Sn)return an.parseElement("indirectExpression",mn,Sn);an.raiseParseError(mn,"Unexpected value: "+mn.currentToken().value)})}use(en){return en(this),this}GRAMMAR={};COMMANDS={};FEATURES={};LEAF_EXPRESSIONS=[];INDIRECT_EXPRESSIONS=[];initElt(en,an,pn){en.startToken=an,en.sourceFor=Yr.sourceFor,en.lineFor=Yr.lineFor,en.programSource=pn.source}parseElement(en,an,pn=void 0){var mn=this.GRAMMAR[en];if(mn){var Sn=an.currentToken(),vn=mn(this,this.runtime,an,pn);if(vn){this.initElt(vn,Sn,an),vn.endToken=vn.endToken||an.lastMatch();for(var pn=vn.root;pn!=null;)this.initElt(pn,Sn,an),pn=pn.root}return vn}}requireElement(en,an,pn,mn){var Sn=this.parseElement(en,an,mn);return Sn||Zr.raiseParseError(an,pn||"Expected "+en),Sn}parseAnyOf(en,an){for(var pn=0;pn<en.length;pn++){var mn=en[pn],Sn=this.parseElement(mn,an);if(Sn)return Sn}}addGrammarElement(en,an){this.GRAMMAR[en]=an}addCommand(en,an){var pn=en+"Command",mn=function(Sn,vn,bn){let hn=an(Sn,vn,bn);if(hn)return hn.type=pn,hn.execute=function(On){return On.meta.command=hn,vn.unifiedExec(this,On)},hn};this.GRAMMAR[pn]=mn,this.COMMANDS[en]=mn}addFeature(en,an){var pn=en+"Feature",mn=function(Sn,vn,bn){var hn=an(Sn,vn,bn);if(hn)return hn.isFeature=!0,hn.keyword=en,hn.type=pn,hn};this.GRAMMAR[pn]=mn,this.FEATURES[en]=mn}addLeafExpression(en,an){this.LEAF_EXPRESSIONS.push(en),this.addGrammarElement(en,an)}addIndirectExpression(en,an){this.INDIRECT_EXPRESSIONS.push(en),this.addGrammarElement(en,an)}static createParserContext(en){var an=en.currentToken(),pn=en.source,mn=pn.split(`
6
+
`),Sn=an&&an.line?an.line-1:mn.length-1,vn=mn[Sn],bn=an&&an.line?an.column:vn.length-1;return vn+`
7
+
`+" ".repeat(bn)+`^^
8
+
9
+
`}static raiseParseError(en,an){an=(an||"Unexpected Token : "+en.currentToken().value)+`
10
+
11
+
`+Zr.createParserContext(en);var pn=new Error(an);throw pn.tokens=en,pn}raiseParseError(en,an){Zr.raiseParseError(en,an)}parseHyperScript(en){var an=this.parseElement("hyperscript",en);if(en.hasMore()&&this.raiseParseError(en),an)return an}setParent(en,an){typeof en=="object"&&(en.parent=an,typeof an=="object"&&(an.children=an.children||new Set,an.children.add(en)),this.setParent(en.next,an))}commandStart(en){return this.COMMANDS[en.value||""]}featureStart(en){return this.FEATURES[en.value||""]}commandBoundary(en){return!!(en.value=="end"||en.value=="then"||en.value=="else"||en.value=="otherwise"||en.value==")"||this.commandStart(en)||this.featureStart(en)||en.type=="EOF")}parseStringTemplate(en){var an=[""];do if(an.push(en.lastWhitespace()),en.currentToken().value==="$"){en.consumeToken();var pn=en.matchOpToken("{");an.push(this.requireElement("expression",en)),pn&&en.requireOpToken("}"),an.push("")}else if(en.currentToken().value==="\\")en.consumeToken(),en.consumeToken();else{var mn=en.consumeToken();an[an.length-1]+=mn?mn.value:""}while(en.hasMore());return an.push(en.lastWhitespace()),an}ensureTerminated(en){let an=this.runtime;for(var pn={type:"implicitReturn",op:function(Sn){return Sn.meta.returned=!0,Sn.meta.resolve&&Sn.meta.resolve(),an.HALT},execute:function(Sn){}},mn=en;mn.next;)mn=mn.next;mn.next=pn}}class tn{constructor(en,an){this.lexer=en??new Kr,this.parser=an??new Zr(this).use(fi).use(oi),this.parser.runtime=this}matchesSelector(en,an){var pn=en.matches||en.matchesSelector||en.msMatchesSelector||en.mozMatchesSelector||en.webkitMatchesSelector||en.oMatchesSelector;return pn&&pn.call(en,an)}makeEvent(en,an){var pn;return Qr.Event&&typeof Qr.Event=="function"?(pn=new Event(en,{bubbles:!0,cancelable:!0,composed:!0}),pn.detail=an):(pn=document.createEvent("CustomEvent"),pn.initCustomEvent(en,!0,!0,an)),pn}triggerEvent(en,an,pn,mn){pn=pn||{},pn.sender=mn;var Sn=this.makeEvent(an,pn),vn=en.dispatchEvent(Sn);return vn}isArrayLike(en){return Array.isArray(en)||typeof NodeList<"u"&&(en instanceof NodeList||en instanceof HTMLCollection||en instanceof FileList)}isIterable(en){return typeof en=="object"&&Symbol.iterator in en&&typeof en[Symbol.iterator]=="function"}shouldAutoIterate(en){return en!=null&&en[Gn]||this.isArrayLike(en)}forEach(en,an){if(en!=null)if(this.isIterable(en))for(let mn of en)an(mn);else if(this.isArrayLike(en))for(var pn=0;pn<en.length;pn++)an(en[pn]);else an(en)}implicitLoop(en,an){if(this.shouldAutoIterate(en))for(let pn of en)an(pn);else an(en)}wrapArrays(en){for(var an=[],pn=0;pn<en.length;pn++){var mn=en[pn];Array.isArray(mn)?an.push(Promise.all(mn)):an.push(mn)}return an}unwrapAsyncs(en){for(var an=0;an<en.length;an++){var pn=en[an];if(pn.asyncWrapper&&(en[an]=pn.value),Array.isArray(pn))for(var mn=0;mn<pn.length;mn++){var Sn=pn[mn];Sn.asyncWrapper&&(pn[mn]=Sn.value)}}}static HALT={};HALT=tn.HALT;unifiedExec(en,an){for(;;){try{var pn=this.unifiedEval(en,an)}catch(mn){if(an.meta.handlingFinally)console.error(" Exception in finally block: ",mn),pn=tn.HALT;else if(this.registerHyperTrace(an,mn),an.meta.errorHandler&&!an.meta.handlingError){an.meta.handlingError=!0,an.locals[an.meta.errorSymbol]=mn,en=an.meta.errorHandler;continue}else an.meta.currentException=mn,pn=tn.HALT}if(pn==null){console.error(en," did not return a next element to execute! context: ",an);return}else if(pn.then){pn.then(mn=>{this.unifiedExec(mn,an)}).catch(mn=>{this.unifiedExec({op:function(){throw mn}},an)});return}else if(pn===tn.HALT)if(an.meta.finallyHandler&&!an.meta.handlingFinally)an.meta.handlingFinally=!0,en=an.meta.finallyHandler;else if(an.meta.onHalt&&an.meta.onHalt(),an.meta.currentException)if(an.meta.reject){an.meta.reject(an.meta.currentException);return}else throw an.meta.currentException;else return;else en=pn}}unifiedEval(en,an,pn){var mn=[an],Sn=!1,vn=!1;if(en.args)for(var bn=0;bn<en.args.length;bn++){var hn=en.args[bn];if(hn==null)mn.push(null);else if(Array.isArray(hn)){for(var On=[],Pn=0;Pn<hn.length;Pn++){var Nn=hn[Pn],Dn=Nn?Nn.evaluate(an):null;Dn&&(Dn.then?Sn=!0:Dn.asyncWrapper&&(vn=!0)),On.push(Dn)}mn.push(On)}else if(hn.evaluate){var Dn=hn.evaluate(an);if(Dn&&(Dn.then?Sn=!0:Dn.asyncWrapper&&(vn=!0)),mn.push(Dn),Dn){if(pn===!0)break}else if(pn===!1)break}else mn.push(hn)}return Sn?new Promise((Ln,Fn)=>{mn=this.wrapArrays(mn),Promise.all(mn).then(function(Mn){vn&&this.unwrapAsyncs(Mn);try{var Hn=en.op.apply(en,Mn);Ln(Hn)}catch(Wn){Fn(Wn)}}).catch(function(Mn){Fn(Mn)})}):(vn&&this.unwrapAsyncs(mn),en.op.apply(en,mn))}_scriptAttrs=null;getScriptAttributes(){return this._scriptAttrs==null&&(this._scriptAttrs=Wr.attributes.replace(/ /g,"").split(",")),this._scriptAttrs}getScript(en){for(var an=0;an<this.getScriptAttributes().length;an++){var pn=this.getScriptAttributes()[an];if(en.hasAttribute&&en.hasAttribute(pn))return en.getAttribute(pn)}return en instanceof HTMLScriptElement&&en.type==="text/hyperscript"?en.innerText:null}hyperscriptFeaturesMap=new WeakMap;getHyperscriptFeatures(en){var an=this.hyperscriptFeaturesMap.get(en);return typeof an>"u"&&en&&this.hyperscriptFeaturesMap.set(en,an={}),an}addFeatures(en,an){en&&(Object.assign(an.locals,this.getHyperscriptFeatures(en)),this.addFeatures(en.parentElement,an))}makeContext(en,an,pn,mn){return new kn(en,an,pn,mn,this)}getScriptSelector(){return this.getScriptAttributes().map(function(en){return"["+en+"]"}).join(", ")}convertValue(en,an){for(var pn=Gr.dynamicResolvers,mn=0;mn<pn.length;mn++){var Sn=pn[mn],vn=Sn(an,en);if(vn!==void 0)return vn}if(en==null)return null;var bn=Gr[an];if(bn)return bn(en);throw"Unknown conversion : "+an}parse(en){let an=this.lexer,pn=this.parser;var mn=an.tokenize(en);if(this.parser.commandStart(mn.currentToken())){var Sn=pn.requireElement("commandList",mn);return mn.hasMore()&&pn.raiseParseError(mn),pn.ensureTerminated(Sn),Sn}else if(pn.featureStart(mn.currentToken())){var vn=pn.requireElement("hyperscript",mn);return mn.hasMore()&&pn.raiseParseError(mn),vn}else{var bn=pn.requireElement("expression",mn);return mn.hasMore()&&pn.raiseParseError(mn),bn}}evaluateNoPromise(en,an){let pn=en.evaluate(an);if(pn.next)throw new Error(Yr.sourceFor.call(en)+" returned a Promise in a context that they are not allowed.");return pn}evaluate(en,an,pn){class mn extends EventTarget{constructor(On){super(),this.module=On}toString(){return this.module.id}}var Sn="document"in Qr?Qr.document.body:new mn(pn&&pn.module);an=Object.assign(this.makeContext(Sn,null,Sn,null),an||{});var vn=this.parse(en);if(vn.execute)return vn.execute(an),typeof an.meta.returnValue<"u"?an.meta.returnValue:an.result;return vn.apply?(vn.apply(Sn,Sn,pn),this.getHyperscriptFeatures(Sn)):vn.evaluate(an);function bn(){return{}}}processNode(en){var an=this.getScriptSelector();this.matchesSelector(en,an)&&this.initElement(en,en),en instanceof HTMLScriptElement&&en.type==="text/hyperscript"&&this.initElement(en,document.body),en.querySelectorAll&&this.forEach(en.querySelectorAll(an+", [type='text/hyperscript']"),pn=>{this.initElement(pn,pn instanceof HTMLScriptElement&&pn.type==="text/hyperscript"?document.body:pn)})}initElement(en,an){if(!(en.closest&&en.closest(Wr.disableSelector))){var pn=this.getInternalData(en);if(!pn.initialized){var mn=this.getScript(en);if(mn)try{pn.initialized=!0,pn.script=mn;let bn=this.lexer,hn=this.parser;var Sn=bn.tokenize(mn),vn=hn.parseHyperScript(Sn);if(!vn)return;vn.apply(an||en,en),setTimeout(()=>{this.triggerEvent(an||en,"load",{hyperscript:!0})},1)}catch(bn){this.triggerEvent(en,"exception",{error:bn}),console.error("hyperscript errors were found on the following element:",en,`
12
+
13
+
`,bn.message,bn.stack)}}}}internalDataMap=new WeakMap;getInternalData(en){var an=this.internalDataMap.get(en);return typeof an>"u"&&this.internalDataMap.set(en,an={}),an}typeCheck(en,an,pn){if(en==null&&pn)return!0;var mn=Object.prototype.toString.call(en).slice(8,-1);return mn===an}getElementScope(en){var an=en.meta&&en.meta.owner;if(an){var pn=this.getInternalData(an),mn="elementScope";en.meta.feature&&en.meta.feature.behavior&&(mn=en.meta.feature.behavior+"Scope");var Sn=jn(pn,mn);return Sn}else return{}}isReservedWord(en){return["meta","it","result","locals","event","target","detail","sender","body"].includes(en)}isHyperscriptContext(en){return en instanceof kn}resolveSymbol(en,an,pn){if(en==="me"||en==="my"||en==="I")return an.me;if(en==="it"||en==="its"||en==="result")return an.result;if(en==="you"||en==="your"||en==="yourself")return an.you;if(pn==="global")return Qr[en];if(pn==="element"){var mn=this.getElementScope(an);return mn[en]}else{if(pn==="local")return an.locals[en];if(an.meta&&an.meta.context){var Sn=an.meta.context[en];if(typeof Sn<"u"||an.meta.context.detail&&(Sn=an.meta.context.detail[en],typeof Sn<"u"))return Sn}if(this.isHyperscriptContext(an)&&!this.isReservedWord(en))var vn=an.locals[en];else var vn=an[en];if(typeof vn<"u")return vn;var mn=this.getElementScope(an);return vn=mn[en],typeof vn<"u"?vn:Qr[en]}}setSymbol(en,an,pn,mn){if(pn==="global")Qr[en]=mn;else if(pn==="element"){var Sn=this.getElementScope(an);Sn[en]=mn}else if(pn==="local")an.locals[en]=mn;else if(this.isHyperscriptContext(an)&&!this.isReservedWord(en)&&typeof an.locals[en]<"u")an.locals[en]=mn;else{var Sn=this.getElementScope(an),vn=Sn[en];typeof vn<"u"?Sn[en]=mn:this.isHyperscriptContext(an)&&!this.isReservedWord(en)?an.locals[en]=mn:an[en]=mn}}findNext(en,an){if(en)return en.resolveNext?en.resolveNext(an):en.next?en.next:this.findNext(en.parent,an)}flatGet(en,an,pn){if(en!=null){var mn=pn(en,an);if(typeof mn<"u")return mn;if(this.shouldAutoIterate(en)){var Sn=[];for(var vn of en){var bn=pn(vn,an);Sn.push(bn)}return Sn}}}resolveProperty(en,an){return this.flatGet(en,an,(pn,mn)=>pn[mn])}resolveAttribute(en,an){return this.flatGet(en,an,(pn,mn)=>pn.getAttribute&&pn.getAttribute(mn))}resolveStyle(en,an){return this.flatGet(en,an,(pn,mn)=>pn.style&&pn.style[mn])}resolveComputedStyle(en,an){return this.flatGet(en,an,(pn,mn)=>getComputedStyle(pn).getPropertyValue(mn))}assignToNamespace(en,an,pn,mn){let Sn;typeof document<"u"&&en===document.body?Sn=Qr:Sn=this.getHyperscriptFeatures(en);for(var vn;(vn=an.shift())!==void 0;){var bn=Sn[vn];bn==null&&(bn={},Sn[vn]=bn),Sn=bn}Sn[pn]=mn}getHyperTrace(en,an){for(var pn=[],mn=en;mn.meta.caller;)mn=mn.meta.caller;if(mn.meta.traceMap)return mn.meta.traceMap.get(an,pn)}registerHyperTrace(en,an){for(var pn=[],mn=null;en!=null;)pn.push(en),mn=en,en=en.meta.caller;if(mn.meta.traceMap==null&&(mn.meta.traceMap=new Map),!mn.meta.traceMap.get(an)){var Sn={trace:pn,print:function(vn){vn=vn||console.error,vn("hypertrace /// ");for(var bn=0,hn=0;hn<pn.length;hn++)bn=Math.max(bn,pn[hn].meta.feature.displayName.length);for(var hn=0;hn<pn.length;hn++){var On=pn[hn];vn(" ->",On.meta.feature.displayName.padEnd(bn+2),"-",On.meta.owner)}}};mn.meta.traceMap.set(an,Sn)}}escapeSelector(en){return en.replace(/[:&()\[\]\/]/g,function(an){return"\\"+an})}nullCheck(en,an){if(en==null)throw new Error("'"+an.sourceFor()+"' is null")}isEmpty(en){return en==null||en.length===0}doesExist(en){if(en==null)return!1;if(this.shouldAutoIterate(en)){for(let an of en)return!0;return!1}return!0}getRootNode(en){if(en&&en instanceof Node){var an=en.getRootNode();if(an instanceof Document||an instanceof ShadowRoot)return an}return document}getEventQueueFor(en,an){let pn=this.getInternalData(en);var mn=pn.eventQueues;mn==null&&(mn=new Map,pn.eventQueues=mn);var Sn=mn.get(an);return Sn==null&&(Sn={queue:[],executing:!1},mn.set(an,Sn)),Sn}beepValueToConsole(en,an,pn){if(this.triggerEvent(en,"hyperscript:beep",{element:en,expression:an,value:pn})){var mn;pn?pn instanceof An?mn="ElementCollection":pn.constructor?mn=pn.constructor.name:mn="unknown":mn="object (null)";var Sn=pn;mn==="String"?Sn='"'+Sn+'"':pn instanceof An&&(Sn=Array.from(pn)),console.log("///_ BEEP! The expression ("+Yr.sourceFor.call(an).replace("beep! ","")+") evaluates to:",Sn,"of type "+mn)}}hyperscriptUrl="document"in Qr&&document.currentScript?document.currentScript.src:null}function ln(){return document.cookie.split("; ").map(en=>{let an=en.split("=");return{name:an[0],value:decodeURIComponent(an[1])}})}function dn(xn){document.cookie=xn+"=;expires=Thu, 01 Jan 1970 00:00:00 GMT"}function yn(){for(let xn of ln())dn(xn.name)}let wn=new Proxy({},{get(xn,en){if(en==="then"||en==="asyncWrapper")return null;if(en==="length")return ln().length;if(en==="clear")return dn;if(en==="clearAll")return yn;if(typeof en=="string")if(isNaN(en)){let an=document.cookie.split("; ").find(pn=>pn.startsWith(en+"="))?.split("=")[1];if(an)return decodeURIComponent(an)}else return ln()[parseInt(en)];else if(en===Symbol.iterator)return ln()[en]},set(xn,en,an){var pn=null;return typeof an=="string"?(pn=encodeURIComponent(an),pn+=";samesite=lax"):(pn=encodeURIComponent(an.value),an.expires&&(pn+=";expires="+an.maxAge),an.maxAge&&(pn+=";max-age="+an.maxAge),an.partitioned&&(pn+=";partitioned="+an.partitioned),an.path&&(pn+=";path="+an.path),an.samesite&&(pn+=";samesite="+an.path),an.secure&&(pn+=";secure="+an.path)),document.cookie=en+"="+pn,!0}});class kn{constructor(en,an,pn,mn,Sn){this.meta={parser:Sn.parser,lexer:Sn.lexer,runtime:Sn,owner:en,feature:an,iterators:{},ctx:this},this.locals={cookies:wn},this.me=pn,this.you=void 0,this.result=void 0,this.event=mn,this.target=mn?mn.target:null,this.detail=mn?mn.detail:null,this.sender=mn&&mn.detail?mn.detail.sender:null,this.body="document"in Qr?document.body:null,Sn.addFeatures(en,this)}}class An{constructor(en,an,pn){this._css=en,this.relativeToElement=an,this.escape=pn,this[Gn]=!0}get css(){return this.escape?tn.prototype.escapeSelector(this._css):this._css}get className(){return this._css.substr(1)}get id(){return this.className()}contains(en){for(let an of this)if(an.contains(en))return!0;return!1}get length(){return this.selectMatches().length}[Symbol.iterator](){return this.selectMatches()[Symbol.iterator]()}selectMatches(){return tn.prototype.getRootNode(this.relativeToElement).querySelectorAll(this.css)}}let Gn=Symbol();function jn(xn,en){var an=xn[en];if(an)return an;var pn={};return xn[en]=pn,pn}function Vn(xn){try{return JSON.parse(xn)}catch(en){return ti(en),null}}function ti(xn){console.error?console.error(xn):console.log&&console.log("ERROR: ",xn)}function Ti(xn,en){return new(xn.bind.apply(xn,[xn].concat(en)))}function fi(xn){xn.addLeafExpression("parenthesized",function(nn,on,Jr){if(Jr.matchOpToken("(")){var rn=Jr.clearFollows();try{var cn=nn.requireElement("expression",Jr)}finally{Jr.restoreFollows(rn)}return Jr.requireOpToken(")"),cn}}),xn.addLeafExpression("string",function(nn,on,Jr){var rn=Jr.matchTokenType("STRING");if(rn){var cn=rn.value,sn;if(rn.template){var fn=Kr.tokenize(cn,!0);sn=nn.parseStringTemplate(fn)}else sn=[];return{type:"string",token:rn,args:sn,op:function(un){for(var gn="",En=1;En<arguments.length;En++){var Tn=arguments[En];Tn!==void 0&&(gn+=Tn)}return gn},evaluate:function(un){return sn.length===0?cn:on.unifiedEval(this,un)}}}}),xn.addGrammarElement("nakedString",function(nn,on,Jr){if(Jr.hasMore()){var rn=Jr.consumeUntilWhitespace();return Jr.matchTokenType("WHITESPACE"),{type:"nakedString",tokens:rn,evaluate:function(cn){return rn.map(function(sn){return sn.value}).join("")}}}}),xn.addLeafExpression("number",function(nn,on,Jr){var rn=Jr.matchTokenType("NUMBER");if(rn){var cn=rn,sn=parseFloat(rn.value);return{type:"number",value:sn,numberToken:cn,evaluate:function(){return sn}}}}),xn.addLeafExpression("idRef",function(nn,on,Jr){var rn=Jr.matchTokenType("ID_REF");if(rn&&rn.value)if(rn.template){var cn=rn.value.substring(2),sn=Kr.tokenize(cn),fn=nn.requireElement("expression",sn);return{type:"idRefTemplate",args:[fn],op:function(un,gn){return on.getRootNode(un.me).getElementById(gn)},evaluate:function(un){return on.unifiedEval(this,un)}}}else{let un=rn.value.substring(1);return{type:"idRef",css:rn.value,value:un,evaluate:function(gn){return on.getRootNode(gn.me).getElementById(un)}}}}),xn.addLeafExpression("classRef",function(nn,on,Jr){var rn=Jr.matchTokenType("CLASS_REF");if(rn&&rn.value)if(rn.template){var cn=rn.value.substring(2),sn=Kr.tokenize(cn),fn=nn.requireElement("expression",sn);return{type:"classRefTemplate",args:[fn],op:function(un,gn){return new An("."+gn,un.me,!0)},evaluate:function(un){return on.unifiedEval(this,un)}}}else{let un=rn.value;return{type:"classRef",css:un,evaluate:function(gn){return new An(un,gn.me,!0)}}}});class en extends An{constructor(on,Jr,rn){super(on,Jr),this.templateParts=rn,this.elements=rn.filter(cn=>cn instanceof Element)}get css(){let on="",Jr=0;for(let rn of this.templateParts)rn instanceof Element?on+="[data-hs-query-id='"+Jr+++"']":on+=rn;return on}[Symbol.iterator](){this.elements.forEach((Jr,rn)=>Jr.dataset.hsQueryId=rn);let on=super[Symbol.iterator]();return this.elements.forEach(Jr=>Jr.removeAttribute("data-hs-query-id")),on}}xn.addLeafExpression("queryRef",function(nn,on,Jr){var rn=Jr.matchOpToken("<");if(rn){var cn=Jr.consumeUntil("/");Jr.requireOpToken("/"),Jr.requireOpToken(">");var sn=cn.map(function(En){return En.type==="STRING"?'"'+En.value+'"':En.value}).join(""),fn,un,gn;return/\$[^=]/.test(sn)&&(fn=!0,un=Kr.tokenize(sn,!0),gn=nn.parseStringTemplate(un)),{type:"queryRef",css:sn,args:gn,op:function(En,...Tn){return fn?new en(sn,En.me,Tn):new An(sn,En.me)},evaluate:function(En){return on.unifiedEval(this,En)}}}}),xn.addLeafExpression("attributeRef",function(nn,on,Jr){var rn=Jr.matchTokenType("ATTRIBUTE_REF");if(rn&&rn.value){var cn=rn.value;if(cn.indexOf("[")===0)var sn=cn.substring(2,cn.length-1);else var sn=cn.substring(1);var fn="["+sn+"]",un=sn.split("="),gn=un[0],En=un[1];return En&&En.indexOf('"')===0&&(En=En.substring(1,En.length-1)),{type:"attributeRef",name:gn,css:fn,value:En,op:function(Tn){var Rn=Tn.you||Tn.me;if(Rn)return Rn.getAttribute(gn)},evaluate:function(Tn){return on.unifiedEval(this,Tn)}}}}),xn.addLeafExpression("styleRef",function(nn,on,Jr){var rn=Jr.matchTokenType("STYLE_REF");if(rn&&rn.value){var cn=rn.value.substr(1);return cn.startsWith("computed-")?(cn=cn.substr(9),{type:"computedStyleRef",name:cn,op:function(sn){var fn=sn.you||sn.me;if(fn)return on.resolveComputedStyle(fn,cn)},evaluate:function(sn){return on.unifiedEval(this,sn)}}):{type:"styleRef",name:cn,op:function(sn){var fn=sn.you||sn.me;if(fn)return on.resolveStyle(fn,cn)},evaluate:function(sn){return on.unifiedEval(this,sn)}}}}),xn.addGrammarElement("objectKey",function(nn,on,Jr){var rn;if(rn=Jr.matchTokenType("STRING"))return{type:"objectKey",key:rn.value,evaluate:function(){return rn.value}};if(Jr.matchOpToken("[")){var cn=nn.parseElement("expression",Jr);return Jr.requireOpToken("]"),{type:"objectKey",expr:cn,args:[cn],op:function(fn,un){return un},evaluate:function(fn){return on.unifiedEval(this,fn)}}}else{var sn="";do rn=Jr.matchTokenType("IDENTIFIER")||Jr.matchOpToken("-"),rn&&(sn+=rn.value);while(rn);return{type:"objectKey",key:sn,evaluate:function(){return sn}}}}),xn.addLeafExpression("objectLiteral",function(nn,on,Jr){if(Jr.matchOpToken("{")){var rn=[],cn=[];if(!Jr.matchOpToken("}")){do{var sn=nn.requireElement("objectKey",Jr);Jr.requireOpToken(":");var fn=nn.requireElement("expression",Jr);cn.push(fn),rn.push(sn)}while(Jr.matchOpToken(",")&&!Jr.peekToken("}",0,"R_BRACE"));Jr.requireOpToken("}")}return{type:"objectLiteral",args:[rn,cn],op:function(un,gn,En){for(var Tn={},Rn=0;Rn<gn.length;Rn++)Tn[gn[Rn]]=En[Rn];return Tn},evaluate:function(un){return on.unifiedEval(this,un)}}}}),xn.addGrammarElement("nakedNamedArgumentList",function(nn,on,Jr){var rn=[],cn=[];if(Jr.currentToken().type==="IDENTIFIER")do{var sn=Jr.requireTokenType("IDENTIFIER");Jr.requireOpToken(":");var fn=nn.requireElement("expression",Jr);cn.push(fn),rn.push({name:sn,value:fn})}while(Jr.matchOpToken(","));return{type:"namedArgumentList",fields:rn,args:[cn],op:function(un,gn){for(var En={_namedArgList_:!0},Tn=0;Tn<gn.length;Tn++){var Rn=rn[Tn];En[Rn.name.value]=gn[Tn]}return En},evaluate:function(un){return on.unifiedEval(this,un)}}}),xn.addGrammarElement("namedArgumentList",function(nn,on,Jr){if(Jr.matchOpToken("(")){var rn=nn.requireElement("nakedNamedArgumentList",Jr);return Jr.requireOpToken(")"),rn}}),xn.addGrammarElement("symbol",function(nn,on,Jr){var rn="default";Jr.matchToken("global")?rn="global":Jr.matchToken("element")||Jr.matchToken("module")?(rn="element",Jr.matchOpToken("'")&&Jr.requireToken("s")):Jr.matchToken("local")&&(rn="local");let cn=Jr.matchOpToken(":"),sn=Jr.matchTokenType("IDENTIFIER");if(sn&&sn.value){var fn=sn.value;return cn&&(fn=":"+fn),rn==="default"&&(fn.indexOf("$")===0&&(rn="global"),fn.indexOf(":")===0&&(rn="element")),{type:"symbol",token:sn,scope:rn,name:fn,evaluate:function(un){return on.resolveSymbol(fn,un,rn)}}}}),xn.addGrammarElement("implicitMeTarget",function(nn,on,Jr){return{type:"implicitMeTarget",evaluate:function(rn){return rn.you||rn.me}}}),xn.addLeafExpression("boolean",function(nn,on,Jr){var rn=Jr.matchToken("true")||Jr.matchToken("false");if(!rn)return;let cn=rn.value==="true";return{type:"boolean",evaluate:function(sn){return cn}}}),xn.addLeafExpression("null",function(nn,on,Jr){if(Jr.matchToken("null"))return{type:"null",evaluate:function(rn){return null}}}),xn.addLeafExpression("arrayLiteral",function(nn,on,Jr){if(Jr.matchOpToken("[")){var rn=[];if(!Jr.matchOpToken("]")){do{var cn=nn.requireElement("expression",Jr);rn.push(cn)}while(Jr.matchOpToken(","));Jr.requireOpToken("]")}return{type:"arrayLiteral",values:rn,args:[rn],op:function(sn,fn){return fn},evaluate:function(sn){return on.unifiedEval(this,sn)}}}}),xn.addLeafExpression("blockLiteral",function(nn,on,Jr){if(Jr.matchOpToken("\\")){var rn=[],cn=Jr.matchTokenType("IDENTIFIER");if(cn)for(rn.push(cn);Jr.matchOpToken(",");)rn.push(Jr.requireTokenType("IDENTIFIER"));Jr.requireOpToken("-"),Jr.requireOpToken(">");var sn=nn.requireElement("expression",Jr);return{type:"blockLiteral",args:rn,expr:sn,evaluate:function(fn){var un=function(){for(var gn=0;gn<rn.length;gn++)fn.locals[rn[gn].value]=arguments[gn];return sn.evaluate(fn)};return un}}}}),xn.addIndirectExpression("propertyAccess",function(nn,on,Jr,rn){if(Jr.matchOpToken(".")){var cn=Jr.requireTokenType("IDENTIFIER"),sn={type:"propertyAccess",root:rn,prop:cn,args:[rn],op:function(fn,un){var gn=on.resolveProperty(un,cn.value);return gn},evaluate:function(fn){return on.unifiedEval(this,fn)}};return nn.parseElement("indirectExpression",Jr,sn)}}),xn.addIndirectExpression("of",function(nn,on,Jr,rn){if(Jr.matchToken("of")){for(var cn=nn.requireElement("unaryExpression",Jr),sn=null,fn=rn;fn.root;)sn=fn,fn=fn.root;fn.type!=="symbol"&&fn.type!=="attributeRef"&&fn.type!=="styleRef"&&fn.type!=="computedStyleRef"&&nn.raiseParseError(Jr,"Cannot take a property of a non-symbol: "+fn.type);var un=fn.type==="attributeRef",gn=fn.type==="styleRef"||fn.type==="computedStyleRef";if(un||gn)var En=fn;var Tn=fn.name,Rn={type:"ofExpression",prop:fn.token,root:cn,attribute:En,expression:rn,args:[cn],op:function(Bn,Cn){return un?on.resolveAttribute(Cn,Tn):gn?fn.type==="computedStyleRef"?on.resolveComputedStyle(Cn,Tn):on.resolveStyle(Cn,Tn):on.resolveProperty(Cn,Tn)},evaluate:function(Bn){return on.unifiedEval(this,Bn)}};return fn.type==="attributeRef"&&(Rn.attribute=fn),sn?(sn.root=Rn,sn.args=[Rn]):rn=Rn,nn.parseElement("indirectExpression",Jr,rn)}}),xn.addIndirectExpression("possessive",function(nn,on,Jr,rn){if(!nn.possessivesDisabled){var cn=Jr.matchOpToken("'");if(cn||rn.type==="symbol"&&(rn.name==="my"||rn.name==="its"||rn.name==="your")&&(Jr.currentToken().type==="IDENTIFIER"||Jr.currentToken().type==="ATTRIBUTE_REF"||Jr.currentToken().type==="STYLE_REF")){cn&&Jr.requireToken("s");var sn,fn,un;sn=nn.parseElement("attributeRef",Jr),sn==null&&(fn=nn.parseElement("styleRef",Jr),fn==null&&(un=Jr.requireTokenType("IDENTIFIER")));var gn={type:"possessive",root:rn,attribute:sn||fn,prop:un,args:[rn],op:function(En,Tn){if(sn)var Rn=on.resolveAttribute(Tn,sn.name);else if(fn){var Rn;fn.type==="computedStyleRef"?Rn=on.resolveComputedStyle(Tn,fn.name):Rn=on.resolveStyle(Tn,fn.name)}else var Rn=on.resolveProperty(Tn,un.value);return Rn},evaluate:function(En){return on.unifiedEval(this,En)}};return nn.parseElement("indirectExpression",Jr,gn)}}}),xn.addIndirectExpression("inExpression",function(nn,on,Jr,rn){if(Jr.matchToken("in")){var cn=nn.requireElement("unaryExpression",Jr),sn={type:"inExpression",root:rn,args:[rn,cn],op:function(fn,un,gn){var En=[];if(un.css)on.implicitLoop(gn,function(Rn){for(var Bn=Rn.querySelectorAll(un.css),Cn=0;Cn<Bn.length;Cn++)En.push(Bn[Cn])});else if(un instanceof Element){var Tn=!1;if(on.implicitLoop(gn,function(Rn){Rn.contains(un)&&(Tn=!0)}),Tn)return un}else on.implicitLoop(un,function(Rn){on.implicitLoop(gn,function(Bn){Rn===Bn&&En.push(Rn)})});return En},evaluate:function(fn){return on.unifiedEval(this,fn)}};return nn.parseElement("indirectExpression",Jr,sn)}}),xn.addIndirectExpression("asExpression",function(nn,on,Jr,rn){if(Jr.matchToken("as")){Jr.matchToken("a")||Jr.matchToken("an");var cn=nn.requireElement("dotOrColonPath",Jr).evaluate(),sn={type:"asExpression",root:rn,args:[rn],op:function(fn,un){return on.convertValue(un,cn)},evaluate:function(fn){return on.unifiedEval(this,fn)}};return nn.parseElement("indirectExpression",Jr,sn)}}),xn.addIndirectExpression("functionCall",function(nn,on,Jr,rn){if(Jr.matchOpToken("(")){var cn=[];if(!Jr.matchOpToken(")")){do cn.push(nn.requireElement("expression",Jr));while(Jr.matchOpToken(","));Jr.requireOpToken(")")}if(rn.root)var sn={type:"functionCall",root:rn,argExressions:cn,args:[rn.root,cn],op:function(fn,un,gn){on.nullCheck(un,rn.root);var En=un[rn.prop.value];return on.nullCheck(En,rn),En.hyperfunc&&gn.push(fn),En.apply(un,gn)},evaluate:function(fn){return on.unifiedEval(this,fn)}};else var sn={type:"functionCall",root:rn,argExressions:cn,args:[rn,cn],op:function(un,gn,En){on.nullCheck(gn,rn),gn.hyperfunc&&En.push(un);var Tn=gn.apply(null,En);return Tn},evaluate:function(un){return on.unifiedEval(this,un)}};return nn.parseElement("indirectExpression",Jr,sn)}}),xn.addIndirectExpression("attributeRefAccess",function(nn,on,Jr,rn){var cn=nn.parseElement("attributeRef",Jr);if(cn){var sn={type:"attributeRefAccess",root:rn,attribute:cn,args:[rn],op:function(fn,un){var gn=on.resolveAttribute(un,cn.name);return gn},evaluate:function(fn){return on.unifiedEval(this,fn)}};return sn}}),xn.addIndirectExpression("arrayIndex",function(nn,on,Jr,rn){if(Jr.matchOpToken("[")){var cn=!1,sn=!1,fn=null,un=null;if(Jr.matchOpToken(".."))cn=!0,fn=nn.requireElement("expression",Jr);else if(fn=nn.requireElement("expression",Jr),Jr.matchOpToken("..")){sn=!0;var gn=Jr.currentToken();gn.type!=="R_BRACKET"&&(un=nn.parseElement("expression",Jr))}Jr.requireOpToken("]");var En={type:"arrayIndex",root:rn,prop:fn,firstIndex:fn,secondIndex:un,args:[rn,fn,un],op:function(Tn,Rn,Bn,Cn){return Rn==null?null:cn?(Bn<0&&(Bn=Rn.length+Bn),Rn.slice(0,Bn+1)):sn?Cn!=null?(Cn<0&&(Cn=Rn.length+Cn),Rn.slice(Bn,Cn+1)):Rn.slice(Bn):Rn[Bn]},evaluate:function(Tn){return on.unifiedEval(this,Tn)}};return nn.parseElement("indirectExpression",Jr,En)}});var an=["em","ex","cap","ch","ic","rem","lh","rlh","vw","vh","vi","vb","vmin","vmax","cm","mm","Q","pc","pt","px"];xn.addGrammarElement("postfixExpression",function(nn,on,Jr){var rn=nn.parseElement("negativeNumber",Jr);let cn=Jr.matchAnyToken.apply(Jr,an)||Jr.matchOpToken("%");if(cn)return{type:"stringPostfix",postfix:cn.value,args:[rn],op:function(gn,En){return""+En+cn.value},evaluate:function(gn){return on.unifiedEval(this,gn)}};var sn=null;if(Jr.matchToken("s")||Jr.matchToken("seconds")?sn=1e3:(Jr.matchToken("ms")||Jr.matchToken("milliseconds"))&&(sn=1),sn)return{type:"timeExpression",time:rn,factor:sn,args:[rn],op:function(gn,En){return En*sn},evaluate:function(gn){return on.unifiedEval(this,gn)}};if(Jr.matchOpToken(":")){var fn=Jr.requireTokenType("IDENTIFIER");if(!fn.value)return;var un=!Jr.matchOpToken("!");return{type:"typeCheck",typeName:fn,nullOk:un,args:[rn],op:function(gn,En){var Tn=on.typeCheck(En,this.typeName.value,un);if(Tn)return En;throw new Error("Typecheck failed! Expected: "+fn.value)},evaluate:function(gn){return on.unifiedEval(this,gn)}}}else return rn}),xn.addGrammarElement("logicalNot",function(nn,on,Jr){if(Jr.matchToken("not")){var rn=nn.requireElement("unaryExpression",Jr);return{type:"logicalNot",root:rn,args:[rn],op:function(cn,sn){return!sn},evaluate:function(cn){return on.unifiedEval(this,cn)}}}}),xn.addGrammarElement("noExpression",function(nn,on,Jr){if(Jr.matchToken("no")){var rn=nn.requireElement("unaryExpression",Jr);return{type:"noExpression",root:rn,args:[rn],op:function(cn,sn){return on.isEmpty(sn)},evaluate:function(cn){return on.unifiedEval(this,cn)}}}}),xn.addLeafExpression("some",function(nn,on,Jr){if(Jr.matchToken("some")){var rn=nn.requireElement("expression",Jr);return{type:"noExpression",root:rn,args:[rn],op:function(cn,sn){return!on.isEmpty(sn)},evaluate(cn){return on.unifiedEval(this,cn)}}}}),xn.addGrammarElement("negativeNumber",function(nn,on,Jr){if(Jr.matchOpToken("-")){var rn=nn.requireElement("negativeNumber",Jr);return{type:"negativeNumber",root:rn,args:[rn],op:function(cn,sn){return-1*sn},evaluate:function(cn){return on.unifiedEval(this,cn)}}}else return nn.requireElement("primaryExpression",Jr)}),xn.addGrammarElement("unaryExpression",function(nn,on,Jr){return Jr.matchToken("the"),nn.parseAnyOf(["beepExpression","logicalNot","relativePositionalExpression","positionalExpression","noExpression","postfixExpression"],Jr)}),xn.addGrammarElement("beepExpression",function(nn,on,Jr){if(Jr.matchToken("beep!")){var rn=nn.parseElement("unaryExpression",Jr);if(rn){rn.booped=!0;var cn=rn.evaluate;return rn.evaluate=function(sn){let fn=cn.apply(rn,arguments),un=sn.me;return on.beepValueToConsole(un,rn,fn),fn},rn}}});var pn=function(nn,on,Jr,rn){for(var cn=on.querySelectorAll(Jr),sn=0;sn<cn.length;sn++){var fn=cn[sn];if(fn.compareDocumentPosition(nn)===Node.DOCUMENT_POSITION_PRECEDING)return fn}if(rn)return cn[0]},mn=function(nn,on,Jr,rn){for(var cn=on.querySelectorAll(Jr),sn=cn.length-1;sn>=0;sn--){var fn=cn[sn];if(fn.compareDocumentPosition(nn)===Node.DOCUMENT_POSITION_FOLLOWING)return fn}if(rn)return cn[cn.length-1]},Sn=function(nn,on,Jr,rn){var cn=[];tn.prototype.forEach(on,function(gn){(gn.matches(Jr)||gn===nn)&&cn.push(gn)});for(var sn=0;sn<cn.length-1;sn++){var fn=cn[sn];if(fn===nn)return cn[sn+1]}if(rn){var un=cn[0];if(un&&un.matches(Jr))return un}},vn=function(nn,on,Jr,rn){return Sn(nn,Array.from(on).reverse(),Jr,rn)};xn.addGrammarElement("relativePositionalExpression",function(nn,on,Jr){var rn=Jr.matchAnyToken("next","previous");if(rn){var cn=rn.value==="next",sn=nn.parseElement("expression",Jr);if(Jr.matchToken("from")){Jr.pushFollow("in");try{var fn=nn.requireElement("unaryExpression",Jr)}finally{Jr.popFollow()}}else var fn=nn.requireElement("implicitMeTarget",Jr);var un=!1,gn;if(Jr.matchToken("in")){un=!0;var En=nn.requireElement("unaryExpression",Jr)}else Jr.matchToken("within")?gn=nn.requireElement("unaryExpression",Jr):gn=document.body;var Tn=!1;return Jr.matchToken("with")&&(Jr.requireToken("wrapping"),Tn=!0),{type:"relativePositionalExpression",from:fn,forwardSearch:cn,inSearch:un,wrapping:Tn,inElt:En,withinElt:gn,operator:rn.value,args:[sn,fn,En,gn],op:function(Rn,Bn,Cn,In,_n){var Un=Bn.css;if(Un==null)throw"Expected a CSS value to be returned by "+Yr.sourceFor.apply(sn);if(un){if(In)return cn?Sn(Cn,In,Un,Tn):vn(Cn,In,Un,Tn)}else if(_n)return cn?pn(Cn,_n,Un,Tn):mn(Cn,_n,Un,Tn)},evaluate:function(Rn){return on.unifiedEval(this,Rn)}}}}),xn.addGrammarElement("positionalExpression",function(nn,on,Jr){var rn=Jr.matchAnyToken("first","last","random");if(!rn)return;Jr.matchAnyToken("in","from","of");var cn=nn.requireElement("unaryExpression",Jr);let sn=rn.value;return{type:"positionalExpression",rhs:cn,operator:rn.value,args:[cn],op:function(fn,un){if(un&&!Array.isArray(un)&&(un.children?un=un.children:un=Array.from(un)),un){if(sn==="first")return un[0];if(sn==="last")return un[un.length-1];if(sn==="random")return un[Math.floor(Math.random()*un.length)]}},evaluate:function(fn){return on.unifiedEval(this,fn)}}}),xn.addGrammarElement("mathOperator",function(nn,on,Jr){var rn=nn.parseElement("unaryExpression",Jr),cn,sn=null;for(cn=Jr.matchAnyOpToken("+","-","*","/")||Jr.matchToken("mod");cn;){sn=sn||cn;var fn=cn.value;sn.value!==fn&&nn.raiseParseError(Jr,"You must parenthesize math operations with different operators");var un=nn.parseElement("unaryExpression",Jr);rn={type:"mathOperator",lhs:rn,rhs:un,operator:fn,args:[rn,un],op:function(gn,En,Tn){if(fn==="+")return En+Tn;if(fn==="-")return En-Tn;if(fn==="*")return En*Tn;if(fn==="/")return En/Tn;if(fn==="mod")return En%Tn},evaluate:function(gn){return on.unifiedEval(this,gn)}},cn=Jr.matchAnyOpToken("+","-","*","/")||Jr.matchToken("mod")}return rn}),xn.addGrammarElement("mathExpression",function(nn,on,Jr){return nn.parseAnyOf(["mathOperator","unaryExpression"],Jr)});function bn(nn,on,Jr){if(on.contains)return on.contains(Jr);if(on.includes)return on.includes(Jr);throw Error("The value of "+nn.sourceFor()+" does not have a contains or includes method on it")}function hn(nn,on,Jr){if(on.match)return!!on.match(Jr);if(on.matches)return on.matches(Jr);throw Error("The value of "+nn.sourceFor()+" does not have a match or matches method on it")}xn.addGrammarElement("comparisonOperator",function(nn,on,Jr){var rn=nn.parseElement("mathExpression",Jr),cn=Jr.matchAnyOpToken("<",">","<=",">=","==","===","!=","!=="),sn=cn?cn.value:null,fn=!0,un=!1;if(sn==null&&(Jr.matchToken("is")||Jr.matchToken("am")?Jr.matchToken("not")?Jr.matchToken("in")?sn="not in":Jr.matchToken("a")||Jr.matchToken("an")?(sn="not a",un=!0):Jr.matchToken("empty")?(sn="not empty",fn=!1):(Jr.matchToken("really")?sn="!==":sn="!=",Jr.matchToken("equal")&&Jr.matchToken("to")):Jr.matchToken("in")?sn="in":Jr.matchToken("a")||Jr.matchToken("an")?(sn="a",un=!0):Jr.matchToken("empty")?(sn="empty",fn=!1):Jr.matchToken("less")?(Jr.requireToken("than"),Jr.matchToken("or")?(Jr.requireToken("equal"),Jr.requireToken("to"),sn="<="):sn="<"):Jr.matchToken("greater")?(Jr.requireToken("than"),Jr.matchToken("or")?(Jr.requireToken("equal"),Jr.requireToken("to"),sn=">="):sn=">"):(Jr.matchToken("really")?sn="===":sn="==",Jr.matchToken("equal")&&Jr.matchToken("to")):Jr.matchToken("equals")?sn="==":Jr.matchToken("really")?(Jr.requireToken("equals"),sn="==="):Jr.matchToken("exist")||Jr.matchToken("exists")?(sn="exist",fn=!1):Jr.matchToken("matches")||Jr.matchToken("match")?sn="match":Jr.matchToken("contains")||Jr.matchToken("contain")?sn="contain":Jr.matchToken("includes")||Jr.matchToken("include")?sn="include":(Jr.matchToken("do")||Jr.matchToken("does"))&&(Jr.requireToken("not"),Jr.matchToken("matches")||Jr.matchToken("match")?sn="not match":Jr.matchToken("contains")||Jr.matchToken("contain")?sn="not contain":Jr.matchToken("exist")||Jr.matchToken("exist")?(sn="not exist",fn=!1):Jr.matchToken("include")?sn="not include":nn.raiseParseError(Jr,"Expected matches or contains"))),sn){var gn,En,Tn;un?(gn=Jr.requireTokenType("IDENTIFIER"),En=!Jr.matchOpToken("!")):fn&&(Tn=nn.requireElement("mathExpression",Jr),(sn==="match"||sn==="not match")&&(Tn=Tn.css?Tn.css:Tn));var Rn=rn;rn={type:"comparisonOperator",operator:sn,typeName:gn,nullOk:En,lhs:rn,rhs:Tn,args:[rn,Tn],op:function(Bn,Cn,In){if(sn==="==")return Cn==In;if(sn==="!=")return Cn!=In;if(sn==="===")return Cn===In;if(sn==="!==")return Cn!==In;if(sn==="match")return Cn!=null&&hn(Rn,Cn,In);if(sn==="not match")return Cn==null||!hn(Rn,Cn,In);if(sn==="in")return In!=null&&bn(Tn,In,Cn);if(sn==="not in")return In==null||!bn(Tn,In,Cn);if(sn==="contain")return Cn!=null&&bn(Rn,Cn,In);if(sn==="not contain")return Cn==null||!bn(Rn,Cn,In);if(sn==="include")return Cn!=null&&bn(Rn,Cn,In);if(sn==="not include")return Cn==null||!bn(Rn,Cn,In);if(sn==="===")return Cn===In;if(sn==="!==")return Cn!==In;if(sn==="<")return Cn<In;if(sn===">")return Cn>In;if(sn==="<=")return Cn<=In;if(sn===">=")return Cn>=In;if(sn==="empty")return on.isEmpty(Cn);if(sn==="not empty")return!on.isEmpty(Cn);if(sn==="exist")return on.doesExist(Cn);if(sn==="not exist")return!on.doesExist(Cn);if(sn==="a")return on.typeCheck(Cn,gn.value,En);if(sn==="not a")return!on.typeCheck(Cn,gn.value,En);throw"Unknown comparison : "+sn},evaluate:function(Bn){return on.unifiedEval(this,Bn)}}}return rn}),xn.addGrammarElement("comparisonExpression",function(nn,on,Jr){return nn.parseAnyOf(["comparisonOperator","mathExpression"],Jr)}),xn.addGrammarElement("logicalOperator",function(nn,on,Jr){var rn=nn.parseElement("comparisonExpression",Jr),cn,sn=null;for(cn=Jr.matchToken("and")||Jr.matchToken("or");cn;){sn=sn||cn,sn.value!==cn.value&&nn.raiseParseError(Jr,"You must parenthesize logical operations with different operators");var fn=nn.requireElement("comparisonExpression",Jr);let un=cn.value;rn={type:"logicalOperator",operator:un,lhs:rn,rhs:fn,args:[rn,fn],op:function(gn,En,Tn){return un==="and"?En&&Tn:En||Tn},evaluate:function(gn){return on.unifiedEval(this,gn,un==="or")}},cn=Jr.matchToken("and")||Jr.matchToken("or")}return rn}),xn.addGrammarElement("logicalExpression",function(nn,on,Jr){return nn.parseAnyOf(["logicalOperator","mathExpression"],Jr)}),xn.addGrammarElement("asyncExpression",function(nn,on,Jr){if(Jr.matchToken("async")){var rn=nn.requireElement("logicalExpression",Jr),cn={type:"asyncExpression",value:rn,evaluate:function(sn){return{asyncWrapper:!0,value:this.value.evaluate(sn)}}};return cn}else return nn.parseElement("logicalExpression",Jr)}),xn.addGrammarElement("expression",function(nn,on,Jr){return Jr.matchToken("the"),nn.parseElement("asyncExpression",Jr)}),xn.addGrammarElement("assignableExpression",function(nn,on,Jr){Jr.matchToken("the");var rn=nn.parseElement("primaryExpression",Jr);return rn&&(rn.type==="symbol"||rn.type==="ofExpression"||rn.type==="propertyAccess"||rn.type==="attributeRefAccess"||rn.type==="attributeRef"||rn.type==="styleRef"||rn.type==="arrayIndex"||rn.type==="possessive")||nn.raiseParseError(Jr,"A target expression must be writable. The expression type '"+(rn&&rn.type)+"' is not."),rn}),xn.addGrammarElement("hyperscript",function(nn,on,Jr){var rn=[];if(Jr.hasMore())for(;nn.featureStart(Jr.currentToken())||Jr.currentToken().value==="(";){var cn=nn.requireElement("feature",Jr);rn.push(cn),Jr.matchToken("end")}return{type:"hyperscript",features:rn,apply:function(sn,fn,un){for(let gn of rn)gn.install(sn,fn,un)}}});var On=function(nn){var on=[];if(nn.token(0).value==="("&&(nn.token(1).value===")"||nn.token(2).value===","||nn.token(2).value===")")){nn.matchOpToken("(");do on.push(nn.requireTokenType("IDENTIFIER"));while(nn.matchOpToken(","));nn.requireOpToken(")")}return on};xn.addFeature("on",function(nn,on,Jr){if(Jr.matchToken("on")){var rn=!1;Jr.matchToken("every")&&(rn=!0);var cn=[],sn=null;do{var fn=nn.requireElement("eventName",Jr,"Expected event name"),un=fn.evaluate();sn?sn=sn+" or "+un:sn="on "+un;var gn=On(Jr),En=null;Jr.matchOpToken("[")&&(En=nn.requireElement("expression",Jr),Jr.requireOpToken("]"));var Tn,Rn,Bn;if(Jr.currentToken().type==="NUMBER"){var Cn=Jr.consumeToken();if(!Cn.value)return;if(Tn=parseInt(Cn.value),Jr.matchToken("to")){var In=Jr.consumeToken();if(!In.value)return;Rn=parseInt(In.value)}else Jr.matchToken("and")&&(Bn=!0,Jr.requireToken("on"))}var _n,Un;if(un==="intersection"){if(_n={},Jr.matchToken("with")&&(_n.with=nn.requireElement("expression",Jr).evaluate()),Jr.matchToken("having"))do Jr.matchToken("margin")?_n.rootMargin=nn.requireElement("stringLike",Jr).evaluate():Jr.matchToken("threshold")?_n.threshold=nn.requireElement("expression",Jr).evaluate():nn.raiseParseError(Jr,"Unknown intersection config specification");while(Jr.matchToken("and"))}else if(un==="mutation")if(Un={},Jr.matchToken("of"))do if(Jr.matchToken("anything"))Un.attributes=!0,Un.subtree=!0,Un.characterData=!0,Un.childList=!0;else if(Jr.matchToken("childList"))Un.childList=!0;else if(Jr.matchToken("attributes"))Un.attributes=!0,Un.attributeOldValue=!0;else if(Jr.matchToken("subtree"))Un.subtree=!0;else if(Jr.matchToken("characterData"))Un.characterData=!0,Un.characterDataOldValue=!0;else if(Jr.currentToken().type==="ATTRIBUTE_REF"){var ni=Jr.consumeToken();Un.attributeFilter==null&&(Un.attributeFilter=[]),ni.value.indexOf("@")==0?Un.attributeFilter.push(ni.value.substring(1)):nn.raiseParseError(Jr,"Only shorthand attribute references are allowed here")}else nn.raiseParseError(Jr,"Unknown mutation config specification");while(Jr.matchToken("or"));else Un.attributes=!0,Un.characterData=!0,Un.childList=!0;var ii=null,Ri=!1;if(Jr.matchToken("from"))if(Jr.matchToken("elsewhere"))Ri=!0;else{Jr.pushFollow("or");try{ii=nn.requireElement("expression",Jr)}finally{Jr.popFollow()}ii||nn.raiseParseError(Jr,'Expected either target value or "elsewhere".')}if(ii===null&&Ri===!1&&Jr.matchToken("elsewhere")&&(Ri=!0),Jr.matchToken("in"))var Bi=nn.parseElement("unaryExpression",Jr);if(Jr.matchToken("debounced")){Jr.requireToken("at");var si=nn.requireElement("unaryExpression",Jr),ki=si.evaluate({})}else if(Jr.matchToken("throttled")){Jr.requireToken("at");var si=nn.requireElement("unaryExpression",Jr),Hi=si.evaluate({})}cn.push({execCount:0,every:rn,on:un,args:gn,filter:En,from:ii,inExpr:Bi,elsewhere:Ri,startCount:Tn,endCount:Rn,unbounded:Bn,debounceTime:ki,throttleTime:Hi,mutationSpec:Un,intersectionSpec:_n,debounced:void 0,lastExec:void 0})}while(Jr.matchToken("or"));var Ci=!0;if(!rn&&Jr.matchToken("queue"))if(Jr.matchToken("all"))var Ni=!0,Ci=!1;else if(Jr.matchToken("first"))var Ca=!0;else if(Jr.matchToken("none"))var _a=!0;else Jr.requireToken("last");var uo=nn.requireElement("commandList",Jr);nn.ensureTerminated(uo);var ts,co;if(Jr.matchToken("catch")&&(ts=Jr.requireTokenType("IDENTIFIER").value,co=nn.requireElement("commandList",Jr),nn.ensureTerminated(co)),Jr.matchToken("finally")){var Ws=nn.requireElement("commandList",Jr);nn.ensureTerminated(Ws)}var _i={displayName:sn,events:cn,start:uo,every:rn,execCount:0,errorHandler:co,errorSymbol:ts,execute:function(xi){let aa=on.getEventQueueFor(xi.me,_i);if(aa.executing&&rn===!1){if(_a||Ca&&aa.queue.length>0)return;Ci&&(aa.queue.length=0),aa.queue.push(xi);return}_i.execCount++,aa.executing=!0,xi.meta.onHalt=function(){aa.executing=!1;var Li=aa.queue.shift();Li&&setTimeout(function(){_i.execute(Li)},1)},xi.meta.reject=function(Li){console.error(Li.message?Li.message:Li);var $n=on.getHyperTrace(xi,Li);$n&&$n.print(),on.triggerEvent(xi.me,"exception",{error:Li})},uo.execute(xi)},install:function(xi,aa){for(let $n of _i.events){var Li;$n.elsewhere?Li=[document]:$n.from?Li=$n.from.evaluate(on.makeContext(xi,_i,xi,null)):Li=[xi],on.implicitLoop(Li,function(ji){var Ia=$n.on;if(ji==null){console.warn("'%s' feature ignored because target does not exists:",sn,xi);return}if($n.mutationSpec&&(Ia="hyperscript:mutation",new MutationObserver(function(pa,li){_i.executing||on.triggerEvent(ji,Ia,{mutationList:pa,observer:li})}).observe(ji,$n.mutationSpec)),$n.intersectionSpec){Ia="hyperscript:intersection";let ja=new IntersectionObserver(function(pa){for(let fo of pa){var li={observer:ja};li=Object.assign(li,fo),li.intersecting=fo.isIntersecting,on.triggerEvent(ji,Ia,li)}},$n.intersectionSpec);ja.observe(ji)}var Kl=ji.addEventListener||ji.on;Kl.call(ji,Ia,function ja(pa){if(typeof Node<"u"&&xi instanceof Node&&ji!==xi&&!xi.isConnected){ji.removeEventListener(Ia,ja);return}var li=on.makeContext(xi,_i,xi,pa);if(!($n.elsewhere&&xi.contains(pa.target))){$n.from&&(li.result=ji);for(let ho of $n.args){let Xs=li.event[ho.value];Xs!==void 0?li.locals[ho.value]=Xs:"detail"in li.event&&(li.locals[ho.value]=li.event.detail[ho.value])}if(li.meta.errorHandler=co,li.meta.errorSymbol=ts,li.meta.finallyHandler=Ws,$n.filter){var fo=li.meta.context;li.meta.context=li.event;try{var Yl=$n.filter.evaluate(li);if(!Yl)return}finally{li.meta.context=fo}}if($n.inExpr){for(var Oa=pa.target;;)if(Oa.matches&&Oa.matches($n.inExpr.css)){li.result=Oa;break}else if(Oa=Oa.parentElement,Oa==null)return}if($n.execCount++,$n.startCount){if($n.endCount){if($n.execCount<$n.startCount||$n.execCount>$n.endCount)return}else if($n.unbounded){if($n.execCount<$n.startCount)return}else if($n.execCount!==$n.startCount)return}if($n.debounceTime){$n.debounced&&clearTimeout($n.debounced),$n.debounced=setTimeout(function(){_i.execute(li)},$n.debounceTime);return}if($n.throttleTime){if($n.lastExec&&Date.now()<$n.lastExec+$n.throttleTime)return;$n.lastExec=Date.now()}_i.execute(li)}})})}}};return nn.setParent(uo,_i),_i}}),xn.addFeature("def",function(nn,on,Jr){if(Jr.matchToken("def")){var rn=nn.requireElement("dotOrColonPath",Jr),cn=rn.evaluate(),sn=cn.split("."),fn=sn.pop(),un=[];if(Jr.matchOpToken("(")&&!Jr.matchOpToken(")")){do un.push(Jr.requireTokenType("IDENTIFIER"));while(Jr.matchOpToken(","));Jr.requireOpToken(")")}var gn=nn.requireElement("commandList",Jr),En,Tn;if(Jr.matchToken("catch")&&(En=Jr.requireTokenType("IDENTIFIER").value,Tn=nn.parseElement("commandList",Jr)),Jr.matchToken("finally")){var Rn=nn.requireElement("commandList",Jr);nn.ensureTerminated(Rn)}var Bn={displayName:fn+"("+un.map(function(Cn){return Cn.value}).join(", ")+")",name:fn,args:un,start:gn,errorHandler:Tn,errorSymbol:En,finallyHandler:Rn,install:function(Cn,In){var _n=function(){var Un=on.makeContext(In,Bn,Cn,null);Un.meta.errorHandler=Tn,Un.meta.errorSymbol=En,Un.meta.finallyHandler=Rn;for(var ni=0;ni<un.length;ni++){var ii=un[ni],Ri=arguments[ni];ii&&(Un.locals[ii.value]=Ri)}Un.meta.caller=arguments[un.length],Un.meta.caller&&(Un.meta.callingCommand=Un.meta.caller.meta.command);var Bi,si=null,ki=new Promise(function(Hi,Ci){Bi=Hi,si=Ci});return gn.execute(Un),Un.meta.returned?Un.meta.returnValue:(Un.meta.resolve=Bi,Un.meta.reject=si,ki)};_n.hyperfunc=!0,_n.hypername=cn,on.assignToNamespace(Cn,sn,fn,_n)}};return nn.ensureTerminated(gn),Tn&&nn.ensureTerminated(Tn),nn.setParent(gn,Bn),Bn}}),xn.addFeature("set",function(nn,on,Jr){let rn=nn.parseElement("setCommand",Jr);if(rn){rn.target.scope!=="element"&&nn.raiseParseError(Jr,"variables declared at the feature level must be element scoped.");let cn={start:rn,install:function(sn,fn){rn&&rn.execute(on.makeContext(sn,cn,sn,null))}};return nn.ensureTerminated(rn),cn}}),xn.addFeature("init",function(nn,on,Jr){if(Jr.matchToken("init")){var rn=Jr.matchToken("immediately"),cn=nn.requireElement("commandList",Jr),sn={start:cn,install:function(fn,un){let gn=function(){cn&&cn.execute(on.makeContext(fn,sn,fn,null))};rn?gn():setTimeout(gn,0)}};return nn.ensureTerminated(cn),nn.setParent(cn,sn),sn}}),xn.addFeature("worker",function(nn,on,Jr){if(Jr.matchToken("worker")){nn.raiseParseError(Jr,"In order to use the 'worker' feature, include the _hyperscript worker plugin. See https://hyperscript.org/features/worker/ for more info.");return}}),xn.addFeature("behavior",function(nn,on,Jr){if(Jr.matchToken("behavior")){var rn=nn.requireElement("dotOrColonPath",Jr).evaluate(),cn=rn.split("."),sn=cn.pop(),fn=[];if(Jr.matchOpToken("(")&&!Jr.matchOpToken(")")){do fn.push(Jr.requireTokenType("IDENTIFIER").value);while(Jr.matchOpToken(","));Jr.requireOpToken(")")}for(var un=nn.requireElement("hyperscript",Jr),gn=0;gn<un.features.length;gn++){var En=un.features[gn];En.behavior=rn}return{install:function(Tn,Rn){on.assignToNamespace(Qr.document&&Qr.document.body,cn,sn,function(Bn,Cn,In){for(var _n=on.getInternalData(Bn),Un=jn(_n,rn+"Scope"),ni=0;ni<fn.length;ni++)Un[fn[ni]]=In[fn[ni]];un.apply(Bn,Cn)})}}}}),xn.addFeature("install",function(nn,on,Jr){if(Jr.matchToken("install")){var rn=nn.requireElement("dotOrColonPath",Jr).evaluate(),cn=rn.split("."),sn=nn.parseElement("namedArgumentList",Jr),fn;return fn={install:function(un,gn){on.unifiedEval({args:[sn],op:function(En,Tn){for(var Rn=Qr,Bn=0;Bn<cn.length;Bn++)if(Rn=Rn[cn[Bn]],typeof Rn!="object"&&typeof Rn!="function")throw new Error("No such behavior defined as "+rn);if(!(Rn instanceof Function))throw new Error(rn+" is not a behavior");Rn(un,gn,Tn)}},on.makeContext(un,fn,un,null))}}}}),xn.addGrammarElement("jsBody",function(nn,on,Jr){for(var rn=Jr.currentToken().start,cn=Jr.currentToken(),sn=[],fn="",un=!1;Jr.hasMore();){cn=Jr.consumeToken();var gn=Jr.token(0,!0);if(gn.type==="IDENTIFIER"&&gn.value==="end")break;un?cn.type==="IDENTIFIER"||cn.type==="NUMBER"?fn+=cn.value:(fn!==""&&sn.push(fn),fn="",un=!1):cn.type==="IDENTIFIER"&&cn.value==="function"&&(un=!0)}var En=cn.end+1;return{type:"jsBody",exposedFunctionNames:sn,jsSource:Jr.source.substring(rn,En)}}),xn.addFeature("js",function(nn,on,Jr){if(Jr.matchToken("js")){var rn=nn.requireElement("jsBody",Jr),cn=rn.jsSource+`
14
+
return { `+rn.exposedFunctionNames.map(function(fn){return fn+":"+fn}).join(",")+" } ",sn=new Function(cn);return{jsSource:cn,function:sn,exposedFunctionNames:rn.exposedFunctionNames,install:function(){Object.assign(Qr,sn())}}}}),xn.addCommand("js",function(nn,on,Jr){if(Jr.matchToken("js")){var rn=[];if(Jr.matchOpToken("(")&&!Jr.matchOpToken(")")){do{var cn=Jr.requireTokenType("IDENTIFIER");rn.push(cn.value)}while(Jr.matchOpToken(","));Jr.requireOpToken(")")}var sn=nn.requireElement("jsBody",Jr);Jr.matchToken("end");var fn=Ti(Function,rn.concat([sn.jsSource])),un={jsSource:sn.jsSource,function:fn,inputs:rn,op:function(gn){var En=[];rn.forEach(function(Rn){En.push(on.resolveSymbol(Rn,gn,"default"))});var Tn=fn.apply(Qr,En);return Tn&&typeof Tn.then=="function"?new Promise(function(Rn){Tn.then(function(Bn){gn.result=Bn,Rn(on.findNext(this,gn))})}):(gn.result=Tn,on.findNext(this,gn))}};return un}}),xn.addCommand("async",function(nn,on,Jr){if(Jr.matchToken("async")){if(Jr.matchToken("do")){for(var rn=nn.requireElement("commandList",Jr),cn=rn;cn.next;)cn=cn.next;cn.next=on.HALT,Jr.requireToken("end")}else var rn=nn.requireElement("command",Jr);var sn={body:rn,op:function(fn){return setTimeout(function(){rn.execute(fn)}),on.findNext(this,fn)}};return nn.setParent(rn,sn),sn}}),xn.addCommand("tell",function(nn,on,Jr){var rn=Jr.currentToken();if(Jr.matchToken("tell")){var cn=nn.requireElement("expression",Jr),sn=nn.requireElement("commandList",Jr);Jr.hasMore()&&!nn.featureStart(Jr.currentToken())&&Jr.requireToken("end");var fn="tell_"+rn.start,un={value:cn,body:sn,args:[cn],resolveNext:function(gn){var En=gn.meta.iterators[fn];return En.index<En.value.length?(gn.you=En.value[En.index++],sn):(gn.you=En.originalYou,this.next?this.next:on.findNext(this.parent,gn))},op:function(gn,En){return En==null?En=[]:Array.isArray(En)||En instanceof NodeList||(En=[En]),gn.meta.iterators[fn]={originalYou:gn.you,index:0,value:En},this.resolveNext(gn)}};return nn.setParent(sn,un),un}}),xn.addCommand("wait",function(nn,on,Jr){if(Jr.matchToken("wait")){var rn;if(Jr.matchToken("for")){Jr.matchToken("a");var cn=[];do{var sn=Jr.token(0);sn.type==="NUMBER"||sn.type==="L_PAREN"?cn.push({time:nn.requireElement("expression",Jr).evaluate()}):cn.push({name:nn.requireElement("dotOrColonPath",Jr,"Expected event name").evaluate(),args:On(Jr)})}while(Jr.matchToken("or"));if(Jr.matchToken("from"))var fn=nn.requireElement("expression",Jr);return rn={event:cn,on:fn,args:[fn],op:function(gn,En){var Tn=En||gn.me;if(!(Tn instanceof EventTarget))throw new Error("Not a valid event target: "+this.on.sourceFor());return new Promise(Rn=>{var Bn=!1;for(let In of cn){var Cn=_n=>{if(gn.result=_n,In.args)for(let Un of In.args)gn.locals[Un.value]=_n[Un.value]||(_n.detail?_n.detail[Un.value]:null);Bn||(Bn=!0,Rn(on.findNext(this,gn)))};In.name?Tn.addEventListener(In.name,Cn,{once:!0}):In.time!=null&&setTimeout(Cn,In.time,In.time)}})}},rn}else{var un;return Jr.matchToken("a")?(Jr.requireToken("tick"),un=0):un=nn.requireElement("expression",Jr),rn={type:"waitCmd",time:un,args:[un],op:function(gn,En){return new Promise(Tn=>{setTimeout(()=>{Tn(on.findNext(this,gn))},En)})},execute:function(gn){return on.unifiedExec(this,gn)}},rn}}}),xn.addGrammarElement("dotOrColonPath",function(nn,on,Jr){var rn=Jr.matchTokenType("IDENTIFIER");if(rn){var cn=[rn.value],sn=Jr.matchOpToken(".")||Jr.matchOpToken(":");if(sn)do cn.push(Jr.requireTokenType("IDENTIFIER","NUMBER").value);while(Jr.matchOpToken(sn.value));return{type:"dotOrColonPath",path:cn,evaluate:function(){return cn.join(sn?sn.value:"")}}}}),xn.addGrammarElement("eventName",function(nn,on,Jr){var rn;return(rn=Jr.matchTokenType("STRING"))?{evaluate:function(){return rn.value}}:nn.parseElement("dotOrColonPath",Jr)});function Pn(nn,on,Jr,rn){var cn=on.requireElement("eventName",rn),sn=on.parseElement("namedArgumentList",rn);if(nn==="send"&&rn.matchToken("to")||nn==="trigger"&&rn.matchToken("on"))var fn=on.requireElement("expression",rn);else var fn=on.requireElement("implicitMeTarget",rn);var un={eventName:cn,details:sn,to:fn,args:[fn,cn,sn],op:function(gn,En,Tn,Rn){return Jr.nullCheck(En,fn),Jr.implicitLoop(En,function(Bn){Jr.triggerEvent(Bn,Tn,Rn,gn.me)}),Jr.findNext(un,gn)}};return un}xn.addCommand("trigger",function(nn,on,Jr){if(Jr.matchToken("trigger"))return Pn("trigger",nn,on,Jr)}),xn.addCommand("send",function(nn,on,Jr){if(Jr.matchToken("send"))return Pn("send",nn,on,Jr)});var Nn=function(nn,on,Jr,rn){if(rn)if(nn.commandBoundary(Jr.currentToken()))nn.raiseParseError(Jr,"'return' commands must return a value. If you do not wish to return a value, use 'exit' instead.");else var cn=nn.requireElement("expression",Jr);var sn={value:cn,args:[cn],op:function(fn,un){var gn=fn.meta.resolve;return fn.meta.returned=!0,fn.meta.returnValue=un,gn&&(un?gn(un):gn()),on.HALT}};return sn};xn.addCommand("return",function(nn,on,Jr){if(Jr.matchToken("return"))return Nn(nn,on,Jr,!0)}),xn.addCommand("exit",function(nn,on,Jr){if(Jr.matchToken("exit"))return Nn(nn,on,Jr,!1)}),xn.addCommand("halt",function(nn,on,Jr){if(Jr.matchToken("halt")){if(Jr.matchToken("the")){Jr.requireToken("event"),Jr.matchOpToken("'")&&Jr.requireToken("s");var rn=!0}if(Jr.matchToken("bubbling"))var cn=!0;else if(Jr.matchToken("default"))var sn=!0;var fn=Nn(nn,on,Jr,!1),un={keepExecuting:!0,bubbling:cn,haltDefault:sn,exit:fn,op:function(gn){if(gn.event)return cn?gn.event.stopPropagation():(sn||gn.event.stopPropagation(),gn.event.preventDefault()),rn?on.findNext(this,gn):fn}};return un}}),xn.addCommand("log",function(nn,on,Jr){if(Jr.matchToken("log")){for(var rn=[nn.parseElement("expression",Jr)];Jr.matchOpToken(",");)rn.push(nn.requireElement("expression",Jr));if(Jr.matchToken("with"))var cn=nn.requireElement("expression",Jr);var sn={exprs:rn,withExpr:cn,args:[cn,rn],op:function(fn,un,gn){return un?un.apply(null,gn):console.log.apply(null,gn),on.findNext(this,fn)}};return sn}}),xn.addCommand("beep!",function(nn,on,Jr){if(Jr.matchToken("beep!")){for(var rn=[nn.parseElement("expression",Jr)];Jr.matchOpToken(",");)rn.push(nn.requireElement("expression",Jr));var cn={exprs:rn,args:[rn],op:function(sn,fn){for(let un=0;un<rn.length;un++){let gn=rn[un],En=fn[un];on.beepValueToConsole(sn.me,gn,En)}return on.findNext(this,sn)}};return cn}}),xn.addCommand("throw",function(nn,on,Jr){if(Jr.matchToken("throw")){var rn=nn.requireElement("expression",Jr),cn={expr:rn,args:[rn],op:function(sn,fn){throw on.registerHyperTrace(sn,fn),fn}};return cn}});var Dn=function(nn,on,Jr){var rn=nn.requireElement("expression",Jr),cn={expr:rn,args:[rn],op:function(sn,fn){return sn.result=fn,on.findNext(cn,sn)}};return cn};xn.addCommand("call",function(nn,on,Jr){if(Jr.matchToken("call")){var rn=Dn(nn,on,Jr);return rn.expr&&rn.expr.type!=="functionCall"&&nn.raiseParseError(Jr,"Must be a function invocation"),rn}}),xn.addCommand("get",function(nn,on,Jr){if(Jr.matchToken("get"))return Dn(nn,on,Jr)}),xn.addCommand("make",function(nn,on,Jr){if(Jr.matchToken("make")){Jr.matchToken("a")||Jr.matchToken("an");var rn=nn.requireElement("expression",Jr),cn=[];if(rn.type!=="queryRef"&&Jr.matchToken("from"))do cn.push(nn.requireElement("expression",Jr));while(Jr.matchOpToken(","));if(Jr.matchToken("called"))var sn=nn.requireElement("symbol",Jr);var fn;return rn.type==="queryRef"?(fn={op:function(un){for(var gn,En="div",Tn,Rn=[],Bn=/(?:(^|#|\.)([^#\. ]+))/g;gn=Bn.exec(rn.css);)gn[1]===""?En=gn[2].trim():gn[1]==="#"?Tn=gn[2].trim():Rn.push(gn[2].trim());var Cn=document.createElement(En);Tn!==void 0&&(Cn.id=Tn);for(var In=0;In<Rn.length;In++){var _n=Rn[In];Cn.classList.add(_n)}return un.result=Cn,sn&&on.setSymbol(sn.name,un,sn.scope,Cn),on.findNext(this,un)}},fn):(fn={args:[rn,cn],op:function(un,gn,En){return un.result=Ti(gn,En),sn&&on.setSymbol(sn.name,un,sn.scope,un.result),on.findNext(this,un)}},fn)}}),xn.addGrammarElement("pseudoCommand",function(nn,on,Jr){let rn=Jr.token(1);if(!(rn&&rn.op&&(rn.value==="."||rn.value==="(")))return null;for(var cn=nn.requireElement("primaryExpression",Jr),sn=cn.root,fn=cn;sn.root!=null;)fn=fn.root,sn=sn.root;if(cn.type!=="functionCall"&&nn.raiseParseError(Jr,"Pseudo-commands must be function calls"),fn.type==="functionCall"&&fn.root.root==null){if(Jr.matchAnyToken("the","to","on","with","into","from","at"))var un=nn.requireElement("expression",Jr);else if(Jr.matchToken("me"))var un=nn.requireElement("implicitMeTarget",Jr)}var gn;return un?gn={type:"pseudoCommand",root:un,argExressions:fn.argExressions,args:[un,fn.argExressions],op:function(En,Tn,Rn){on.nullCheck(Tn,un);var Bn=Tn[fn.root.name];return on.nullCheck(Bn,fn),Bn.hyperfunc&&Rn.push(En),En.result=Bn.apply(Tn,Rn),on.findNext(gn,En)},execute:function(En){return on.unifiedExec(this,En)}}:gn={type:"pseudoCommand",expr:cn,args:[cn],op:function(En,Tn){return En.result=Tn,on.findNext(gn,En)},execute:function(En){return on.unifiedExec(this,En)}},gn});var Ln=function(nn,on,Jr,rn,cn){var sn=rn.type==="symbol",fn=rn.type==="attributeRef",un=rn.type==="styleRef",gn=rn.type==="arrayIndex";!(fn||un||sn)&&rn.root==null&&nn.raiseParseError(Jr,"Can only put directly into symbols, not references");var En=null,Tn=null;if(!sn)if(fn||un){En=nn.requireElement("implicitMeTarget",Jr);var Rn=rn}else if(gn)Tn=rn.firstIndex,En=rn.root;else{Tn=rn.prop?rn.prop.value:null;var Rn=rn.attribute;En=rn.root}var Bn={target:rn,symbolWrite:sn,value:cn,args:[En,Tn,cn],op:function(Cn,In,_n,Un){return sn?on.setSymbol(rn.name,Cn,rn.scope,Un):(on.nullCheck(In,En),gn?In[_n]=Un:on.implicitLoop(In,function(ni){Rn?Rn.type==="attributeRef"?Un==null?ni.removeAttribute(Rn.name):ni.setAttribute(Rn.name,Un):ni.style[Rn.name]=Un:ni[_n]=Un})),on.findNext(this,Cn)}};return Bn};xn.addCommand("default",function(nn,on,Jr){if(Jr.matchToken("default")){var rn=nn.requireElement("assignableExpression",Jr);Jr.requireToken("to");var cn=nn.requireElement("expression",Jr),sn=Ln(nn,on,Jr,rn,cn),fn={target:rn,value:cn,setter:sn,args:[rn],op:function(un,gn){return gn?on.findNext(this,un):sn}};return sn.parent=fn,fn}}),xn.addCommand("set",function(nn,on,Jr){if(Jr.matchToken("set")){if(Jr.currentToken().type==="L_BRACE"){var rn=nn.requireElement("objectLiteral",Jr);Jr.requireToken("on");var cn=nn.requireElement("expression",Jr),sn={objectLiteral:rn,target:cn,args:[rn,cn],op:function(un,gn,En){return Object.assign(En,gn),on.findNext(this,un)}};return sn}try{Jr.pushFollow("to");var cn=nn.requireElement("assignableExpression",Jr)}finally{Jr.popFollow()}Jr.requireToken("to");var fn=nn.requireElement("expression",Jr);return Ln(nn,on,Jr,cn,fn)}}),xn.addCommand("if",function(nn,on,Jr){if(!Jr.matchToken("if"))return;var rn=nn.requireElement("expression",Jr);Jr.matchToken("then");var cn=nn.parseElement("commandList",Jr),sn=!1;let fn=Jr.matchToken("else")||Jr.matchToken("otherwise");if(fn){let En=Jr.peekToken("if");if(sn=En!=null&&En.line===fn.line,sn)var un=nn.parseElement("command",Jr);else var un=nn.parseElement("commandList",Jr)}Jr.hasMore()&&!sn&&Jr.requireToken("end");var gn={expr:rn,trueBranch:cn,falseBranch:un,args:[rn],op:function(En,Tn){return Tn?cn:un||on.findNext(this,En)}};return nn.setParent(cn,gn),nn.setParent(un,gn),gn});var Fn=function(nn,on,Jr,rn){var cn=on.currentToken(),sn;if(on.matchToken("for")||rn){var fn=on.requireTokenType("IDENTIFIER");sn=fn.value,on.requireToken("in");var un=nn.requireElement("expression",on)}else if(on.matchToken("in")){sn="it";var un=nn.requireElement("expression",on)}else if(on.matchToken("while"))var gn=nn.requireElement("expression",on);else if(on.matchToken("until")){var En=!0;if(on.matchToken("event")){var Tn=nn.requireElement("dotOrColonPath",on,"Expected event name");if(on.matchToken("from"))var Rn=nn.requireElement("expression",on)}else var gn=nn.requireElement("expression",on)}else if(!nn.commandBoundary(on.currentToken())&&on.currentToken().value!=="forever"){var Bn=nn.requireElement("expression",on);on.requireToken("times")}else{on.matchToken("forever");var Cn=!0}if(on.matchToken("index"))var fn=on.requireTokenType("IDENTIFIER"),In=fn.value;else if(on.matchToken("indexed")){on.requireToken("by");var fn=on.requireTokenType("IDENTIFIER"),In=fn.value}var _n=nn.parseElement("commandList",on);if(_n&&Tn){for(var Un=_n;Un.next;)Un=Un.next;var ni={type:"waitATick",op:function(){return new Promise(function(si){setTimeout(function(){si(Jr.findNext(ni))},0)})}};Un.next=ni}if(on.hasMore()&&on.requireToken("end"),sn==null){sn="_implicit_repeat_"+cn.start;var ii=sn}else var ii=sn+"_"+cn.start;var Ri={identifier:sn,indexIdentifier:In,slot:ii,expression:un,forever:Cn,times:Bn,until:En,event:Tn,on:Rn,whileExpr:gn,resolveNext:function(){return this},loop:_n,args:[gn,Bn],op:function(si,ki,Hi){var Ci=si.meta.iterators[ii],Ni=!1,Ca=null;if(this.forever)Ni=!0;else if(this.until)Tn?Ni=si.meta.iterators[ii].eventFired===!1:Ni=ki!==!0;else if(gn)Ni=ki;else if(Hi)Ni=Ci.index<Hi;else{var _a=Ci.iterator.next();Ni=!_a.done,Ca=_a.value}return Ni?(Ci.value?si.result=si.locals[sn]=Ca:si.result=Ci.index,In&&(si.locals[In]=Ci.index),Ci.index++,_n):(si.meta.iterators[ii]=null,Jr.findNext(this.parent,si))}};nn.setParent(_n,Ri);var Bi={name:"repeatInit",args:[un,Tn,Rn],op:function(si,ki,Hi,Ci){var Ni={index:0,value:ki,eventFired:!1};if(si.meta.iterators[ii]=Ni,ki&&ki[Symbol.iterator]&&(Ni.iterator=ki[Symbol.iterator]()),Tn){var Ca=Ci||si.me;Ca.addEventListener(Hi,function(_a){si.meta.iterators[ii].eventFired=!0},{once:!0})}return Ri},execute:function(si){return Jr.unifiedExec(this,si)}};return nn.setParent(Ri,Bi),Bi};xn.addCommand("repeat",function(nn,on,Jr){if(Jr.matchToken("repeat"))return Fn(nn,Jr,on,!1)}),xn.addCommand("for",function(nn,on,Jr){if(Jr.matchToken("for"))return Fn(nn,Jr,on,!0)}),xn.addCommand("continue",function(nn,on,Jr){if(Jr.matchToken("continue")){var rn={op:function(cn){for(var sn=this.parent;;sn=sn.parent)if(sn==null&&nn.raiseParseError(Jr,"Command `continue` cannot be used outside of a `repeat` loop."),sn.loop!=null)return sn.resolveNext(cn)}};return rn}}),xn.addCommand("break",function(nn,on,Jr){if(Jr.matchToken("break")){var rn={op:function(cn){for(var sn=this.parent;;sn=sn.parent)if(sn==null&&nn.raiseParseError(Jr,"Command `continue` cannot be used outside of a `repeat` loop."),sn.loop!=null)return on.findNext(sn.parent,cn)}};return rn}}),xn.addGrammarElement("stringLike",function(nn,on,Jr){return nn.parseAnyOf(["string","nakedString"],Jr)}),xn.addCommand("append",function(nn,on,Jr){if(Jr.matchToken("append")){var rn=null,cn=nn.requireElement("expression",Jr),sn={type:"symbol",evaluate:function(gn){return on.resolveSymbol("result",gn)}};Jr.matchToken("to")?rn=nn.requireElement("expression",Jr):rn=sn;var fn=null;(rn.type==="symbol"||rn.type==="attributeRef"||rn.root!=null)&&(fn=Ln(nn,on,Jr,rn,sn));var un={value:cn,target:rn,args:[rn,cn],op:function(gn,En,Tn){if(Array.isArray(En))return En.push(Tn),on.findNext(this,gn);if(En instanceof Element)return Tn instanceof Element?En.insertAdjacentElement("beforeend",Tn):En.insertAdjacentHTML("beforeend",Tn),on.processNode(En),on.findNext(this,gn);if(fn)return gn.result=(En||"")+Tn,fn;throw Error("Unable to append a value!")},execute:function(gn){return on.unifiedExec(this,gn)}};return fn!=null&&(fn.parent=un),un}});function Mn(nn,on,Jr){Jr.matchToken("at")||Jr.matchToken("from");let rn={includeStart:!0,includeEnd:!1};return rn.from=Jr.matchToken("start")?0:nn.requireElement("expression",Jr),(Jr.matchToken("to")||Jr.matchOpToken(".."))&&(Jr.matchToken("end")?rn.toEnd=!0:rn.to=nn.requireElement("expression",Jr)),Jr.matchToken("inclusive")?rn.includeEnd=!0:Jr.matchToken("exclusive")&&(rn.includeStart=!1),rn}class Hn{constructor(on,Jr){this.re=on,this.str=Jr}next(){let on=this.re.exec(this.str);return on===null?{done:!0}:{value:on}}}class Wn{constructor(on,Jr,rn){this.re=on,this.flags=Jr,this.str=rn}[Symbol.iterator](){return new Hn(new RegExp(this.re,this.flags),this.str)}}xn.addCommand("pick",(nn,on,Jr)=>{if(Jr.matchToken("pick")){if(Jr.matchToken("the"),Jr.matchToken("item")||Jr.matchToken("items")||Jr.matchToken("character")||Jr.matchToken("characters")){let rn=Mn(nn,on,Jr);return Jr.requireToken("from"),{args:[nn.requireElement("expression",Jr),rn.from,rn.to],op(sn,fn,un,gn){return rn.toEnd&&(gn=fn.length),rn.includeStart||un++,rn.includeEnd&&gn++,(gn==null||gn==null)&&(gn=un+1),sn.result=fn.slice(un,gn),on.findNext(this,sn)}}}if(Jr.matchToken("match")){Jr.matchToken("of");let rn=nn.parseElement("expression",Jr),cn="";return Jr.matchOpToken("|")&&(cn=Jr.requireTokenType("IDENTIFIER").value),Jr.requireToken("from"),{args:[nn.parseElement("expression",Jr),rn],op(fn,un,gn){return fn.result=new RegExp(gn,cn).exec(un),on.findNext(this,fn)}}}if(Jr.matchToken("matches")){Jr.matchToken("of");let rn=nn.parseElement("expression",Jr),cn="gu";return Jr.matchOpToken("|")&&(cn="g"+Jr.requireTokenType("IDENTIFIER").value.replace("g","")),Jr.requireToken("from"),{args:[nn.parseElement("expression",Jr),rn],op(fn,un,gn){return fn.result=new Wn(gn,cn,un),on.findNext(this,fn)}}}}}),xn.addCommand("increment",function(nn,on,Jr){if(Jr.matchToken("increment")){var rn,cn=nn.parseElement("assignableExpression",Jr);Jr.matchToken("by")&&(rn=nn.requireElement("expression",Jr));var sn={type:"implicitIncrementOp",target:cn,args:[cn,rn],op:function(fn,un,gn){un=un?parseFloat(un):0,gn=rn?parseFloat(gn):1;var En=un+gn;return fn.result=En,En},evaluate:function(fn){return on.unifiedEval(this,fn)}};return Ln(nn,on,Jr,cn,sn)}}),xn.addCommand("decrement",function(nn,on,Jr){if(Jr.matchToken("decrement")){var rn,cn=nn.parseElement("assignableExpression",Jr);Jr.matchToken("by")&&(rn=nn.requireElement("expression",Jr));var sn={type:"implicitDecrementOp",target:cn,args:[cn,rn],op:function(fn,un,gn){un=un?parseFloat(un):0,gn=rn?parseFloat(gn):1;var En=un-gn;return fn.result=En,En},evaluate:function(fn){return on.unifiedEval(this,fn)}};return Ln(nn,on,Jr,cn,sn)}});function zn(nn,on){var Jr="text",rn;return nn.matchToken("a")||nn.matchToken("an"),nn.matchToken("json")||nn.matchToken("Object")?Jr="json":nn.matchToken("response")?Jr="response":nn.matchToken("html")?Jr="html":nn.matchToken("text")||(rn=on.requireElement("dotOrColonPath",nn).evaluate()),{type:Jr,conversion:rn}}xn.addCommand("fetch",function(nn,on,Jr){if(Jr.matchToken("fetch")){var rn=nn.requireElement("stringLike",Jr);if(Jr.matchToken("as"))var cn=zn(Jr,nn);if(Jr.matchToken("with")&&Jr.currentToken().value!=="{")var sn=nn.parseElement("nakedNamedArgumentList",Jr);else var sn=nn.parseElement("objectLiteral",Jr);cn==null&&Jr.matchToken("as")&&(cn=zn(Jr,nn));var fn=cn?cn.type:"text",un=cn?cn.conversion:null,gn={url:rn,argExpressions:sn,args:[rn,sn],op:function(En,Tn,Rn){var Bn=Rn||{};Bn.sender=En.me,Bn.headers=Bn.headers||{};var Cn=new AbortController;let In=En.me.addEventListener("fetch:abort",function(){Cn.abort()},{once:!0});Bn.signal=Cn.signal,on.triggerEvent(En.me,"hyperscript:beforeFetch",Bn),on.triggerEvent(En.me,"fetch:beforeRequest",Bn),Rn=Bn;var _n=!1;return Rn.timeout&&setTimeout(function(){_n||Cn.abort()},Rn.timeout),fetch(Tn,Rn).then(function(Un){let ni={response:Un};return on.triggerEvent(En.me,"fetch:afterResponse",ni),Un=ni.response,fn==="response"?(En.result=Un,on.triggerEvent(En.me,"fetch:afterRequest",{result:Un}),_n=!0,on.findNext(gn,En)):fn==="json"?Un.json().then(function(ii){return En.result=ii,on.triggerEvent(En.me,"fetch:afterRequest",{result:ii}),_n=!0,on.findNext(gn,En)}):Un.text().then(function(ii){return un&&(ii=on.convertValue(ii,un)),fn==="html"&&(ii=on.convertValue(ii,"Fragment")),En.result=ii,on.triggerEvent(En.me,"fetch:afterRequest",{result:ii}),_n=!0,on.findNext(gn,En)})}).catch(function(Un){throw on.triggerEvent(En.me,"fetch:error",{reason:Un}),Un}).finally(function(){En.me.removeEventListener("fetch:abort",In)})}};return gn}})}function oi(xn){xn.addCommand("settle",function(vn,bn,hn){if(hn.matchToken("settle")){if(vn.commandBoundary(hn.currentToken()))var On=vn.requireElement("implicitMeTarget",hn);else var On=vn.requireElement("expression",hn);var Pn={type:"settleCmd",args:[On],op:function(Nn,Dn){bn.nullCheck(Dn,On);var Ln=null,Fn=!1,Mn=!1,Hn=new Promise(function(Wn){Ln=Wn});return Dn.addEventListener("transitionstart",function(){Mn=!0},{once:!0}),setTimeout(function(){!Mn&&!Fn&&Ln(bn.findNext(Pn,Nn))},500),Dn.addEventListener("transitionend",function(){Fn||Ln(bn.findNext(Pn,Nn))},{once:!0}),Hn},execute:function(Nn){return bn.unifiedExec(this,Nn)}};return Pn}}),xn.addCommand("add",function(vn,bn,hn){if(hn.matchToken("add")){var On=vn.parseElement("classRef",hn),Pn=null,Nn=null;if(On==null)Pn=vn.parseElement("attributeRef",hn),Pn==null&&(Nn=vn.parseElement("styleLiteral",hn),Nn==null&&vn.raiseParseError(hn,"Expected either a class reference or attribute expression"));else for(var Dn=[On];On=vn.parseElement("classRef",hn);)Dn.push(On);if(hn.matchToken("to"))var Ln=vn.requireElement("expression",hn);else var Ln=vn.requireElement("implicitMeTarget",hn);if(hn.matchToken("when")){Nn&&vn.raiseParseError(hn,"Only class and properties are supported with a when clause");var Fn=vn.requireElement("expression",hn)}return Dn?{classRefs:Dn,to:Ln,args:[Ln,Dn],op:function(Mn,Hn,Wn){return bn.nullCheck(Hn,Ln),bn.forEach(Wn,function(zn){bn.implicitLoop(Hn,function(nn){Fn?(Mn.result=nn,bn.evaluateNoPromise(Fn,Mn)?nn instanceof Element&&nn.classList.add(zn.className):nn instanceof Element&&nn.classList.remove(zn.className),Mn.result=null):nn instanceof Element&&nn.classList.add(zn.className)})}),bn.findNext(this,Mn)}}:Pn?{type:"addCmd",attributeRef:Pn,to:Ln,args:[Ln],op:function(Mn,Hn,Wn){return bn.nullCheck(Hn,Ln),bn.implicitLoop(Hn,function(zn){Fn?(Mn.result=zn,bn.evaluateNoPromise(Fn,Mn)?zn.setAttribute(Pn.name,Pn.value):zn.removeAttribute(Pn.name),Mn.result=null):zn.setAttribute(Pn.name,Pn.value)}),bn.findNext(this,Mn)},execute:function(Mn){return bn.unifiedExec(this,Mn)}}:{type:"addCmd",cssDeclaration:Nn,to:Ln,args:[Ln,Nn],op:function(Mn,Hn,Wn){return bn.nullCheck(Hn,Ln),bn.implicitLoop(Hn,function(zn){zn.style.cssText+=Wn}),bn.findNext(this,Mn)},execute:function(Mn){return bn.unifiedExec(this,Mn)}}}}),xn.addGrammarElement("styleLiteral",function(vn,bn,hn){if(hn.matchOpToken("{")){for(var On=[""],Pn=[];hn.hasMore();){if(hn.matchOpToken("\\"))hn.consumeToken();else{if(hn.matchOpToken("}"))break;if(hn.matchToken("$")){var Nn=hn.matchOpToken("{"),Dn=vn.parseElement("expression",hn);Nn&&hn.requireOpToken("}"),Pn.push(Dn),On.push("")}else{var Ln=hn.consumeToken();On[On.length-1]+=hn.source.substring(Ln.start,Ln.end)}}On[On.length-1]+=hn.lastWhitespace()}return{type:"styleLiteral",args:[Pn],op:function(Fn,Mn){var Hn="";return On.forEach(function(Wn,zn){Hn+=Wn,zn in Mn&&(Hn+=Mn[zn])}),Hn},evaluate:function(Fn){return bn.unifiedEval(this,Fn)}}}}),xn.addCommand("remove",function(vn,bn,hn){if(hn.matchToken("remove")){var On=vn.parseElement("classRef",hn),Pn=null,Nn=null;if(On==null)Pn=vn.parseElement("attributeRef",hn),Pn==null&&(Nn=vn.parseElement("expression",hn),Nn==null&&vn.raiseParseError(hn,"Expected either a class reference, attribute expression or value expression"));else for(var Dn=[On];On=vn.parseElement("classRef",hn);)Dn.push(On);if(hn.matchToken("from"))var Ln=vn.requireElement("expression",hn);else if(Nn==null)var Ln=vn.requireElement("implicitMeTarget",hn);return Nn?{elementExpr:Nn,from:Ln,args:[Nn,Ln],op:function(Fn,Mn,Hn){return bn.nullCheck(Mn,Nn),bn.implicitLoop(Mn,function(Wn){Wn.parentElement&&(Hn==null||Hn.contains(Wn))&&Wn.parentElement.removeChild(Wn)}),bn.findNext(this,Fn)}}:{classRefs:Dn,attributeRef:Pn,elementExpr:Nn,from:Ln,args:[Dn,Ln],op:function(Fn,Mn,Hn){return bn.nullCheck(Hn,Ln),Mn?bn.forEach(Mn,function(Wn){bn.implicitLoop(Hn,function(zn){zn.classList.remove(Wn.className)})}):bn.implicitLoop(Hn,function(Wn){Wn.removeAttribute(Pn.name)}),bn.findNext(this,Fn)}}}}),xn.addCommand("toggle",function(vn,bn,hn){if(hn.matchToken("toggle")){if(hn.matchAnyToken("the","my"),hn.currentToken().type==="STYLE_REF"){var On=hn.consumeToken().value.substr(1),Pn=!0,Nn=pn(vn,hn,On);if(hn.matchToken("of")){hn.pushFollow("with");try{var Dn=vn.requireElement("expression",hn)}finally{hn.popFollow()}}else var Dn=vn.requireElement("implicitMeTarget",hn)}else if(hn.matchToken("between")){var Ln=!0,Fn=vn.parseElement("classRef",hn);hn.requireToken("and");var Mn=vn.requireElement("classRef",hn)}else{var Fn=vn.parseElement("classRef",hn),Hn=null;if(Fn==null)Hn=vn.parseElement("attributeRef",hn),Hn==null&&vn.raiseParseError(hn,"Expected either a class reference or attribute expression");else for(var Wn=[Fn];Fn=vn.parseElement("classRef",hn);)Wn.push(Fn)}if(Pn!==!0)if(hn.matchToken("on"))var Dn=vn.requireElement("expression",hn);else var Dn=vn.requireElement("implicitMeTarget",hn);if(hn.matchToken("for"))var zn=vn.requireElement("expression",hn);else if(hn.matchToken("until")){var nn=vn.requireElement("dotOrColonPath",hn,"Expected event name");if(hn.matchToken("from"))var on=vn.requireElement("expression",hn)}var Jr={classRef:Fn,classRef2:Mn,classRefs:Wn,attributeRef:Hn,on:Dn,time:zn,evt:nn,from:on,toggle:function(rn,cn,sn,fn){bn.nullCheck(rn,Dn),Pn?bn.implicitLoop(rn,function(un){Nn("toggle",un)}):Ln?bn.implicitLoop(rn,function(un){un.classList.contains(cn.className)?(un.classList.remove(cn.className),un.classList.add(sn.className)):(un.classList.add(cn.className),un.classList.remove(sn.className))}):fn?bn.forEach(fn,function(un){bn.implicitLoop(rn,function(gn){gn.classList.toggle(un.className)})}):bn.implicitLoop(rn,function(un){un.hasAttribute(Hn.name)?un.removeAttribute(Hn.name):un.setAttribute(Hn.name,Hn.value)})},args:[Dn,zn,nn,on,Fn,Mn,Wn],op:function(rn,cn,sn,fn,un,gn,En,Tn){return sn?new Promise(function(Rn){Jr.toggle(cn,gn,En,Tn),setTimeout(function(){Jr.toggle(cn,gn,En,Tn),Rn(bn.findNext(Jr,rn))},sn)}):fn?new Promise(function(Rn){var Bn=un||rn.me;Bn.addEventListener(fn,function(){Jr.toggle(cn,gn,En,Tn),Rn(bn.findNext(Jr,rn))},{once:!0}),Jr.toggle(cn,gn,En,Tn)}):(this.toggle(cn,gn,En,Tn),bn.findNext(Jr,rn))}};return Jr}});var en={display:function(vn,bn,hn){if(hn)bn.style.display=hn;else if(vn==="toggle")getComputedStyle(bn).display==="none"?en.display("show",bn,hn):en.display("hide",bn,hn);else if(vn==="hide"){let On=xn.runtime.getInternalData(bn);On.originalDisplay==null&&(On.originalDisplay=bn.style.display),bn.style.display="none"}else{let On=xn.runtime.getInternalData(bn);On.originalDisplay&&On.originalDisplay!=="none"?bn.style.display=On.originalDisplay:bn.style.removeProperty("display")}},visibility:function(vn,bn,hn){hn?bn.style.visibility=hn:vn==="toggle"?getComputedStyle(bn).visibility==="hidden"?en.visibility("show",bn,hn):en.visibility("hide",bn,hn):vn==="hide"?bn.style.visibility="hidden":bn.style.visibility="visible"},opacity:function(vn,bn,hn){hn?bn.style.opacity=hn:vn==="toggle"?getComputedStyle(bn).opacity==="0"?en.opacity("show",bn,hn):en.opacity("hide",bn,hn):vn==="hide"?bn.style.opacity="0":bn.style.opacity="1"}},an=function(vn,bn,hn){var On,Pn=hn.currentToken();return Pn.value==="when"||Pn.value==="with"||vn.commandBoundary(Pn)?On=vn.parseElement("implicitMeTarget",hn):On=vn.parseElement("expression",hn),On},pn=function(vn,bn,hn){var On=Wr.defaultHideShowStrategy,Pn=en;Wr.hideShowStrategies&&(Pn=Object.assign(Pn,Wr.hideShowStrategies)),hn=hn||On||"display";var Nn=Pn[hn];return Nn==null&&vn.raiseParseError(bn,"Unknown show/hide strategy : "+hn),Nn};xn.addCommand("hide",function(vn,bn,hn){if(hn.matchToken("hide")){var On=an(vn,bn,hn),Pn=null;hn.matchToken("with")&&(Pn=hn.requireTokenType("IDENTIFIER","STYLE_REF").value,Pn.indexOf("*")===0&&(Pn=Pn.substr(1)));var Nn=pn(vn,hn,Pn);return{target:On,args:[On],op:function(Dn,Ln){return bn.nullCheck(Ln,On),bn.implicitLoop(Ln,function(Fn){Nn("hide",Fn)}),bn.findNext(this,Dn)}}}}),xn.addCommand("show",function(vn,bn,hn){if(hn.matchToken("show")){var On=an(vn,bn,hn),Pn=null;hn.matchToken("with")&&(Pn=hn.requireTokenType("IDENTIFIER","STYLE_REF").value,Pn.indexOf("*")===0&&(Pn=Pn.substr(1)));var Nn=null;if(hn.matchOpToken(":")){var Dn=hn.consumeUntilWhitespace();hn.matchTokenType("WHITESPACE"),Nn=Dn.map(function(Mn){return Mn.value}).join("")}if(hn.matchToken("when"))var Ln=vn.requireElement("expression",hn);var Fn=pn(vn,hn,Pn);return{target:On,when:Ln,args:[On],op:function(Mn,Hn){return bn.nullCheck(Hn,On),bn.implicitLoop(Hn,function(Wn){Ln?(Mn.result=Wn,bn.evaluateNoPromise(Ln,Mn)?Fn("show",Wn,Nn):Fn("hide",Wn),Mn.result=null):Fn("show",Wn,Nn)}),bn.findNext(this,Mn)}}}}),xn.addCommand("take",function(vn,bn,hn){if(hn.matchToken("take")){let Fn=null,Mn=[];for(;Fn=vn.parseElement("classRef",hn);)Mn.push(Fn);var On=null,Pn=null;let Hn=Mn.length>0;if(Hn||(On=vn.parseElement("attributeRef",hn),On==null&&vn.raiseParseError(hn,"Expected either a class reference or attribute expression"),hn.matchToken("with")&&(Pn=vn.requireElement("expression",hn))),hn.matchToken("from"))var Nn=vn.requireElement("expression",hn);if(hn.matchToken("for"))var Dn=vn.requireElement("expression",hn);else var Dn=vn.requireElement("implicitMeTarget",hn);if(Hn){var Ln={classRefs:Mn,from:Nn,forElt:Dn,args:[Mn,Nn,Dn],op:function(Wn,zn,nn,on){return bn.nullCheck(on,Dn),bn.implicitLoop(zn,function(Jr){var rn=Jr.className;nn?bn.implicitLoop(nn,function(cn){cn.classList.remove(rn)}):bn.implicitLoop(Jr,function(cn){cn.classList.remove(rn)}),bn.implicitLoop(on,function(cn){cn.classList.add(rn)})}),bn.findNext(this,Wn)}};return Ln}else{var Ln={attributeRef:On,from:Nn,forElt:Dn,args:[Nn,Dn,Pn],op:function(zn,nn,on,Jr){return bn.nullCheck(nn,Nn),bn.nullCheck(on,Dn),bn.implicitLoop(nn,function(rn){Jr?rn.setAttribute(On.name,Jr):rn.removeAttribute(On.name)}),bn.implicitLoop(on,function(rn){rn.setAttribute(On.name,On.value||"")}),bn.findNext(this,zn)}};return Ln}}});function mn(vn,bn,hn,On){if(hn!=null)var Pn=vn.resolveSymbol(hn,bn);else var Pn=bn;if(Pn instanceof Element||Pn instanceof HTMLDocument){for(;Pn.firstChild;)Pn.removeChild(Pn.firstChild);Pn.append(xn.runtime.convertValue(On,"Fragment")),vn.processNode(Pn)}else if(hn!=null)vn.setSymbol(hn,bn,null,On);else throw"Don't know how to put a value into "+typeof bn}xn.addCommand("put",function(vn,bn,hn){if(hn.matchToken("put")){var On=vn.requireElement("expression",hn),Pn=hn.matchAnyToken("into","before","after");Pn==null&&hn.matchToken("at")&&(hn.matchToken("the"),Pn=hn.matchAnyToken("start","end"),hn.requireToken("of")),Pn==null&&vn.raiseParseError(hn,"Expected one of 'into', 'before', 'at start of', 'at end of', 'after'");var Nn=vn.requireElement("expression",hn),Dn=Pn.value,Ln=!1,Fn=!1,Mn=null,Hn=null;if(Nn.type==="arrayIndex"&&Dn==="into")Ln=!0,Hn=Nn.prop,Mn=Nn.root;else if(Nn.prop&&Nn.root&&Dn==="into")Hn=Nn.prop.value,Mn=Nn.root;else if(Nn.type==="symbol"&&Dn==="into")Fn=!0,Hn=Nn.name;else if(Nn.type==="attributeRef"&&Dn==="into"){var Wn=!0;Hn=Nn.name,Mn=vn.requireElement("implicitMeTarget",hn)}else if(Nn.type==="styleRef"&&Dn==="into"){var zn=!0;Hn=Nn.name,Mn=vn.requireElement("implicitMeTarget",hn)}else if(Nn.attribute&&Dn==="into"){var Wn=Nn.attribute.type==="attributeRef",zn=Nn.attribute.type==="styleRef";Hn=Nn.attribute.name,Mn=Nn.root}else Mn=Nn;var nn={target:Nn,operation:Dn,symbolWrite:Fn,value:On,args:[Mn,Hn,On],op:function(on,Jr,rn,cn){if(Fn)mn(bn,on,rn,cn);else if(bn.nullCheck(Jr,Mn),Dn==="into")Wn?bn.implicitLoop(Jr,function(fn){fn.setAttribute(rn,cn)}):zn?bn.implicitLoop(Jr,function(fn){fn.style[rn]=cn}):Ln?Jr[rn]=cn:bn.implicitLoop(Jr,function(fn){mn(bn,fn,rn,cn)});else{var sn=Dn==="before"?Element.prototype.before:Dn==="after"?Element.prototype.after:Dn==="start"?Element.prototype.prepend:Element.prototype.append;bn.implicitLoop(Jr,function(fn){sn.call(fn,cn instanceof Node?cn:bn.convertValue(cn,"Fragment")),fn.parentElement?bn.processNode(fn.parentElement):bn.processNode(fn)})}return bn.findNext(this,on)}};return nn}});function Sn(vn,bn,hn){var On;if(hn.matchToken("the")||hn.matchToken("element")||hn.matchToken("elements")||hn.currentToken().type==="CLASS_REF"||hn.currentToken().type==="ID_REF"||hn.currentToken().op&&hn.currentToken().value==="<"){vn.possessivesDisabled=!0;try{On=vn.parseElement("expression",hn)}finally{delete vn.possessivesDisabled}hn.matchOpToken("'")&&hn.requireToken("s")}else if(hn.currentToken().type==="IDENTIFIER"&&hn.currentToken().value==="its"){var Pn=hn.matchToken("its");On={type:"pseudopossessiveIts",token:Pn,name:Pn.value,evaluate:function(Nn){return bn.resolveSymbol("it",Nn)}}}else hn.matchToken("my")||hn.matchToken("me"),On=vn.parseElement("implicitMeTarget",hn);return On}xn.addCommand("transition",function(vn,bn,hn){if(hn.matchToken("transition")){for(var On=Sn(vn,bn,hn),Pn=[],Nn=[],Dn=[],Ln=hn.currentToken();!vn.commandBoundary(Ln)&&Ln.value!=="over"&&Ln.value!=="using";){if(hn.currentToken().type==="STYLE_REF"){let zn=hn.consumeToken().value.substr(1);Pn.push({type:"styleRefValue",evaluate:function(){return zn}})}else Pn.push(vn.requireElement("stringLike",hn));hn.matchToken("from")?Nn.push(vn.requireElement("expression",hn)):Nn.push(null),hn.requireToken("to"),hn.matchToken("initial")?Dn.push({type:"initial_literal",evaluate:function(){return"initial"}}):Dn.push(vn.requireElement("expression",hn)),Ln=hn.currentToken()}if(hn.matchToken("over"))var Fn=vn.requireElement("expression",hn);else if(hn.matchToken("using"))var Mn=vn.requireElement("expression",hn);var Hn={to:Dn,args:[On,Pn,Nn,Dn,Mn,Fn],op:function(Wn,zn,nn,on,Jr,rn,cn){bn.nullCheck(zn,On);var sn=[];return bn.implicitLoop(zn,function(fn){var un=new Promise(function(gn,En){var Tn=fn.style.transition;cn?fn.style.transition="all "+cn+"ms ease-in":rn?fn.style.transition=rn:fn.style.transition=Wr.defaultTransition;for(var Rn=bn.getInternalData(fn),Bn=getComputedStyle(fn),Cn={},In=0;In<Bn.length;In++){var _n=Bn[In],Un=Bn[_n];Cn[_n]=Un}Rn.initialStyles||(Rn.initialStyles=Cn);for(var In=0;In<nn.length;In++){var ni=nn[In],ii=on[In];ii==="computed"||ii==null?fn.style[ni]=Cn[ni]:fn.style[ni]=ii}var Ri=!1,Bi=!1;fn.addEventListener("transitionend",function(){Bi||(fn.style.transition=Tn,Bi=!0,gn())},{once:!0}),fn.addEventListener("transitionstart",function(){Ri=!0},{once:!0}),setTimeout(function(){!Bi&&!Ri&&(fn.style.transition=Tn,Bi=!0,gn())},100),setTimeout(function(){for(var si=[],ki=0;ki<nn.length;ki++){var Hi=nn[ki],Ci=Jr[ki];if(Ci==="initial"){var Ni=Rn.initialStyles[Hi];fn.style[Hi]=Ni}else fn.style[Hi]=Ci}},0)});sn.push(un)}),Promise.all(sn).then(function(){return bn.findNext(Hn,Wn)})}};return Hn}}),xn.addCommand("measure",function(vn,bn,hn){if(hn.matchToken("measure")){var On=Sn(vn,bn,hn),Pn=[];if(!vn.commandBoundary(hn.currentToken()))do Pn.push(hn.matchTokenType("IDENTIFIER").value);while(hn.matchOpToken(","));return{properties:Pn,args:[On],op:function(Nn,Dn){bn.nullCheck(Dn,On),0 in Dn&&(Dn=Dn[0]);var Ln=Dn.getBoundingClientRect(),Fn={top:Dn.scrollTop,left:Dn.scrollLeft,topMax:Dn.scrollTopMax,leftMax:Dn.scrollLeftMax,height:Dn.scrollHeight,width:Dn.scrollWidth};return Nn.result={x:Ln.x,y:Ln.y,left:Ln.left,top:Ln.top,right:Ln.right,bottom:Ln.bottom,width:Ln.width,height:Ln.height,bounds:Ln,scrollLeft:Fn.left,scrollTop:Fn.top,scrollLeftMax:Fn.leftMax,scrollTopMax:Fn.topMax,scrollWidth:Fn.width,scrollHeight:Fn.height,scroll:Fn},bn.forEach(Pn,function(Mn){if(Mn in Nn.result)Nn.locals[Mn]=Nn.result[Mn];else throw"No such measurement as "+Mn}),bn.findNext(this,Nn)}}}}),xn.addLeafExpression("closestExpr",function(vn,bn,hn){if(hn.matchToken("closest")){if(hn.matchToken("parent"))var On=!0;var Pn=null;if(hn.currentToken().type==="ATTRIBUTE_REF"){var Nn=vn.requireElement("attributeRefAccess",hn,null);Pn="["+Nn.attribute.name+"]"}if(Pn==null){var Dn=vn.requireElement("expression",hn);Dn.css==null?vn.raiseParseError(hn,"Expected a CSS expression"):Pn=Dn.css}if(hn.matchToken("to"))var Ln=vn.parseElement("expression",hn);else var Ln=vn.parseElement("implicitMeTarget",hn);var Fn={type:"closestExpr",parentSearch:On,expr:Dn,css:Pn,to:Ln,args:[Ln],op:function(Mn,Hn){if(Hn==null)return null;{let Wn=[];return bn.implicitLoop(Hn,function(zn){On?Wn.push(zn.parentElement?zn.parentElement.closest(Pn):null):Wn.push(zn.closest(Pn))}),bn.shouldAutoIterate(Hn)?Wn:Wn[0]}},evaluate:function(Mn){return bn.unifiedEval(this,Mn)}};return Nn?(Nn.root=Fn,Nn.args=[Fn],Nn):Fn}}),xn.addCommand("go",function(vn,bn,hn){if(hn.matchToken("go")){if(hn.matchToken("back"))var On=!0;else if(hn.matchToken("to"),hn.matchToken("url")){var Pn=vn.requireElement("stringLike",hn),Nn=!0;if(hn.matchToken("in")){hn.requireToken("new"),hn.requireToken("window");var Dn=!0}}else{hn.matchToken("the");var Ln=hn.matchAnyToken("top","middle","bottom"),Fn=hn.matchAnyToken("left","center","right");(Ln||Fn)&&hn.requireToken("of");var Pn=vn.requireElement("unaryExpression",hn),Mn=hn.matchAnyOpToken("+","-");if(Mn){hn.pushFollow("px");try{var Hn=vn.requireElement("expression",hn)}finally{hn.popFollow()}}hn.matchToken("px");var Wn=hn.matchAnyToken("smoothly","instantly"),zn={block:"start",inline:"nearest"};Ln&&(Ln.value==="top"?zn.block="start":Ln.value==="bottom"?zn.block="end":Ln.value==="middle"&&(zn.block="center")),Fn&&(Fn.value==="left"?zn.inline="start":Fn.value==="center"?zn.inline="center":Fn.value==="right"&&(zn.inline="end")),Wn&&(Wn.value==="smoothly"?zn.behavior="smooth":Wn.value==="instantly"&&(zn.behavior="instant"))}var nn={target:Pn,args:[Pn,Hn],op:function(on,Jr,rn){return On?window.history.back():Nn?Jr&&(Dn?window.open(Jr):window.location.href=Jr):bn.implicitLoop(Jr,function(cn){if(cn===window&&(cn=document.body),Mn){let sn=cn.getBoundingClientRect(),fn=document.createElement("div"),un=Mn.value==="+"?rn:rn*-1,gn=zn.inline=="start"||zn.inline=="end"?un:0,En=zn.block=="start"||zn.block=="end"?un:0;fn.style.position="absolute",fn.style.top=sn.top+window.scrollY+En+"px",fn.style.left=sn.left+window.scrollX+gn+"px",fn.style.height=sn.height+"px",fn.style.width=sn.width+"px",fn.style.zIndex=""+Number.MIN_SAFE_INTEGER,fn.style.opacity="0",document.body.appendChild(fn),setTimeout(function(){document.body.removeChild(fn)},100),cn=fn}cn.scrollIntoView(zn)}),bn.findNext(nn,on)}};return nn}}),Wr.conversions.dynamicResolvers.push(function(vn,bn){if(!(vn==="Values"||vn.indexOf("Values:")===0))return;var hn=vn.split(":")[1],On={},Pn=xn.runtime.implicitLoop.bind(xn.runtime);if(Pn(bn,function(Ln){var Fn=Dn(Ln);if(Fn!==void 0){On[Fn.name]=Fn.value;return}if(Ln.querySelectorAll!=null){var Mn=Ln.querySelectorAll("input,select,textarea");Mn.forEach(Nn)}}),hn){if(hn==="JSON")return JSON.stringify(On);if(hn==="Form")return new URLSearchParams(On).toString();throw"Unknown conversion: "+hn}else return On;function Nn(Ln){var Fn=Dn(Ln);if(Fn!=null){if(On[Fn.name]==null){On[Fn.name]=Fn.value;return}if(Array.isArray(On[Fn.name])&&Array.isArray(Fn.value)){On[Fn.name]=[].concat(On[Fn.name],Fn.value);return}}}function Dn(Ln){try{var Fn={name:Ln.name,value:Ln.value};if(Fn.name==null||Fn.value==null||Ln.type=="radio"&&Ln.checked==!1)return;if(Ln.type=="checkbox"&&(Ln.checked==!1?Fn.value=void 0:typeof Fn.value=="string"&&(Fn.value=[Fn.value])),Ln.type=="select-multiple"){var Mn=Ln.querySelectorAll("option[selected]");Fn.value=[];for(var Hn=0;Hn<Mn.length;Hn++)Fn.value.push(Mn[Hn].value)}return Fn}catch{return}}}),Wr.conversions.HTML=function(vn){var bn=function(hn){if(hn instanceof Array)return hn.map(function(Dn){return bn(Dn)}).join("");if(hn instanceof HTMLElement)return hn.outerHTML;if(hn instanceof NodeList){for(var On="",Pn=0;Pn<hn.length;Pn++){var Nn=hn[Pn];Nn instanceof HTMLElement&&(On+=Nn.outerHTML)}return On}return hn.toString?hn.toString():""};return bn(vn)},Wr.conversions.Fragment=function(vn){var bn=document.createDocumentFragment();return xn.runtime.implicitLoop(vn,function(hn){if(hn instanceof Node)bn.append(hn);else{var On=document.createElement("template");On.innerHTML=hn,bn.append(On.content)}}),bn}}let Jn=new tn,mi=Jn.lexer,ai=Jn.parser;function Ui(xn,en){return Jn.evaluate(xn,en)}function gi(){var xn=Array.from(Qr.document.querySelectorAll("script[type='text/hyperscript'][src]"));Promise.all(xn.map(function(mn){return fetch(mn.src).then(function(Sn){return Sn.text()})})).then(mn=>mn.forEach(Sn=>ri(Sn))).then(()=>en(function(){pn(),Jn.processNode(document.documentElement),document.dispatchEvent(new Event("hyperscript:ready")),Qr.document.addEventListener("htmx:load",function(mn){Jn.processNode(mn.detail.elt)})}));function en(mn){document.readyState!=="loading"?setTimeout(mn):document.addEventListener("DOMContentLoaded",mn)}function an(){var mn=document.querySelector('meta[name="htmx-config"]');return mn?Vn(mn.content):null}function pn(){var mn=an();mn&&Object.assign(Wr,mn)}}let ri=Object.assign(Ui,{config:Wr,use(xn){xn(ri)},internals:{lexer:mi,parser:ai,runtime:Jn,Lexer:Kr,Tokens:Yr,Parser:Zr,Runtime:tn},ElementCollection:An,addFeature:ai.addFeature.bind(ai),addCommand:ai.addCommand.bind(ai),addLeafExpression:ai.addLeafExpression.bind(ai),addIndirectExpression:ai.addIndirectExpression.bind(ai),evaluate:Jn.evaluate.bind(Jn),parse:Jn.parse.bind(Jn),processNode:Jn.processNode.bind(Jn),version:"0.9.14",browserInit:gi});return ri})});var zs=po(mo()),Vs=po(Ys());function $s(Qr,Gr){var Wr=Object.keys(Qr);if(Object.getOwnPropertySymbols){var Kr=Object.getOwnPropertySymbols(Qr);Gr&&(Kr=Kr.filter(function(Yr){return Object.getOwnPropertyDescriptor(Qr,Yr).enumerable})),Wr.push.apply(Wr,Kr)}return Wr}function Qi(Qr){for(var Gr=1;Gr<arguments.length;Gr++){var Wr=arguments[Gr]!=null?arguments[Gr]:{};Gr%2?$s(Object(Wr),!0).forEach(function(Kr){iu(Qr,Kr,Wr[Kr])}):Object.getOwnPropertyDescriptors?Object.defineProperties(Qr,Object.getOwnPropertyDescriptors(Wr)):$s(Object(Wr)).forEach(function(Kr){Object.defineProperty(Qr,Kr,Object.getOwnPropertyDescriptor(Wr,Kr))})}return Qr}function To(Qr){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?To=function(Gr){return typeof Gr}:To=function(Gr){return Gr&&typeof Symbol=="function"&&Gr.constructor===Symbol&&Gr!==Symbol.prototype?"symbol":typeof Gr},To(Qr)}function iu(Qr,Gr,Wr){return Gr in Qr?Object.defineProperty(Qr,Gr,{value:Wr,enumerable:!0,configurable:!0,writable:!0}):Qr[Gr]=Wr,Qr}function ta(){return ta=Object.assign||function(Qr){for(var Gr=1;Gr<arguments.length;Gr++){var Wr=arguments[Gr];for(var Kr in Wr)Object.prototype.hasOwnProperty.call(Wr,Kr)&&(Qr[Kr]=Wr[Kr])}return Qr},ta.apply(this,arguments)}function au(Qr,Gr){if(Qr==null)return{};var Wr={},Kr=Object.keys(Qr),Yr,Zr;for(Zr=0;Zr<Kr.length;Zr++)Yr=Kr[Zr],!(Gr.indexOf(Yr)>=0)&&(Wr[Yr]=Qr[Yr]);return Wr}function ou(Qr,Gr){if(Qr==null)return{};var Wr=au(Qr,Gr),Kr,Yr;if(Object.getOwnPropertySymbols){var Zr=Object.getOwnPropertySymbols(Qr);for(Yr=0;Yr<Zr.length;Yr++)Kr=Zr[Yr],!(Gr.indexOf(Kr)>=0)&&Object.prototype.propertyIsEnumerable.call(Qr,Kr)&&(Wr[Kr]=Qr[Kr])}return Wr}var su="1.15.6";function ea(Qr){if(typeof window<"u"&&window.navigator)return!!navigator.userAgent.match(Qr)}var ra=ea(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),Ja=ea(/Edge/i),Js=ea(/firefox/i),Xa=ea(/safari/i)&&!ea(/chrome/i)&&!ea(/android/i),vs=ea(/iP(ad|od|hone)/i),ol=ea(/chrome/i)&&ea(/android/i),sl={capture:!1,passive:!1};function ei(Qr,Gr,Wr){Qr.addEventListener(Gr,Wr,!ra&&sl)}function Zn(Qr,Gr,Wr){Qr.removeEventListener(Gr,Wr,!ra&&sl)}function Co(Qr,Gr){if(Gr){if(Gr[0]===">"&&(Gr=Gr.substring(1)),Qr)try{if(Qr.matches)return Qr.matches(Gr);if(Qr.msMatchesSelector)return Qr.msMatchesSelector(Gr);if(Qr.webkitMatchesSelector)return Qr.webkitMatchesSelector(Gr)}catch{return!1}return!1}}function ll(Qr){return Qr.host&&Qr!==document&&Qr.host.nodeType?Qr.host:Qr.parentNode}function Vi(Qr,Gr,Wr,Kr){if(Qr){Wr=Wr||document;do{if(Gr!=null&&(Gr[0]===">"?Qr.parentNode===Wr&&Co(Qr,Gr):Co(Qr,Gr))||Kr&&Qr===Wr)return Qr;if(Qr===Wr)break}while(Qr=ll(Qr))}return null}var Zs=/\s+/g;function Mi(Qr,Gr,Wr){if(Qr&&Gr)if(Qr.classList)Qr.classList[Wr?"add":"remove"](Gr);else{var Kr=(" "+Qr.className+" ").replace(Zs," ").replace(" "+Gr+" "," ");Qr.className=(Kr+(Wr?" "+Gr:"")).replace(Zs," ")}}function Xn(Qr,Gr,Wr){var Kr=Qr&&Qr.style;if(Kr){if(Wr===void 0)return document.defaultView&&document.defaultView.getComputedStyle?Wr=document.defaultView.getComputedStyle(Qr,""):Qr.currentStyle&&(Wr=Qr.currentStyle),Gr===void 0?Wr:Wr[Gr];!(Gr in Kr)&&Gr.indexOf("webkit")===-1&&(Gr="-webkit-"+Gr),Kr[Gr]=Wr+(typeof Wr=="string"?"":"px")}}function Na(Qr,Gr){var Wr="";if(typeof Qr=="string")Wr=Qr;else do{var Kr=Xn(Qr,"transform");Kr&&Kr!=="none"&&(Wr=Kr+" "+Wr)}while(!Gr&&(Qr=Qr.parentNode));var Yr=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return Yr&&new Yr(Wr)}function ul(Qr,Gr,Wr){if(Qr){var Kr=Qr.getElementsByTagName(Gr),Yr=0,Zr=Kr.length;if(Wr)for(;Yr<Zr;Yr++)Wr(Kr[Yr],Yr);return Kr}return[]}function Xi(){var Qr=document.scrollingElement;return Qr||document.documentElement}function yi(Qr,Gr,Wr,Kr,Yr){if(!(!Qr.getBoundingClientRect&&Qr!==window)){var Zr,tn,ln,dn,yn,wn,kn;if(Qr!==window&&Qr.parentNode&&Qr!==Xi()?(Zr=Qr.getBoundingClientRect(),tn=Zr.top,ln=Zr.left,dn=Zr.bottom,yn=Zr.right,wn=Zr.height,kn=Zr.width):(tn=0,ln=0,dn=window.innerHeight,yn=window.innerWidth,wn=window.innerHeight,kn=window.innerWidth),(Gr||Wr)&&Qr!==window&&(Yr=Yr||Qr.parentNode,!ra))do if(Yr&&Yr.getBoundingClientRect&&(Xn(Yr,"transform")!=="none"||Wr&&Xn(Yr,"position")!=="static")){var An=Yr.getBoundingClientRect();tn-=An.top+parseInt(Xn(Yr,"border-top-width")),ln-=An.left+parseInt(Xn(Yr,"border-left-width")),dn=tn+Zr.height,yn=ln+Zr.width;break}while(Yr=Yr.parentNode);if(Kr&&Qr!==window){var Gn=Na(Yr||Qr),jn=Gn&&Gn.a,Vn=Gn&&Gn.d;Gn&&(tn/=Vn,ln/=jn,kn/=jn,wn/=Vn,dn=tn+wn,yn=ln+kn)}return{top:tn,left:ln,bottom:dn,right:yn,width:kn,height:wn}}}function el(Qr,Gr,Wr){for(var Kr=la(Qr,!0),Yr=yi(Qr)[Gr];Kr;){var Zr=yi(Kr)[Wr],tn=void 0;if(Wr==="top"||Wr==="left"?tn=Yr>=Zr:tn=Yr<=Zr,!tn)return Kr;if(Kr===Xi())break;Kr=la(Kr,!1)}return!1}function La(Qr,Gr,Wr,Kr){for(var Yr=0,Zr=0,tn=Qr.children;Zr<tn.length;){if(tn[Zr].style.display!=="none"&&tn[Zr]!==Qn.ghost&&(Kr||tn[Zr]!==Qn.dragged)&&Vi(tn[Zr],Wr.draggable,Qr,!1)){if(Yr===Gr)return tn[Zr];Yr++}Zr++}return null}function ys(Qr,Gr){for(var Wr=Qr.lastElementChild;Wr&&(Wr===Qn.ghost||Xn(Wr,"display")==="none"||Gr&&!Co(Wr,Gr));)Wr=Wr.previousElementSibling;return Wr||null}function Gi(Qr,Gr){var Wr=0;if(!Qr||!Qr.parentNode)return-1;for(;Qr=Qr.previousElementSibling;)Qr.nodeName.toUpperCase()!=="TEMPLATE"&&Qr!==Qn.clone&&(!Gr||Co(Qr,Gr))&&Wr++;return Wr}function tl(Qr){var Gr=0,Wr=0,Kr=Xi();if(Qr)do{var Yr=Na(Qr),Zr=Yr.a,tn=Yr.d;Gr+=Qr.scrollLeft*Zr,Wr+=Qr.scrollTop*tn}while(Qr!==Kr&&(Qr=Qr.parentNode));return[Gr,Wr]}function lu(Qr,Gr){for(var Wr in Qr)if(Qr.hasOwnProperty(Wr)){for(var Kr in Gr)if(Gr.hasOwnProperty(Kr)&&Gr[Kr]===Qr[Wr][Kr])return Number(Wr)}return-1}function la(Qr,Gr){if(!Qr||!Qr.getBoundingClientRect)return Xi();var Wr=Qr,Kr=!1;do if(Wr.clientWidth<Wr.scrollWidth||Wr.clientHeight<Wr.scrollHeight){var Yr=Xn(Wr);if(Wr.clientWidth<Wr.scrollWidth&&(Yr.overflowX=="auto"||Yr.overflowX=="scroll")||Wr.clientHeight<Wr.scrollHeight&&(Yr.overflowY=="auto"||Yr.overflowY=="scroll")){if(!Wr.getBoundingClientRect||Wr===document.body)return Xi();if(Kr||Gr)return Wr;Kr=!0}}while(Wr=Wr.parentNode);return Xi()}function uu(Qr,Gr){if(Qr&&Gr)for(var Wr in Gr)Gr.hasOwnProperty(Wr)&&(Qr[Wr]=Gr[Wr]);return Qr}function is(Qr,Gr){return Math.round(Qr.top)===Math.round(Gr.top)&&Math.round(Qr.left)===Math.round(Gr.left)&&Math.round(Qr.height)===Math.round(Gr.height)&&Math.round(Qr.width)===Math.round(Gr.width)}var Qa;function cl(Qr,Gr){return function(){if(!Qa){var Wr=arguments,Kr=this;Wr.length===1?Qr.call(Kr,Wr[0]):Qr.apply(Kr,Wr),Qa=setTimeout(function(){Qa=void 0},Gr)}}}function cu(){clearTimeout(Qa),Qa=void 0}function fl(Qr,Gr,Wr){Qr.scrollLeft+=Gr,Qr.scrollTop+=Wr}function hl(Qr){var Gr=window.Polymer,Wr=window.jQuery||window.Zepto;return Gr&&Gr.dom?Gr.dom(Qr).cloneNode(!0):Wr?Wr(Qr).clone(!0)[0]:Qr.cloneNode(!0)}function dl(Qr,Gr,Wr){var Kr={};return Array.from(Qr.children).forEach(function(Yr){var Zr,tn,ln,dn;if(!(!Vi(Yr,Gr.draggable,Qr,!1)||Yr.animated||Yr===Wr)){var yn=yi(Yr);Kr.left=Math.min((Zr=Kr.left)!==null&&Zr!==void 0?Zr:1/0,yn.left),Kr.top=Math.min((tn=Kr.top)!==null&&tn!==void 0?tn:1/0,yn.top),Kr.right=Math.max((ln=Kr.right)!==null&&ln!==void 0?ln:-1/0,yn.right),Kr.bottom=Math.max((dn=Kr.bottom)!==null&&dn!==void 0?dn:-1/0,yn.bottom)}}),Kr.width=Kr.right-Kr.left,Kr.height=Kr.bottom-Kr.top,Kr.x=Kr.left,Kr.y=Kr.top,Kr}var Pi="Sortable"+new Date().getTime();function fu(){var Qr=[],Gr;return{captureAnimationState:function(){if(Qr=[],!!this.options.animation){var Kr=[].slice.call(this.el.children);Kr.forEach(function(Yr){if(!(Xn(Yr,"display")==="none"||Yr===Qn.ghost)){Qr.push({target:Yr,rect:yi(Yr)});var Zr=Qi({},Qr[Qr.length-1].rect);if(Yr.thisAnimationDuration){var tn=Na(Yr,!0);tn&&(Zr.top-=tn.f,Zr.left-=tn.e)}Yr.fromRect=Zr}})}},addAnimationState:function(Kr){Qr.push(Kr)},removeAnimationState:function(Kr){Qr.splice(lu(Qr,{target:Kr}),1)},animateAll:function(Kr){var Yr=this;if(!this.options.animation){clearTimeout(Gr),typeof Kr=="function"&&Kr();return}var Zr=!1,tn=0;Qr.forEach(function(ln){var dn=0,yn=ln.target,wn=yn.fromRect,kn=yi(yn),An=yn.prevFromRect,Gn=yn.prevToRect,jn=ln.rect,Vn=Na(yn,!0);Vn&&(kn.top-=Vn.f,kn.left-=Vn.e),yn.toRect=kn,yn.thisAnimationDuration&&is(An,kn)&&!is(wn,kn)&&(jn.top-kn.top)/(jn.left-kn.left)===(wn.top-kn.top)/(wn.left-kn.left)&&(dn=du(jn,An,Gn,Yr.options)),is(kn,wn)||(yn.prevFromRect=wn,yn.prevToRect=kn,dn||(dn=Yr.options.animation),Yr.animate(yn,jn,kn,dn)),dn&&(Zr=!0,tn=Math.max(tn,dn),clearTimeout(yn.animationResetTimer),yn.animationResetTimer=setTimeout(function(){yn.animationTime=0,yn.prevFromRect=null,yn.fromRect=null,yn.prevToRect=null,yn.thisAnimationDuration=null},dn),yn.thisAnimationDuration=dn)}),clearTimeout(Gr),Zr?Gr=setTimeout(function(){typeof Kr=="function"&&Kr()},tn):typeof Kr=="function"&&Kr(),Qr=[]},animate:function(Kr,Yr,Zr,tn){if(tn){Xn(Kr,"transition",""),Xn(Kr,"transform","");var ln=Na(this.el),dn=ln&&ln.a,yn=ln&&ln.d,wn=(Yr.left-Zr.left)/(dn||1),kn=(Yr.top-Zr.top)/(yn||1);Kr.animatingX=!!wn,Kr.animatingY=!!kn,Xn(Kr,"transform","translate3d("+wn+"px,"+kn+"px,0)"),this.forRepaintDummy=hu(Kr),Xn(Kr,"transition","transform "+tn+"ms"+(this.options.easing?" "+this.options.easing:"")),Xn(Kr,"transform","translate3d(0,0,0)"),typeof Kr.animated=="number"&&clearTimeout(Kr.animated),Kr.animated=setTimeout(function(){Xn(Kr,"transition",""),Xn(Kr,"transform",""),Kr.animated=!1,Kr.animatingX=!1,Kr.animatingY=!1},tn)}}}}function hu(Qr){return Qr.offsetWidth}function du(Qr,Gr,Wr,Kr){return Math.sqrt(Math.pow(Gr.top-Qr.top,2)+Math.pow(Gr.left-Qr.left,2))/Math.sqrt(Math.pow(Gr.top-Wr.top,2)+Math.pow(Gr.left-Wr.left,2))*Kr.animation}var Aa=[],as={initializeByDefault:!0},Za={mount:function(Gr){for(var Wr in as)as.hasOwnProperty(Wr)&&!(Wr in Gr)&&(Gr[Wr]=as[Wr]);Aa.forEach(function(Kr){if(Kr.pluginName===Gr.pluginName)throw"Sortable: Cannot mount plugin ".concat(Gr.pluginName," more than once")}),Aa.push(Gr)},pluginEvent:function(Gr,Wr,Kr){var Yr=this;this.eventCanceled=!1,Kr.cancel=function(){Yr.eventCanceled=!0};var Zr=Gr+"Global";Aa.forEach(function(tn){Wr[tn.pluginName]&&(Wr[tn.pluginName][Zr]&&Wr[tn.pluginName][Zr](Qi({sortable:Wr},Kr)),Wr.options[tn.pluginName]&&Wr[tn.pluginName][Gr]&&Wr[tn.pluginName][Gr](Qi({sortable:Wr},Kr)))})},initializePlugins:function(Gr,Wr,Kr,Yr){Aa.forEach(function(ln){var dn=ln.pluginName;if(!(!Gr.options[dn]&&!ln.initializeByDefault)){var yn=new ln(Gr,Wr,Gr.options);yn.sortable=Gr,yn.options=Gr.options,Gr[dn]=yn,ta(Kr,yn.defaults)}});for(var Zr in Gr.options)if(Gr.options.hasOwnProperty(Zr)){var tn=this.modifyOption(Gr,Zr,Gr.options[Zr]);typeof tn<"u"&&(Gr.options[Zr]=tn)}},getEventProperties:function(Gr,Wr){var Kr={};return Aa.forEach(function(Yr){typeof Yr.eventProperties=="function"&&ta(Kr,Yr.eventProperties.call(Wr[Yr.pluginName],Gr))}),Kr},modifyOption:function(Gr,Wr,Kr){var Yr;return Aa.forEach(function(Zr){Gr[Zr.pluginName]&&Zr.optionListeners&&typeof Zr.optionListeners[Wr]=="function"&&(Yr=Zr.optionListeners[Wr].call(Gr[Zr.pluginName],Kr))}),Yr}};function pu(Qr){var Gr=Qr.sortable,Wr=Qr.rootEl,Kr=Qr.name,Yr=Qr.targetEl,Zr=Qr.cloneEl,tn=Qr.toEl,ln=Qr.fromEl,dn=Qr.oldIndex,yn=Qr.newIndex,wn=Qr.oldDraggableIndex,kn=Qr.newDraggableIndex,An=Qr.originalEvent,Gn=Qr.putSortable,jn=Qr.extraEventProperties;if(Gr=Gr||Wr&&Wr[Pi],!!Gr){var Vn,ti=Gr.options,Ti="on"+Kr.charAt(0).toUpperCase()+Kr.substr(1);window.CustomEvent&&!ra&&!Ja?Vn=new CustomEvent(Kr,{bubbles:!0,cancelable:!0}):(Vn=document.createEvent("Event"),Vn.initEvent(Kr,!0,!0)),Vn.to=tn||Wr,Vn.from=ln||Wr,Vn.item=Yr||Wr,Vn.clone=Zr,Vn.oldIndex=dn,Vn.newIndex=yn,Vn.oldDraggableIndex=wn,Vn.newDraggableIndex=kn,Vn.originalEvent=An,Vn.pullMode=Gn?Gn.lastPutMode:void 0;var fi=Qi(Qi({},jn),Za.getEventProperties(Kr,Gr));for(var oi in fi)Vn[oi]=fi[oi];Wr&&Wr.dispatchEvent(Vn),ti[Ti]&&ti[Ti].call(Gr,Vn)}}var mu=["evt"],Di=function(Gr,Wr){var Kr=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{},Yr=Kr.evt,Zr=ou(Kr,mu);Za.pluginEvent.bind(Qn)(Gr,Wr,Qi({dragEl:qn,parentEl:hi,ghostEl:Yn,rootEl:ui,nextEl:va,lastDownEl:xo,cloneEl:ci,cloneHidden:sa,dragStarted:za,putSortable:wi,activeSortable:Qn.active,originalEvent:Yr,oldIndex:Ra,oldDraggableIndex:Ka,newIndex:Fi,newDraggableIndex:oa,hideGhostForTarget:vl,unhideGhostForTarget:yl,cloneNowHidden:function(){sa=!0},cloneNowShown:function(){sa=!1},dispatchSortableEvent:function(ln){Oi({sortable:Wr,name:ln,originalEvent:Yr})}},Zr))};function Oi(Qr){pu(Qi({putSortable:wi,cloneEl:ci,targetEl:qn,rootEl:ui,oldIndex:Ra,oldDraggableIndex:Ka,newIndex:Fi,newDraggableIndex:oa},Qr))}var qn,hi,Yn,ui,va,xo,ci,sa,Ra,Fi,Ka,oa,vo,wi,Pa=!1,Io=!1,Oo=[],ma,zi,os,ss,rl,nl,za,Da,Ya,$a=!1,yo=!1,So,Ii,ls=[],ds=!1,Ao=[],Po=typeof document<"u",bo=vs,il=Ja||ra?"cssFloat":"float",gu=Po&&!ol&&!vs&&"draggable"in document.createElement("div"),pl=function(){if(Po){if(ra)return!1;var Qr=document.createElement("x");return Qr.style.cssText="pointer-events:auto",Qr.style.pointerEvents==="auto"}}(),ml=function(Gr,Wr){var Kr=Xn(Gr),Yr=parseInt(Kr.width)-parseInt(Kr.paddingLeft)-parseInt(Kr.paddingRight)-parseInt(Kr.borderLeftWidth)-parseInt(Kr.borderRightWidth),Zr=La(Gr,0,Wr),tn=La(Gr,1,Wr),ln=Zr&&Xn(Zr),dn=tn&&Xn(tn),yn=ln&&parseInt(ln.marginLeft)+parseInt(ln.marginRight)+yi(Zr).width,wn=dn&&parseInt(dn.marginLeft)+parseInt(dn.marginRight)+yi(tn).width;if(Kr.display==="flex")return Kr.flexDirection==="column"||Kr.flexDirection==="column-reverse"?"vertical":"horizontal";if(Kr.display==="grid")return Kr.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(Zr&&ln.float&&ln.float!=="none"){var kn=ln.float==="left"?"left":"right";return tn&&(dn.clear==="both"||dn.clear===kn)?"vertical":"horizontal"}return Zr&&(ln.display==="block"||ln.display==="flex"||ln.display==="table"||ln.display==="grid"||yn>=Yr&&Kr[il]==="none"||tn&&Kr[il]==="none"&&yn+wn>Yr)?"vertical":"horizontal"},vu=function(Gr,Wr,Kr){var Yr=Kr?Gr.left:Gr.top,Zr=Kr?Gr.right:Gr.bottom,tn=Kr?Gr.width:Gr.height,ln=Kr?Wr.left:Wr.top,dn=Kr?Wr.right:Wr.bottom,yn=Kr?Wr.width:Wr.height;return Yr===ln||Zr===dn||Yr+tn/2===ln+yn/2},yu=function(Gr,Wr){var Kr;return Oo.some(function(Yr){var Zr=Yr[Pi].options.emptyInsertThreshold;if(!(!Zr||ys(Yr))){var tn=yi(Yr),ln=Gr>=tn.left-Zr&&Gr<=tn.right+Zr,dn=Wr>=tn.top-Zr&&Wr<=tn.bottom+Zr;if(ln&&dn)return Kr=Yr}}),Kr},gl=function(Gr){function Wr(Zr,tn){return function(ln,dn,yn,wn){var kn=ln.options.group.name&&dn.options.group.name&&ln.options.group.name===dn.options.group.name;if(Zr==null&&(tn||kn))return!0;if(Zr==null||Zr===!1)return!1;if(tn&&Zr==="clone")return Zr;if(typeof Zr=="function")return Wr(Zr(ln,dn,yn,wn),tn)(ln,dn,yn,wn);var An=(tn?ln:dn).options.group.name;return Zr===!0||typeof Zr=="string"&&Zr===An||Zr.join&&Zr.indexOf(An)>-1}}var Kr={},Yr=Gr.group;(!Yr||To(Yr)!="object")&&(Yr={name:Yr}),Kr.name=Yr.name,Kr.checkPull=Wr(Yr.pull,!0),Kr.checkPut=Wr(Yr.put),Kr.revertClone=Yr.revertClone,Gr.group=Kr},vl=function(){!pl&&Yn&&Xn(Yn,"display","none")},yl=function(){!pl&&Yn&&Xn(Yn,"display","")};Po&&!ol&&document.addEventListener("click",function(Qr){if(Io)return Qr.preventDefault(),Qr.stopPropagation&&Qr.stopPropagation(),Qr.stopImmediatePropagation&&Qr.stopImmediatePropagation(),Io=!1,!1},!0);var ga=function(Gr){if(qn){Gr=Gr.touches?Gr.touches[0]:Gr;var Wr=yu(Gr.clientX,Gr.clientY);if(Wr){var Kr={};for(var Yr in Gr)Gr.hasOwnProperty(Yr)&&(Kr[Yr]=Gr[Yr]);Kr.target=Kr.rootEl=Wr,Kr.preventDefault=void 0,Kr.stopPropagation=void 0,Wr[Pi]._onDragOver(Kr)}}},bu=function(Gr){qn&&qn.parentNode[Pi]._isOutsideThisEl(Gr.target)};function Qn(Qr,Gr){if(!(Qr&&Qr.nodeType&&Qr.nodeType===1))throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(Qr));this.el=Qr,this.options=Gr=ta({},Gr),Qr[Pi]=this;var Wr={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(Qr.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return ml(Qr,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(tn,ln){tn.setData("Text",ln.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:Qn.supportPointer!==!1&&"PointerEvent"in window&&(!Xa||vs),emptyInsertThreshold:5};Za.initializePlugins(this,Qr,Wr);for(var Kr in Wr)!(Kr in Gr)&&(Gr[Kr]=Wr[Kr]);gl(Gr);for(var Yr in this)Yr.charAt(0)==="_"&&typeof this[Yr]=="function"&&(this[Yr]=this[Yr].bind(this));this.nativeDraggable=Gr.forceFallback?!1:gu,this.nativeDraggable&&(this.options.touchStartThreshold=1),Gr.supportPointer?ei(Qr,"pointerdown",this._onTapStart):(ei(Qr,"mousedown",this._onTapStart),ei(Qr,"touchstart",this._onTapStart)),this.nativeDraggable&&(ei(Qr,"dragover",this),ei(Qr,"dragenter",this)),Oo.push(this.el),Gr.store&&Gr.store.get&&this.sort(Gr.store.get(this)||[]),ta(this,fu())}Qn.prototype={constructor:Qn,_isOutsideThisEl:function(Gr){!this.el.contains(Gr)&&Gr!==this.el&&(Da=null)},_getDirection:function(Gr,Wr){return typeof this.options.direction=="function"?this.options.direction.call(this,Gr,Wr,qn):this.options.direction},_onTapStart:function(Gr){if(Gr.cancelable){var Wr=this,Kr=this.el,Yr=this.options,Zr=Yr.preventOnFilter,tn=Gr.type,ln=Gr.touches&&Gr.touches[0]||Gr.pointerType&&Gr.pointerType==="touch"&&Gr,dn=(ln||Gr).target,yn=Gr.target.shadowRoot&&(Gr.path&&Gr.path[0]||Gr.composedPath&&Gr.composedPath()[0])||dn,wn=Yr.filter;if(Iu(Kr),!qn&&!(/mousedown|pointerdown/.test(tn)&&Gr.button!==0||Yr.disabled)&&!yn.isContentEditable&&!(!this.nativeDraggable&&Xa&&dn&&dn.tagName.toUpperCase()==="SELECT")&&(dn=Vi(dn,Yr.draggable,Kr,!1),!(dn&&dn.animated)&&xo!==dn)){if(Ra=Gi(dn),Ka=Gi(dn,Yr.draggable),typeof wn=="function"){if(wn.call(this,Gr,dn,this)){Oi({sortable:Wr,rootEl:yn,name:"filter",targetEl:dn,toEl:Kr,fromEl:Kr}),Di("filter",Wr,{evt:Gr}),Zr&&Gr.preventDefault();return}}else if(wn&&(wn=wn.split(",").some(function(kn){if(kn=Vi(yn,kn.trim(),Kr,!1),kn)return Oi({sortable:Wr,rootEl:kn,name:"filter",targetEl:dn,fromEl:Kr,toEl:Kr}),Di("filter",Wr,{evt:Gr}),!0}),wn)){Zr&&Gr.preventDefault();return}Yr.handle&&!Vi(yn,Yr.handle,Kr,!1)||this._prepareDragStart(Gr,ln,dn)}}},_prepareDragStart:function(Gr,Wr,Kr){var Yr=this,Zr=Yr.el,tn=Yr.options,ln=Zr.ownerDocument,dn;if(Kr&&!qn&&Kr.parentNode===Zr){var yn=yi(Kr);if(ui=Zr,qn=Kr,hi=qn.parentNode,va=qn.nextSibling,xo=Kr,vo=tn.group,Qn.dragged=qn,ma={target:qn,clientX:(Wr||Gr).clientX,clientY:(Wr||Gr).clientY},rl=ma.clientX-yn.left,nl=ma.clientY-yn.top,this._lastX=(Wr||Gr).clientX,this._lastY=(Wr||Gr).clientY,qn.style["will-change"]="all",dn=function(){if(Di("delayEnded",Yr,{evt:Gr}),Qn.eventCanceled){Yr._onDrop();return}Yr._disableDelayedDragEvents(),!Js&&Yr.nativeDraggable&&(qn.draggable=!0),Yr._triggerDragStart(Gr,Wr),Oi({sortable:Yr,name:"choose",originalEvent:Gr}),Mi(qn,tn.chosenClass,!0)},tn.ignore.split(",").forEach(function(wn){ul(qn,wn.trim(),us)}),ei(ln,"dragover",ga),ei(ln,"mousemove",ga),ei(ln,"touchmove",ga),tn.supportPointer?(ei(ln,"pointerup",Yr._onDrop),!this.nativeDraggable&&ei(ln,"pointercancel",Yr._onDrop)):(ei(ln,"mouseup",Yr._onDrop),ei(ln,"touchend",Yr._onDrop),ei(ln,"touchcancel",Yr._onDrop)),Js&&this.nativeDraggable&&(this.options.touchStartThreshold=4,qn.draggable=!0),Di("delayStart",this,{evt:Gr}),tn.delay&&(!tn.delayOnTouchOnly||Wr)&&(!this.nativeDraggable||!(Ja||ra))){if(Qn.eventCanceled){this._onDrop();return}tn.supportPointer?(ei(ln,"pointerup",Yr._disableDelayedDrag),ei(ln,"pointercancel",Yr._disableDelayedDrag)):(ei(ln,"mouseup",Yr._disableDelayedDrag),ei(ln,"touchend",Yr._disableDelayedDrag),ei(ln,"touchcancel",Yr._disableDelayedDrag)),ei(ln,"mousemove",Yr._delayedDragTouchMoveHandler),ei(ln,"touchmove",Yr._delayedDragTouchMoveHandler),tn.supportPointer&&ei(ln,"pointermove",Yr._delayedDragTouchMoveHandler),Yr._dragStartTimer=setTimeout(dn,tn.delay)}else dn()}},_delayedDragTouchMoveHandler:function(Gr){var Wr=Gr.touches?Gr.touches[0]:Gr;Math.max(Math.abs(Wr.clientX-this._lastX),Math.abs(Wr.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){qn&&us(qn),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var Gr=this.el.ownerDocument;Zn(Gr,"mouseup",this._disableDelayedDrag),Zn(Gr,"touchend",this._disableDelayedDrag),Zn(Gr,"touchcancel",this._disableDelayedDrag),Zn(Gr,"pointerup",this._disableDelayedDrag),Zn(Gr,"pointercancel",this._disableDelayedDrag),Zn(Gr,"mousemove",this._delayedDragTouchMoveHandler),Zn(Gr,"touchmove",this._delayedDragTouchMoveHandler),Zn(Gr,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(Gr,Wr){Wr=Wr||Gr.pointerType=="touch"&&Gr,!this.nativeDraggable||Wr?this.options.supportPointer?ei(document,"pointermove",this._onTouchMove):Wr?ei(document,"touchmove",this._onTouchMove):ei(document,"mousemove",this._onTouchMove):(ei(qn,"dragend",this),ei(ui,"dragstart",this._onDragStart));try{document.selection?wo(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch{}},_dragStarted:function(Gr,Wr){if(Pa=!1,ui&&qn){Di("dragStarted",this,{evt:Wr}),this.nativeDraggable&&ei(document,"dragover",bu);var Kr=this.options;!Gr&&Mi(qn,Kr.dragClass,!1),Mi(qn,Kr.ghostClass,!0),Qn.active=this,Gr&&this._appendGhost(),Oi({sortable:this,name:"start",originalEvent:Wr})}else this._nulling()},_emulateDragOver:function(){if(zi){this._lastX=zi.clientX,this._lastY=zi.clientY,vl();for(var Gr=document.elementFromPoint(zi.clientX,zi.clientY),Wr=Gr;Gr&&Gr.shadowRoot&&(Gr=Gr.shadowRoot.elementFromPoint(zi.clientX,zi.clientY),Gr!==Wr);)Wr=Gr;if(qn.parentNode[Pi]._isOutsideThisEl(Gr),Wr)do{if(Wr[Pi]){var Kr=void 0;if(Kr=Wr[Pi]._onDragOver({clientX:zi.clientX,clientY:zi.clientY,target:Gr,rootEl:Wr}),Kr&&!this.options.dragoverBubble)break}Gr=Wr}while(Wr=ll(Wr));yl()}},_onTouchMove:function(Gr){if(ma){var Wr=this.options,Kr=Wr.fallbackTolerance,Yr=Wr.fallbackOffset,Zr=Gr.touches?Gr.touches[0]:Gr,tn=Yn&&Na(Yn,!0),ln=Yn&&tn&&tn.a,dn=Yn&&tn&&tn.d,yn=bo&&Ii&&tl(Ii),wn=(Zr.clientX-ma.clientX+Yr.x)/(ln||1)+(yn?yn[0]-ls[0]:0)/(ln||1),kn=(Zr.clientY-ma.clientY+Yr.y)/(dn||1)+(yn?yn[1]-ls[1]:0)/(dn||1);if(!Qn.active&&!Pa){if(Kr&&Math.max(Math.abs(Zr.clientX-this._lastX),Math.abs(Zr.clientY-this._lastY))<Kr)return;this._onDragStart(Gr,!0)}if(Yn){tn?(tn.e+=wn-(os||0),tn.f+=kn-(ss||0)):tn={a:1,b:0,c:0,d:1,e:wn,f:kn};var An="matrix(".concat(tn.a,",").concat(tn.b,",").concat(tn.c,",").concat(tn.d,",").concat(tn.e,",").concat(tn.f,")");Xn(Yn,"webkitTransform",An),Xn(Yn,"mozTransform",An),Xn(Yn,"msTransform",An),Xn(Yn,"transform",An),os=wn,ss=kn,zi=Zr}Gr.cancelable&&Gr.preventDefault()}},_appendGhost:function(){if(!Yn){var Gr=this.options.fallbackOnBody?document.body:ui,Wr=yi(qn,!0,bo,!0,Gr),Kr=this.options;if(bo){for(Ii=Gr;Xn(Ii,"position")==="static"&&Xn(Ii,"transform")==="none"&&Ii!==document;)Ii=Ii.parentNode;Ii!==document.body&&Ii!==document.documentElement?(Ii===document&&(Ii=Xi()),Wr.top+=Ii.scrollTop,Wr.left+=Ii.scrollLeft):Ii=Xi(),ls=tl(Ii)}Yn=qn.cloneNode(!0),Mi(Yn,Kr.ghostClass,!1),Mi(Yn,Kr.fallbackClass,!0),Mi(Yn,Kr.dragClass,!0),Xn(Yn,"transition",""),Xn(Yn,"transform",""),Xn(Yn,"box-sizing","border-box"),Xn(Yn,"margin",0),Xn(Yn,"top",Wr.top),Xn(Yn,"left",Wr.left),Xn(Yn,"width",Wr.width),Xn(Yn,"height",Wr.height),Xn(Yn,"opacity","0.8"),Xn(Yn,"position",bo?"absolute":"fixed"),Xn(Yn,"zIndex","100000"),Xn(Yn,"pointerEvents","none"),Qn.ghost=Yn,Gr.appendChild(Yn),Xn(Yn,"transform-origin",rl/parseInt(Yn.style.width)*100+"% "+nl/parseInt(Yn.style.height)*100+"%")}},_onDragStart:function(Gr,Wr){var Kr=this,Yr=Gr.dataTransfer,Zr=Kr.options;if(Di("dragStart",this,{evt:Gr}),Qn.eventCanceled){this._onDrop();return}Di("setupClone",this),Qn.eventCanceled||(ci=hl(qn),ci.removeAttribute("id"),ci.draggable=!1,ci.style["will-change"]="",this._hideClone(),Mi(ci,this.options.chosenClass,!1),Qn.clone=ci),Kr.cloneId=wo(function(){Di("clone",Kr),!Qn.eventCanceled&&(Kr.options.removeCloneOnHide||ui.insertBefore(ci,qn),Kr._hideClone(),Oi({sortable:Kr,name:"clone"}))}),!Wr&&Mi(qn,Zr.dragClass,!0),Wr?(Io=!0,Kr._loopId=setInterval(Kr._emulateDragOver,50)):(Zn(document,"mouseup",Kr._onDrop),Zn(document,"touchend",Kr._onDrop),Zn(document,"touchcancel",Kr._onDrop),Yr&&(Yr.effectAllowed="move",Zr.setData&&Zr.setData.call(Kr,Yr,qn)),ei(document,"drop",Kr),Xn(qn,"transform","translateZ(0)")),Pa=!0,Kr._dragStartId=wo(Kr._dragStarted.bind(Kr,Wr,Gr)),ei(document,"selectstart",Kr),za=!0,window.getSelection().removeAllRanges(),Xa&&Xn(document.body,"user-select","none")},_onDragOver:function(Gr){var Wr=this.el,Kr=Gr.target,Yr,Zr,tn,ln=this.options,dn=ln.group,yn=Qn.active,wn=vo===dn,kn=ln.sort,An=wi||yn,Gn,jn=this,Vn=!1;if(ds)return;function ti(hn,On){Di(hn,jn,Qi({evt:Gr,isOwner:wn,axis:Gn?"vertical":"horizontal",revert:tn,dragRect:Yr,targetRect:Zr,canSort:kn,fromSortable:An,target:Kr,completed:fi,onMove:function(Nn,Dn){return Eo(ui,Wr,qn,Yr,Nn,yi(Nn),Gr,Dn)},changed:oi},On))}function Ti(){ti("dragOverAnimationCapture"),jn.captureAnimationState(),jn!==An&&An.captureAnimationState()}function fi(hn){return ti("dragOverCompleted",{insertion:hn}),hn&&(wn?yn._hideClone():yn._showClone(jn),jn!==An&&(Mi(qn,wi?wi.options.ghostClass:yn.options.ghostClass,!1),Mi(qn,ln.ghostClass,!0)),wi!==jn&&jn!==Qn.active?wi=jn:jn===Qn.active&&wi&&(wi=null),An===jn&&(jn._ignoreWhileAnimating=Kr),jn.animateAll(function(){ti("dragOverAnimationComplete"),jn._ignoreWhileAnimating=null}),jn!==An&&(An.animateAll(),An._ignoreWhileAnimating=null)),(Kr===qn&&!qn.animated||Kr===Wr&&!Kr.animated)&&(Da=null),!ln.dragoverBubble&&!Gr.rootEl&&Kr!==document&&(qn.parentNode[Pi]._isOutsideThisEl(Gr.target),!hn&&ga(Gr)),!ln.dragoverBubble&&Gr.stopPropagation&&Gr.stopPropagation(),Vn=!0}function oi(){Fi=Gi(qn),oa=Gi(qn,ln.draggable),Oi({sortable:jn,name:"change",toEl:Wr,newIndex:Fi,newDraggableIndex:oa,originalEvent:Gr})}if(Gr.preventDefault!==void 0&&Gr.cancelable&&Gr.preventDefault(),Kr=Vi(Kr,ln.draggable,Wr,!0),ti("dragOver"),Qn.eventCanceled)return Vn;if(qn.contains(Gr.target)||Kr.animated&&Kr.animatingX&&Kr.animatingY||jn._ignoreWhileAnimating===Kr)return fi(!1);if(Io=!1,yn&&!ln.disabled&&(wn?kn||(tn=hi!==ui):wi===this||(this.lastPutMode=vo.checkPull(this,yn,qn,Gr))&&dn.checkPut(this,yn,qn,Gr))){if(Gn=this._getDirection(Gr,Kr)==="vertical",Yr=yi(qn),ti("dragOverValid"),Qn.eventCanceled)return Vn;if(tn)return hi=ui,Ti(),this._hideClone(),ti("revert"),Qn.eventCanceled||(va?ui.insertBefore(qn,va):ui.appendChild(qn)),fi(!0);var Jn=ys(Wr,ln.draggable);if(!Jn||Su(Gr,Gn,this)&&!Jn.animated){if(Jn===qn)return fi(!1);if(Jn&&Wr===Gr.target&&(Kr=Jn),Kr&&(Zr=yi(Kr)),Eo(ui,Wr,qn,Yr,Kr,Zr,Gr,!!Kr)!==!1)return Ti(),Jn&&Jn.nextSibling?Wr.insertBefore(qn,Jn.nextSibling):Wr.appendChild(qn),hi=Wr,oi(),fi(!0)}else if(Jn&&xu(Gr,Gn,this)){var mi=La(Wr,0,ln,!0);if(mi===qn)return fi(!1);if(Kr=mi,Zr=yi(Kr),Eo(ui,Wr,qn,Yr,Kr,Zr,Gr,!1)!==!1)return Ti(),Wr.insertBefore(qn,mi),hi=Wr,oi(),fi(!0)}else if(Kr.parentNode===Wr){Zr=yi(Kr);var ai=0,Ui,gi=qn.parentNode!==Wr,ri=!vu(qn.animated&&qn.toRect||Yr,Kr.animated&&Kr.toRect||Zr,Gn),xn=Gn?"top":"left",en=el(Kr,"top","top")||el(qn,"top","top"),an=en?en.scrollTop:void 0;Da!==Kr&&(Ui=Zr[xn],$a=!1,yo=!ri&&ln.invertSwap||gi),ai=wu(Gr,Kr,Zr,Gn,ri?1:ln.swapThreshold,ln.invertedSwapThreshold==null?ln.swapThreshold:ln.invertedSwapThreshold,yo,Da===Kr);var pn;if(ai!==0){var mn=Gi(qn);do mn-=ai,pn=hi.children[mn];while(pn&&(Xn(pn,"display")==="none"||pn===Yn))}if(ai===0||pn===Kr)return fi(!1);Da=Kr,Ya=ai;var Sn=Kr.nextElementSibling,vn=!1;vn=ai===1;var bn=Eo(ui,Wr,qn,Yr,Kr,Zr,Gr,vn);if(bn!==!1)return(bn===1||bn===-1)&&(vn=bn===1),ds=!0,setTimeout(Tu,30),Ti(),vn&&!Sn?Wr.appendChild(qn):Kr.parentNode.insertBefore(qn,vn?Sn:Kr),en&&fl(en,0,an-en.scrollTop),hi=qn.parentNode,Ui!==void 0&&!yo&&(So=Math.abs(Ui-yi(Kr)[xn])),oi(),fi(!0)}if(Wr.contains(qn))return fi(!1)}return!1},_ignoreWhileAnimating:null,_offMoveEvents:function(){Zn(document,"mousemove",this._onTouchMove),Zn(document,"touchmove",this._onTouchMove),Zn(document,"pointermove",this._onTouchMove),Zn(document,"dragover",ga),Zn(document,"mousemove",ga),Zn(document,"touchmove",ga)},_offUpEvents:function(){var Gr=this.el.ownerDocument;Zn(Gr,"mouseup",this._onDrop),Zn(Gr,"touchend",this._onDrop),Zn(Gr,"pointerup",this._onDrop),Zn(Gr,"pointercancel",this._onDrop),Zn(Gr,"touchcancel",this._onDrop),Zn(document,"selectstart",this)},_onDrop:function(Gr){var Wr=this.el,Kr=this.options;if(Fi=Gi(qn),oa=Gi(qn,Kr.draggable),Di("drop",this,{evt:Gr}),hi=qn&&qn.parentNode,Fi=Gi(qn),oa=Gi(qn,Kr.draggable),Qn.eventCanceled){this._nulling();return}Pa=!1,yo=!1,$a=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),ps(this.cloneId),ps(this._dragStartId),this.nativeDraggable&&(Zn(document,"drop",this),Zn(Wr,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),Xa&&Xn(document.body,"user-select",""),Xn(qn,"transform",""),Gr&&(za&&(Gr.cancelable&&Gr.preventDefault(),!Kr.dropBubble&&Gr.stopPropagation()),Yn&&Yn.parentNode&&Yn.parentNode.removeChild(Yn),(ui===hi||wi&&wi.lastPutMode!=="clone")&&ci&&ci.parentNode&&ci.parentNode.removeChild(ci),qn&&(this.nativeDraggable&&Zn(qn,"dragend",this),us(qn),qn.style["will-change"]="",za&&!Pa&&Mi(qn,wi?wi.options.ghostClass:this.options.ghostClass,!1),Mi(qn,this.options.chosenClass,!1),Oi({sortable:this,name:"unchoose",toEl:hi,newIndex:null,newDraggableIndex:null,originalEvent:Gr}),ui!==hi?(Fi>=0&&(Oi({rootEl:hi,name:"add",toEl:hi,fromEl:ui,originalEvent:Gr}),Oi({sortable:this,name:"remove",toEl:hi,originalEvent:Gr}),Oi({rootEl:hi,name:"sort",toEl:hi,fromEl:ui,originalEvent:Gr}),Oi({sortable:this,name:"sort",toEl:hi,originalEvent:Gr})),wi&&wi.save()):Fi!==Ra&&Fi>=0&&(Oi({sortable:this,name:"update",toEl:hi,originalEvent:Gr}),Oi({sortable:this,name:"sort",toEl:hi,originalEvent:Gr})),Qn.active&&((Fi==null||Fi===-1)&&(Fi=Ra,oa=Ka),Oi({sortable:this,name:"end",toEl:hi,originalEvent:Gr}),this.save()))),this._nulling()},_nulling:function(){Di("nulling",this),ui=qn=hi=Yn=va=ci=xo=sa=ma=zi=za=Fi=oa=Ra=Ka=Da=Ya=wi=vo=Qn.dragged=Qn.ghost=Qn.clone=Qn.active=null,Ao.forEach(function(Gr){Gr.checked=!0}),Ao.length=os=ss=0},handleEvent:function(Gr){switch(Gr.type){case"drop":case"dragend":this._onDrop(Gr);break;case"dragenter":case"dragover":qn&&(this._onDragOver(Gr),Eu(Gr));break;case"selectstart":Gr.preventDefault();break}},toArray:function(){for(var Gr=[],Wr,Kr=this.el.children,Yr=0,Zr=Kr.length,tn=this.options;Yr<Zr;Yr++)Wr=Kr[Yr],Vi(Wr,tn.draggable,this.el,!1)&&Gr.push(Wr.getAttribute(tn.dataIdAttr)||Cu(Wr));return Gr},sort:function(Gr,Wr){var Kr={},Yr=this.el;this.toArray().forEach(function(Zr,tn){var ln=Yr.children[tn];Vi(ln,this.options.draggable,Yr,!1)&&(Kr[Zr]=ln)},this),Wr&&this.captureAnimationState(),Gr.forEach(function(Zr){Kr[Zr]&&(Yr.removeChild(Kr[Zr]),Yr.appendChild(Kr[Zr]))}),Wr&&this.animateAll()},save:function(){var Gr=this.options.store;Gr&&Gr.set&&Gr.set(this)},closest:function(Gr,Wr){return Vi(Gr,Wr||this.options.draggable,this.el,!1)},option:function(Gr,Wr){var Kr=this.options;if(Wr===void 0)return Kr[Gr];var Yr=Za.modifyOption(this,Gr,Wr);typeof Yr<"u"?Kr[Gr]=Yr:Kr[Gr]=Wr,Gr==="group"&&gl(Kr)},destroy:function(){Di("destroy",this);var Gr=this.el;Gr[Pi]=null,Zn(Gr,"mousedown",this._onTapStart),Zn(Gr,"touchstart",this._onTapStart),Zn(Gr,"pointerdown",this._onTapStart),this.nativeDraggable&&(Zn(Gr,"dragover",this),Zn(Gr,"dragenter",this)),Array.prototype.forEach.call(Gr.querySelectorAll("[draggable]"),function(Wr){Wr.removeAttribute("draggable")}),this._onDrop(),this._disableDelayedDragEvents(),Oo.splice(Oo.indexOf(this.el),1),this.el=Gr=null},_hideClone:function(){if(!sa){if(Di("hideClone",this),Qn.eventCanceled)return;Xn(ci,"display","none"),this.options.removeCloneOnHide&&ci.parentNode&&ci.parentNode.removeChild(ci),sa=!0}},_showClone:function(Gr){if(Gr.lastPutMode!=="clone"){this._hideClone();return}if(sa){if(Di("showClone",this),Qn.eventCanceled)return;qn.parentNode==ui&&!this.options.group.revertClone?ui.insertBefore(ci,qn):va?ui.insertBefore(ci,va):ui.appendChild(ci),this.options.group.revertClone&&this.animate(qn,ci),Xn(ci,"display",""),sa=!1}}};function Eu(Qr){Qr.dataTransfer&&(Qr.dataTransfer.dropEffect="move"),Qr.cancelable&&Qr.preventDefault()}function Eo(Qr,Gr,Wr,Kr,Yr,Zr,tn,ln){var dn,yn=Qr[Pi],wn=yn.options.onMove,kn;return window.CustomEvent&&!ra&&!Ja?dn=new CustomEvent("move",{bubbles:!0,cancelable:!0}):(dn=document.createEvent("Event"),dn.initEvent("move",!0,!0)),dn.to=Gr,dn.from=Qr,dn.dragged=Wr,dn.draggedRect=Kr,dn.related=Yr||Gr,dn.relatedRect=Zr||yi(Gr),dn.willInsertAfter=ln,dn.originalEvent=tn,Qr.dispatchEvent(dn),wn&&(kn=wn.call(yn,dn,tn)),kn}function us(Qr){Qr.draggable=!1}function Tu(){ds=!1}function xu(Qr,Gr,Wr){var Kr=yi(La(Wr.el,0,Wr.options,!0)),Yr=dl(Wr.el,Wr.options,Yn),Zr=10;return Gr?Qr.clientX<Yr.left-Zr||Qr.clientY<Kr.top&&Qr.clientX<Kr.right:Qr.clientY<Yr.top-Zr||Qr.clientY<Kr.bottom&&Qr.clientX<Kr.left}function Su(Qr,Gr,Wr){var Kr=yi(ys(Wr.el,Wr.options.draggable)),Yr=dl(Wr.el,Wr.options,Yn),Zr=10;return Gr?Qr.clientX>Yr.right+Zr||Qr.clientY>Kr.bottom&&Qr.clientX>Kr.left:Qr.clientY>Yr.bottom+Zr||Qr.clientX>Kr.right&&Qr.clientY>Kr.top}function wu(Qr,Gr,Wr,Kr,Yr,Zr,tn,ln){var dn=Kr?Qr.clientY:Qr.clientX,yn=Kr?Wr.height:Wr.width,wn=Kr?Wr.top:Wr.left,kn=Kr?Wr.bottom:Wr.right,An=!1;if(!tn){if(ln&&So<yn*Yr){if(!$a&&(Ya===1?dn>wn+yn*Zr/2:dn<kn-yn*Zr/2)&&($a=!0),$a)An=!0;else if(Ya===1?dn<wn+So:dn>kn-So)return-Ya}else if(dn>wn+yn*(1-Yr)/2&&dn<kn-yn*(1-Yr)/2)return ku(Gr)}return An=An||tn,An&&(dn<wn+yn*Zr/2||dn>kn-yn*Zr/2)?dn>wn+yn/2?1:-1:0}function ku(Qr){return Gi(qn)<Gi(Qr)?1:-1}function Cu(Qr){for(var Gr=Qr.tagName+Qr.className+Qr.src+Qr.href+Qr.textContent,Wr=Gr.length,Kr=0;Wr--;)Kr+=Gr.charCodeAt(Wr);return Kr.toString(36)}function Iu(Qr){Ao.length=0;for(var Gr=Qr.getElementsByTagName("input"),Wr=Gr.length;Wr--;){var Kr=Gr[Wr];Kr.checked&&Ao.push(Kr)}}function wo(Qr){return setTimeout(Qr,0)}function ps(Qr){return clearTimeout(Qr)}Po&&ei(document,"touchmove",function(Qr){(Qn.active||Pa)&&Qr.cancelable&&Qr.preventDefault()});Qn.utils={on:ei,off:Zn,css:Xn,find:ul,is:function(Gr,Wr){return!!Vi(Gr,Wr,Gr,!1)},extend:uu,throttle:cl,closest:Vi,toggleClass:Mi,clone:hl,index:Gi,nextTick:wo,cancelNextTick:ps,detectDirection:ml,getChild:La,expando:Pi};Qn.get=function(Qr){return Qr[Pi]};Qn.mount=function(){for(var Qr=arguments.length,Gr=new Array(Qr),Wr=0;Wr<Qr;Wr++)Gr[Wr]=arguments[Wr];Gr[0].constructor===Array&&(Gr=Gr[0]),Gr.forEach(function(Kr){if(!Kr.prototype||!Kr.prototype.constructor)throw"Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(Kr));Kr.utils&&(Qn.utils=Qi(Qi({},Qn.utils),Kr.utils)),Za.mount(Kr)})};Qn.create=function(Qr,Gr){return new Qn(Qr,Gr)};Qn.version=su;var vi=[],Va,ms,gs=!1,cs,fs,Do,Wa;function Ou(){function Qr(){this.defaults={scroll:!0,forceAutoScrollFallback:!1,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0};for(var Gr in this)Gr.charAt(0)==="_"&&typeof this[Gr]=="function"&&(this[Gr]=this[Gr].bind(this))}return Qr.prototype={dragStarted:function(Wr){var Kr=Wr.originalEvent;this.sortable.nativeDraggable?ei(document,"dragover",this._handleAutoScroll):this.options.supportPointer?ei(document,"pointermove",this._handleFallbackAutoScroll):Kr.touches?ei(document,"touchmove",this._handleFallbackAutoScroll):ei(document,"mousemove",this._handleFallbackAutoScroll)},dragOverCompleted:function(Wr){var Kr=Wr.originalEvent;!this.options.dragOverBubble&&!Kr.rootEl&&this._handleAutoScroll(Kr)},drop:function(){this.sortable.nativeDraggable?Zn(document,"dragover",this._handleAutoScroll):(Zn(document,"pointermove",this._handleFallbackAutoScroll),Zn(document,"touchmove",this._handleFallbackAutoScroll),Zn(document,"mousemove",this._handleFallbackAutoScroll)),al(),ko(),cu()},nulling:function(){Do=ms=Va=gs=Wa=cs=fs=null,vi.length=0},_handleFallbackAutoScroll:function(Wr){this._handleAutoScroll(Wr,!0)},_handleAutoScroll:function(Wr,Kr){var Yr=this,Zr=(Wr.touches?Wr.touches[0]:Wr).clientX,tn=(Wr.touches?Wr.touches[0]:Wr).clientY,ln=document.elementFromPoint(Zr,tn);if(Do=Wr,Kr||this.options.forceAutoScrollFallback||Ja||ra||Xa){hs(Wr,this.options,ln,Kr);var dn=la(ln,!0);gs&&(!Wa||Zr!==cs||tn!==fs)&&(Wa&&al(),Wa=setInterval(function(){var yn=la(document.elementFromPoint(Zr,tn),!0);yn!==dn&&(dn=yn,ko()),hs(Wr,Yr.options,yn,Kr)},10),cs=Zr,fs=tn)}else{if(!this.options.bubbleScroll||la(ln,!0)===Xi()){ko();return}hs(Wr,this.options,la(ln,!1),!1)}}},ta(Qr,{pluginName:"scroll",initializeByDefault:!0})}function ko(){vi.forEach(function(Qr){clearInterval(Qr.pid)}),vi=[]}function al(){clearInterval(Wa)}var hs=cl(function(Qr,Gr,Wr,Kr){if(Gr.scroll){var Yr=(Qr.touches?Qr.touches[0]:Qr).clientX,Zr=(Qr.touches?Qr.touches[0]:Qr).clientY,tn=Gr.scrollSensitivity,ln=Gr.scrollSpeed,dn=Xi(),yn=!1,wn;ms!==Wr&&(ms=Wr,ko(),Va=Gr.scroll,wn=Gr.scrollFn,Va===!0&&(Va=la(Wr,!0)));var kn=0,An=Va;do{var Gn=An,jn=yi(Gn),Vn=jn.top,ti=jn.bottom,Ti=jn.left,fi=jn.right,oi=jn.width,Jn=jn.height,mi=void 0,ai=void 0,Ui=Gn.scrollWidth,gi=Gn.scrollHeight,ri=Xn(Gn),xn=Gn.scrollLeft,en=Gn.scrollTop;Gn===dn?(mi=oi<Ui&&(ri.overflowX==="auto"||ri.overflowX==="scroll"||ri.overflowX==="visible"),ai=Jn<gi&&(ri.overflowY==="auto"||ri.overflowY==="scroll"||ri.overflowY==="visible")):(mi=oi<Ui&&(ri.overflowX==="auto"||ri.overflowX==="scroll"),ai=Jn<gi&&(ri.overflowY==="auto"||ri.overflowY==="scroll"));var an=mi&&(Math.abs(fi-Yr)<=tn&&xn+oi<Ui)-(Math.abs(Ti-Yr)<=tn&&!!xn),pn=ai&&(Math.abs(ti-Zr)<=tn&&en+Jn<gi)-(Math.abs(Vn-Zr)<=tn&&!!en);if(!vi[kn])for(var mn=0;mn<=kn;mn++)vi[mn]||(vi[mn]={});(vi[kn].vx!=an||vi[kn].vy!=pn||vi[kn].el!==Gn)&&(vi[kn].el=Gn,vi[kn].vx=an,vi[kn].vy=pn,clearInterval(vi[kn].pid),(an!=0||pn!=0)&&(yn=!0,vi[kn].pid=setInterval(function(){Kr&&this.layer===0&&Qn.active._onTouchMove(Do);var Sn=vi[this.layer].vy?vi[this.layer].vy*ln:0,vn=vi[this.layer].vx?vi[this.layer].vx*ln:0;typeof wn=="function"&&wn.call(Qn.dragged.parentNode[Pi],vn,Sn,Qr,Do,vi[this.layer].el)!=="continue"||fl(vi[this.layer].el,vn,Sn)}.bind({layer:kn}),24))),kn++}while(Gr.bubbleScroll&&An!==dn&&(An=la(An,!1)));gs=yn}},30),bl=function(Gr){var Wr=Gr.originalEvent,Kr=Gr.putSortable,Yr=Gr.dragEl,Zr=Gr.activeSortable,tn=Gr.dispatchSortableEvent,ln=Gr.hideGhostForTarget,dn=Gr.unhideGhostForTarget;if(Wr){var yn=Kr||Zr;ln();var wn=Wr.changedTouches&&Wr.changedTouches.length?Wr.changedTouches[0]:Wr,kn=document.elementFromPoint(wn.clientX,wn.clientY);dn(),yn&&!yn.el.contains(kn)&&(tn("spill"),this.onSpill({dragEl:Yr,putSortable:Kr}))}};function bs(){}bs.prototype={startIndex:null,dragStart:function(Gr){var Wr=Gr.oldDraggableIndex;this.startIndex=Wr},onSpill:function(Gr){var Wr=Gr.dragEl,Kr=Gr.putSortable;this.sortable.captureAnimationState(),Kr&&Kr.captureAnimationState();var Yr=La(this.sortable.el,this.startIndex,this.options);Yr?this.sortable.el.insertBefore(Wr,Yr):this.sortable.el.appendChild(Wr),this.sortable.animateAll(),Kr&&Kr.animateAll()},drop:bl};ta(bs,{pluginName:"revertOnSpill"});function Es(){}Es.prototype={onSpill:function(Gr){var Wr=Gr.dragEl,Kr=Gr.putSortable,Yr=Kr||this.sortable;Yr.captureAnimationState(),Wr.parentNode&&Wr.parentNode.removeChild(Wr),Yr.animateAll()},drop:bl};ta(Es,{pluginName:"removeOnSpill"});Qn.mount(new Ou);Qn.mount(Es,bs);var El=Qn;var Ro=class{observerInitialized=!1;layoutMode;containerSelector;spacing;masonryBreakpoint;constructor(Gr={}){this.layoutMode=Gr.layoutMode??"justified",this.containerSelector=Gr.containerSelector??"#gallery-container",this.spacing=Gr.spacing??8,this.masonryBreakpoint=Gr.masonryBreakpoint??640}setLayoutMode(Gr){this.layoutMode=Gr,this.computeLayout()}computeLayout(){this.layoutMode==="masonry"?this.computeMasonry():this.computeJustified()}computeMasonry(){let Gr=document.querySelector(this.containerSelector);if(!Gr)return;let Wr=this.spacing,Kr=Gr.offsetWidth;if(Kr===0){requestAnimationFrame(()=>this.computeMasonry());return}let Yr=Kr<this.masonryBreakpoint?1:3,Zr=(Kr+Wr)/Yr-Wr,tn=new Array(Yr).fill(0);Gr.querySelectorAll(".gallery-item").forEach(dn=>{let yn=parseFloat(dn.dataset.width),wn=parseFloat(dn.dataset.height);if(!yn||!wn)return;let An=wn/yn*Zr,Gn=0;for(let ti=1;ti<Yr;ti++)tn[ti]<tn[Gn]&&(Gn=ti);let jn=(Zr+Wr)*Gn,Vn=tn[Gn];Object.assign(dn.style,{position:"absolute",width:`${Zr}px`,height:`${An}px`,left:`${jn}px`,top:`${Vn}px`}),tn[Gn]=Vn+An+Wr}),Gr.style.height=`${Math.max(...tn)}px`}computeJustified(){let Gr=document.querySelector(this.containerSelector);if(!Gr)return;let Wr=this.spacing,Kr=Gr.offsetWidth;if(Kr===0){requestAnimationFrame(()=>this.computeJustified());return}let Yr=Array.from(Gr.querySelectorAll(".gallery-item")),Zr=[],tn=0,ln=0;Yr.forEach(dn=>{Object.assign(dn.style,{position:"absolute",left:"0px",top:"0px",width:"auto",height:"auto"})});for(let dn=0;dn<Yr.length;dn++){let yn=Yr[dn],wn=parseFloat(yn.dataset.width),kn=parseFloat(yn.dataset.height);if(!wn||!kn)continue;let An=wn/kn;Zr.push({tile:yn,aspectRatio:An,imgW:wn,imgH:kn}),tn+=An;let Gn=(Kr-(Zr.length-1)*Wr)/tn;if(Gn<300||dn===Yr.length-1){let jn=0;for(let Vn of Zr){let ti=Gn*Vn.aspectRatio;Object.assign(Vn.tile.style,{position:"absolute",top:`${ln}px`,left:`${jn}px`,width:`${ti}px`,height:`${Gn}px`}),jn+=ti+Wr}ln+=Gn+Wr,Zr=[],tn=0}}Gr.style.position="relative",Gr.style.height=`${ln}px`}observe(){if(this.observerInitialized)return;this.observerInitialized=!0;let Gr=document.querySelector(this.containerSelector);if(!Gr)return;if(typeof ResizeObserver<"u"){let Kr=new ResizeObserver(()=>this.computeLayout());Gr.parentElement&&Kr.observe(Gr.parentElement)}new MutationObserver(()=>{this.computeLayout()}).observe(Gr,{childList:!0,subtree:!0})}init(Gr={}){document.addEventListener("DOMContentLoaded",()=>{document.querySelector(Gr.containerSelector??"#gallery-container")&&(this.computeLayout(),this.observe())})}};var No=class Qr{startX=0;threshold=50;observer;static initialized=!1;static id="photo-dialog";constructor(){this.observer=new MutationObserver(this.handleMutation.bind(this))}connect(){this.onTouchStart=this.onTouchStart.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this),console.log("PhotoDialog: Connected touch event handlers.")}static maybeInitForElement(Gr){if(Gr.id!==Qr.id||Qr.initialized)return;let Wr=new Qr;Wr.connect(),document&&document.body?(document.body.addEventListener("touchstart",Wr.onTouchStart),document.body.addEventListener("touchend",Wr.onTouchEnd),Wr.observe(),Qr.initialized=!0):console.warn("document.body not available for PhotoDialog event listeners")}onTouchStart(Gr){this.startX=Gr.touches[0].clientX}onTouchEnd(Gr){let Kr=Gr.changedTouches[0].clientX-this.startX;if(Math.abs(Kr)>this.threshold){let Yr=Kr>0?"swiperight":"swipeleft";Gr.target.dispatchEvent(new CustomEvent(Yr,{bubbles:!0}))}}observe(){this.observer.observe(document.body,{childList:!0,subtree:!0})}disconnect(){this.observer.disconnect(),document.body.removeEventListener("touchstart",this.onTouchStart),document.body.removeEventListener("touchend",this.onTouchEnd),Qr.initialized=!1}handleMutation(){document.getElementById(Qr.id)||(console.log("PhotoDialog not found, removing event listeners"),this.disconnect())}};var Ts={};ru(Ts,{dataURLToBlob:()=>eo,doResize:()=>to,readFileAsDataURL:()=>Ma});function Ma(Qr){return new Promise((Gr,Wr)=>{let Kr=new FileReader;Kr.onload=()=>Gr(Kr.result),Kr.onerror=Wr,Kr.readAsDataURL(Qr)})}function eo(Qr){let[Gr,Wr]=Qr.split(","),Kr=/:(.*?);/.exec(Gr);if(!Kr)throw new Error("Invalid data URL");let Yr=Kr[1],Zr=atob(Wr),tn=new Uint8Array(Zr.length);for(let ln=0;ln<Zr.length;ln++)tn[ln]=Zr.charCodeAt(ln);return new Blob([tn],{type:Yr})}function Tl(Qr){let Gr=Qr.split(",")[1];return Math.ceil(Gr.length*3/4)}function Au(Qr,Gr){return new Promise((Wr,Kr)=>{let Yr=new Image;Yr.onload=()=>{let Zr;Gr.mode==="cover"?Zr=Math.max(Gr.width/Yr.width,Gr.height/Yr.height):Gr.mode==="contain"?Zr=Math.min(Gr.width/Yr.width,Gr.height/Yr.height):Zr=1;let tn=Math.round(Yr.width*Zr),ln=Math.round(Yr.height*Zr),dn=document.createElement("canvas");dn.width=tn,dn.height=ln;let yn=dn.getContext("2d");if(!yn)return Kr(new Error("Failed to get canvas context"));yn.fillStyle="#fff",yn.fillRect(0,0,tn,ln),yn.imageSmoothingEnabled=!0,yn.imageSmoothingQuality="high",yn.drawImage(Yr,0,0,tn,ln),Wr({dataUrl:dn.toDataURL("image/jpeg",Gr.quality),width:tn,height:ln})},Yr.onerror=Zr=>Kr(Zr),Yr.src=Qr})}async function to(Qr,Gr){let Wr=null,Kr=0,Yr=101;for(;Yr-Kr>1;){let Zr=Math.round((Kr+Yr)/2),tn=await Au(Qr,{width:Gr.width,height:Gr.height,quality:Zr/100,mode:Gr.mode});Tl(tn.dataUrl)<Gr.maxSize?(Kr=Zr,Wr=tn):Yr=Zr}if(!Wr)throw new Error("Failed to compress image");return{path:Wr.dataUrl,mime:"image/jpeg",size:Tl(Wr.dataUrl),width:Wr.width,height:Wr.height}}var xl=po(mo());var Lo=class{handleAvatarImageSelect(Gr){let Wr=Gr.files?.[0];Wr&&Ma(Wr).then(Kr=>{let Yr=document.createElement("img");typeof Kr=="string"?Yr.src=Kr:(console.error("Expected dataUrl to be a string, got:",typeof Kr),Yr.src=""),Yr.className="rounded-full w-full h-full object-cover",Yr.alt="Avatar preview";let Zr=Gr.closest("form")?.querySelector("#image-preview");Zr&&(Zr.innerHTML="",Zr.appendChild(Yr))})}async updateProfile(Gr){let Wr=new FormData(Gr),Kr=Wr.get("file");if(Kr instanceof File&&Kr?.type?.startsWith?.("image/"))try{let Yr=await Ma(Kr);if(!Yr||typeof Yr!="string"){console.error("Failed to read file as data URL");return}let Zr=await to(Yr,{width:2e3,height:2e3,maxSize:1e3*1e3,mode:"contain"}),tn=eo(Zr.path);Wr.set("file",tn,Kr.name)}catch(Yr){console.error("Error resizing image:",Yr),Wr.delete("file")}xl.default.ajax("PUT","/actions/profile",{swap:"none",values:Object.fromEntries(Wr),source:Gr})}};var Yo=typeof self<"u"?self:global,so=typeof navigator<"u",Du=so&&typeof HTMLImageElement>"u",qo=!(typeof global>"u"||typeof process>"u"||!process.versions||!process.versions.node),$o=Yo.Buffer,Mo=Yo.BigInt,Jo=!!$o,Pu=Qr=>Qr;function Uo(Qr,Gr=Pu){if(qo)try{return typeof ns=="function"?Promise.resolve(Gr(ns(Qr))):import(Qr).then(Gr)}catch{console.warn(`Couldn't load ${Qr}`)}}var Fs=Yo.fetch,Ru=Qr=>Fs=Qr;if(!Yo.fetch){let Qr=Uo("http",Kr=>Kr),Gr=Uo("https",Kr=>Kr),Wr=(Kr,{headers:Yr}={})=>new Promise(async(Zr,tn)=>{let{port:ln,hostname:dn,pathname:yn,protocol:wn,search:kn}=new URL(Kr),An={method:"GET",hostname:dn,path:encodeURI(yn)+kn,headers:Yr};ln!==""&&(An.port=Number(ln));let Gn=(wn==="https:"?await Gr:await Qr).request(An,jn=>{if(jn.statusCode===301||jn.statusCode===302){let Vn=new URL(jn.headers.location,Kr).toString();return Wr(Vn,{headers:Yr}).then(Zr).catch(tn)}Zr({status:jn.statusCode,arrayBuffer:()=>new Promise(Vn=>{let ti=[];jn.on("data",Ti=>ti.push(Ti)),jn.on("end",()=>Vn(Buffer.concat(ti)))})})});Gn.on("error",tn),Gn.end()});Ru(Wr)}function Kn(Qr,Gr,Wr){return Gr in Qr?Object.defineProperty(Qr,Gr,{value:Wr,enumerable:!0,configurable:!0,writable:!0}):Qr[Gr]=Wr,Qr}var Bo=Qr=>Fl(Qr)?void 0:Qr,Nu=Qr=>Qr!==void 0;function Fl(Qr){return Qr===void 0||(Qr instanceof Map?Qr.size===0:Object.values(Qr).filter(Nu).length===0)}function Ei(Qr){let Gr=new Error(Qr);throw delete Gr.stack,Gr}function ba(Qr){return(Qr=function(Gr){for(;Gr.endsWith("\0");)Gr=Gr.slice(0,-1);return Gr}(Qr).trim())===""?void 0:Qr}function ks(Qr){let Gr=function(Wr){let Kr=0;return Wr.ifd0.enabled&&(Kr+=1024),Wr.exif.enabled&&(Kr+=2048),Wr.makerNote&&(Kr+=2048),Wr.userComment&&(Kr+=1024),Wr.gps.enabled&&(Kr+=512),Wr.interop.enabled&&(Kr+=100),Wr.ifd1.enabled&&(Kr+=1024),Kr+2048}(Qr);return Qr.jfif.enabled&&(Gr+=50),Qr.xmp.enabled&&(Gr+=2e4),Qr.iptc.enabled&&(Gr+=14e3),Qr.icc.enabled&&(Gr+=6e3),Gr}var Cs=Qr=>String.fromCharCode.apply(null,Qr),Sl=typeof TextDecoder<"u"?new TextDecoder("utf-8"):void 0;function ql(Qr){return Sl?Sl.decode(Qr):Jo?Buffer.from(Qr).toString("utf8"):decodeURIComponent(escape(Cs(Qr)))}var Ki=class Qr{static from(Gr,Wr){return Gr instanceof this&&Gr.le===Wr?Gr:new Qr(Gr,void 0,void 0,Wr)}constructor(Gr,Wr=0,Kr,Yr){if(typeof Yr=="boolean"&&(this.le=Yr),Array.isArray(Gr)&&(Gr=new Uint8Array(Gr)),Gr===0)this.byteOffset=0,this.byteLength=0;else if(Gr instanceof ArrayBuffer){Kr===void 0&&(Kr=Gr.byteLength-Wr);let Zr=new DataView(Gr,Wr,Kr);this._swapDataView(Zr)}else if(Gr instanceof Uint8Array||Gr instanceof DataView||Gr instanceof Qr){Kr===void 0&&(Kr=Gr.byteLength-Wr),(Wr+=Gr.byteOffset)+Kr>Gr.byteOffset+Gr.byteLength&&Ei("Creating view outside of available memory in ArrayBuffer");let Zr=new DataView(Gr.buffer,Wr,Kr);this._swapDataView(Zr)}else if(typeof Gr=="number"){let Zr=new DataView(new ArrayBuffer(Gr));this._swapDataView(Zr)}else Ei("Invalid input argument for BufferView: "+Gr)}_swapArrayBuffer(Gr){this._swapDataView(new DataView(Gr))}_swapBuffer(Gr){this._swapDataView(new DataView(Gr.buffer,Gr.byteOffset,Gr.byteLength))}_swapDataView(Gr){this.dataView=Gr,this.buffer=Gr.buffer,this.byteOffset=Gr.byteOffset,this.byteLength=Gr.byteLength}_lengthToEnd(Gr){return this.byteLength-Gr}set(Gr,Wr,Kr=Qr){return Gr instanceof DataView||Gr instanceof Qr?Gr=new Uint8Array(Gr.buffer,Gr.byteOffset,Gr.byteLength):Gr instanceof ArrayBuffer&&(Gr=new Uint8Array(Gr)),Gr instanceof Uint8Array||Ei("BufferView.set(): Invalid data argument."),this.toUint8().set(Gr,Wr),new Kr(this,Wr,Gr.byteLength)}subarray(Gr,Wr){return Wr=Wr||this._lengthToEnd(Gr),new Qr(this,Gr,Wr)}toUint8(){return new Uint8Array(this.buffer,this.byteOffset,this.byteLength)}getUint8Array(Gr,Wr){return new Uint8Array(this.buffer,this.byteOffset+Gr,Wr)}getString(Gr=0,Wr=this.byteLength){return ql(this.getUint8Array(Gr,Wr))}getLatin1String(Gr=0,Wr=this.byteLength){let Kr=this.getUint8Array(Gr,Wr);return Cs(Kr)}getUnicodeString(Gr=0,Wr=this.byteLength){let Kr=[];for(let Yr=0;Yr<Wr&&Gr+Yr<this.byteLength;Yr+=2)Kr.push(this.getUint16(Gr+Yr));return Cs(Kr)}getInt8(Gr){return this.dataView.getInt8(Gr)}getUint8(Gr){return this.dataView.getUint8(Gr)}getInt16(Gr,Wr=this.le){return this.dataView.getInt16(Gr,Wr)}getInt32(Gr,Wr=this.le){return this.dataView.getInt32(Gr,Wr)}getUint16(Gr,Wr=this.le){return this.dataView.getUint16(Gr,Wr)}getUint32(Gr,Wr=this.le){return this.dataView.getUint32(Gr,Wr)}getFloat32(Gr,Wr=this.le){return this.dataView.getFloat32(Gr,Wr)}getFloat64(Gr,Wr=this.le){return this.dataView.getFloat64(Gr,Wr)}getFloat(Gr,Wr=this.le){return this.dataView.getFloat32(Gr,Wr)}getDouble(Gr,Wr=this.le){return this.dataView.getFloat64(Gr,Wr)}getUintBytes(Gr,Wr,Kr){switch(Wr){case 1:return this.getUint8(Gr,Kr);case 2:return this.getUint16(Gr,Kr);case 4:return this.getUint32(Gr,Kr);case 8:return this.getUint64&&this.getUint64(Gr,Kr)}}getUint(Gr,Wr,Kr){switch(Wr){case 8:return this.getUint8(Gr,Kr);case 16:return this.getUint16(Gr,Kr);case 32:return this.getUint32(Gr,Kr);case 64:return this.getUint64&&this.getUint64(Gr,Kr)}}toString(Gr){return this.dataView.toString(Gr,this.constructor.name)}ensureChunk(){}};function Is(Qr,Gr){Ei(`${Qr} '${Gr}' was not loaded, try using full build of exifr.`)}var lo=class extends Map{constructor(Gr){super(),this.kind=Gr}get(Gr,Wr){return this.has(Gr)||Is(this.kind,Gr),Wr&&(Gr in Wr||function(Kr,Yr){Ei(`Unknown ${Kr} '${Yr}'.`)}(this.kind,Gr),Wr[Gr].enabled||Is(this.kind,Gr)),super.get(Gr)}keyList(){return Array.from(this.keys())}},Yi=new lo("file parser"),bi=new lo("segment parser"),Ji=new lo("file reader");function Lu(Qr,Gr){return typeof Qr=="string"?wl(Qr,Gr):so&&!Du&&Qr instanceof HTMLImageElement?wl(Qr.src,Gr):Qr instanceof Uint8Array||Qr instanceof ArrayBuffer||Qr instanceof DataView?new Ki(Qr):so&&Qr instanceof Blob?Os(Qr,Gr,"blob",Sa):void Ei("Invalid input argument")}function wl(Qr,Gr){return(Wr=Qr).startsWith("data:")||Wr.length>1e4?As(Qr,Gr,"base64"):qo&&Qr.includes("://")?Os(Qr,Gr,"url",xa):qo?As(Qr,Gr,"fs"):so?Os(Qr,Gr,"url",xa):void Ei("Invalid input argument");var Wr}async function Os(Qr,Gr,Wr,Kr){return Ji.has(Wr)?As(Qr,Gr,Wr):Kr?async function(Yr,Zr){let tn=await Zr(Yr);return new Ki(tn)}(Qr,Kr):void Ei(`Parser ${Wr} is not loaded`)}async function As(Qr,Gr,Wr){let Kr=new(Ji.get(Wr))(Qr,Gr);return await Kr.read(),Kr}var xa=Qr=>Fs(Qr).then(Gr=>Gr.arrayBuffer()),Sa=Qr=>new Promise((Gr,Wr)=>{let Kr=new FileReader;Kr.onloadend=()=>Gr(Kr.result||new ArrayBuffer),Kr.onerror=Wr,Kr.readAsArrayBuffer(Qr)}),Ds=class extends Map{get tagKeys(){return this.allKeys||(this.allKeys=Array.from(this.keys())),this.allKeys}get tagValues(){return this.allValues||(this.allValues=Array.from(this.values())),this.allValues}};function pi(Qr,Gr,Wr){let Kr=new Ds;for(let[Yr,Zr]of Wr)Kr.set(Yr,Zr);if(Array.isArray(Gr))for(let Yr of Gr)Qr.set(Yr,Kr);else Qr.set(Gr,Kr);return Kr}function wa(Qr,Gr,Wr){let Kr,Yr=Qr.get(Gr);for(Kr of Wr)Yr.set(Kr[0],Kr[1])}var Si=new Map,Wi=new Map,da=new Map,ua=["chunked","firstChunkSize","firstChunkSizeNode","firstChunkSizeBrowser","chunkSize","chunkLimit"],Ha=["jfif","xmp","icc","iptc","ihdr"],ka=["tiff",...Ha],di=["ifd0","ifd1","exif","gps","interop"],ca=[...ka,...di],fa=["makerNote","userComment"],Ga=["translateKeys","translateValues","reviveValues","multiSegment"],ha=[...Ga,"sanitize","mergeOutput","silentErrors"],Ho=class{get translate(){return this.translateKeys||this.translateValues||this.reviveValues}},ya=class extends Ho{get needed(){return this.enabled||this.deps.size>0}constructor(Gr,Wr,Kr,Yr){if(super(),Kn(this,"enabled",!1),Kn(this,"skip",new Set),Kn(this,"pick",new Set),Kn(this,"deps",new Set),Kn(this,"translateKeys",!1),Kn(this,"translateValues",!1),Kn(this,"reviveValues",!1),this.key=Gr,this.enabled=Wr,this.parse=this.enabled,this.applyInheritables(Yr),this.canBeFiltered=di.includes(Gr),this.canBeFiltered&&(this.dict=Si.get(Gr)),Kr!==void 0)if(Array.isArray(Kr))this.parse=this.enabled=!0,this.canBeFiltered&&Kr.length>0&&this.translateTagSet(Kr,this.pick);else if(typeof Kr=="object"){if(this.enabled=!0,this.parse=Kr.parse!==!1,this.canBeFiltered){let{pick:Zr,skip:tn}=Kr;Zr&&Zr.length>0&&this.translateTagSet(Zr,this.pick),tn&&tn.length>0&&this.translateTagSet(tn,this.skip)}this.applyInheritables(Kr)}else Kr===!0||Kr===!1?this.parse=this.enabled=Kr:Ei(`Invalid options argument: ${Kr}`)}applyInheritables(Gr){let Wr,Kr;for(Wr of Ga)Kr=Gr[Wr],Kr!==void 0&&(this[Wr]=Kr)}translateTagSet(Gr,Wr){if(this.dict){let Kr,Yr,{tagKeys:Zr,tagValues:tn}=this.dict;for(Kr of Gr)typeof Kr=="string"?(Yr=tn.indexOf(Kr),Yr===-1&&(Yr=Zr.indexOf(Number(Kr))),Yr!==-1&&Wr.add(Number(Zr[Yr]))):Wr.add(Kr)}else for(let Kr of Gr)Wr.add(Kr)}finalizeFilters(){!this.enabled&&this.deps.size>0?(this.enabled=!0,Go(this.pick,this.deps)):this.enabled&&this.pick.size>0&&Go(this.pick,this.deps)}},Ai={jfif:!1,tiff:!0,xmp:!1,icc:!1,iptc:!1,ifd0:!0,ifd1:!1,exif:!0,gps:!0,interop:!1,ihdr:void 0,makerNote:!1,userComment:!1,multiSegment:!1,skip:[],pick:[],translateKeys:!0,translateValues:!0,reviveValues:!0,sanitize:!0,mergeOutput:!0,silentErrors:!0,chunked:!0,firstChunkSize:void 0,firstChunkSizeNode:512,firstChunkSizeBrowser:65536,chunkSize:65536,chunkLimit:5},kl=new Map,ia=class extends Ho{static useCached(Gr){let Wr=kl.get(Gr);return Wr!==void 0||(Wr=new this(Gr),kl.set(Gr,Wr)),Wr}constructor(Gr){super(),Gr===!0?this.setupFromTrue():Gr===void 0?this.setupFromUndefined():Array.isArray(Gr)?this.setupFromArray(Gr):typeof Gr=="object"?this.setupFromObject(Gr):Ei(`Invalid options argument ${Gr}`),this.firstChunkSize===void 0&&(this.firstChunkSize=so?this.firstChunkSizeBrowser:this.firstChunkSizeNode),this.mergeOutput&&(this.ifd1.enabled=!1),this.filterNestedSegmentTags(),this.traverseTiffDependencyTree(),this.checkLoadedPlugins()}setupFromUndefined(){let Gr;for(Gr of ua)this[Gr]=Ai[Gr];for(Gr of ha)this[Gr]=Ai[Gr];for(Gr of fa)this[Gr]=Ai[Gr];for(Gr of ca)this[Gr]=new ya(Gr,Ai[Gr],void 0,this)}setupFromTrue(){let Gr;for(Gr of ua)this[Gr]=Ai[Gr];for(Gr of ha)this[Gr]=Ai[Gr];for(Gr of fa)this[Gr]=!0;for(Gr of ca)this[Gr]=new ya(Gr,!0,void 0,this)}setupFromArray(Gr){let Wr;for(Wr of ua)this[Wr]=Ai[Wr];for(Wr of ha)this[Wr]=Ai[Wr];for(Wr of fa)this[Wr]=Ai[Wr];for(Wr of ca)this[Wr]=new ya(Wr,!1,void 0,this);this.setupGlobalFilters(Gr,void 0,di)}setupFromObject(Gr){let Wr;for(Wr of(di.ifd0=di.ifd0||di.image,di.ifd1=di.ifd1||di.thumbnail,Object.assign(this,Gr),ua))this[Wr]=xs(Gr[Wr],Ai[Wr]);for(Wr of ha)this[Wr]=xs(Gr[Wr],Ai[Wr]);for(Wr of fa)this[Wr]=xs(Gr[Wr],Ai[Wr]);for(Wr of ka)this[Wr]=new ya(Wr,Ai[Wr],Gr[Wr],this);for(Wr of di)this[Wr]=new ya(Wr,Ai[Wr],Gr[Wr],this.tiff);this.setupGlobalFilters(Gr.pick,Gr.skip,di,ca),Gr.tiff===!0?this.batchEnableWithBool(di,!0):Gr.tiff===!1?this.batchEnableWithUserValue(di,Gr):Array.isArray(Gr.tiff)?this.setupGlobalFilters(Gr.tiff,void 0,di):typeof Gr.tiff=="object"&&this.setupGlobalFilters(Gr.tiff.pick,Gr.tiff.skip,di)}batchEnableWithBool(Gr,Wr){for(let Kr of Gr)this[Kr].enabled=Wr}batchEnableWithUserValue(Gr,Wr){for(let Kr of Gr){let Yr=Wr[Kr];this[Kr].enabled=Yr!==!1&&Yr!==void 0}}setupGlobalFilters(Gr,Wr,Kr,Yr=Kr){if(Gr&&Gr.length){for(let tn of Yr)this[tn].enabled=!1;let Zr=Cl(Gr,Kr);for(let[tn,ln]of Zr)Go(this[tn].pick,ln),this[tn].enabled=!0}else if(Wr&&Wr.length){let Zr=Cl(Wr,Kr);for(let[tn,ln]of Zr)Go(this[tn].skip,ln)}}filterNestedSegmentTags(){let{ifd0:Gr,exif:Wr,xmp:Kr,iptc:Yr,icc:Zr}=this;this.makerNote?Wr.deps.add(37500):Wr.skip.add(37500),this.userComment?Wr.deps.add(37510):Wr.skip.add(37510),Kr.enabled||Gr.skip.add(700),Yr.enabled||Gr.skip.add(33723),Zr.enabled||Gr.skip.add(34675)}traverseTiffDependencyTree(){let{ifd0:Gr,exif:Wr,gps:Kr,interop:Yr}=this;Yr.needed&&(Wr.deps.add(40965),Gr.deps.add(40965)),Wr.needed&&Gr.deps.add(34665),Kr.needed&&Gr.deps.add(34853),this.tiff.enabled=di.some(Zr=>this[Zr].enabled===!0)||this.makerNote||this.userComment;for(let Zr of di)this[Zr].finalizeFilters()}get onlyTiff(){return!Ha.map(Gr=>this[Gr].enabled).some(Gr=>Gr===!0)&&this.tiff.enabled}checkLoadedPlugins(){for(let Gr of ka)this[Gr].enabled&&!bi.has(Gr)&&Is("segment parser",Gr)}};function Cl(Qr,Gr){let Wr,Kr,Yr,Zr,tn=[];for(Yr of Gr){for(Zr of(Wr=Si.get(Yr),Kr=[],Wr))(Qr.includes(Zr[0])||Qr.includes(Zr[1]))&&Kr.push(Zr[0]);Kr.length&&tn.push([Yr,Kr])}return tn}function xs(Qr,Gr){return Qr!==void 0?Qr:Gr!==void 0?Gr:void 0}function Go(Qr,Gr){for(let Wr of Gr)Qr.add(Wr)}Kn(ia,"default",Ai);var $i=class{constructor(Gr){Kn(this,"parsers",{}),Kn(this,"output",{}),Kn(this,"errors",[]),Kn(this,"pushToErrors",Wr=>this.errors.push(Wr)),this.options=ia.useCached(Gr)}async read(Gr){this.file=await Lu(Gr,this.options)}setup(){if(this.fileParser)return;let{file:Gr}=this,Wr=Gr.getUint16(0);for(let[Kr,Yr]of Yi)if(Yr.canHandle(Gr,Wr))return this.fileParser=new Yr(this.options,this.file,this.parsers),Gr[Kr]=!0;this.file.close&&this.file.close(),Ei("Unknown file format")}async parse(){let{output:Gr,errors:Wr}=this;return this.setup(),this.options.silentErrors?(await this.executeParsers().catch(this.pushToErrors),Wr.push(...this.fileParser.errors)):await this.executeParsers(),this.file.close&&this.file.close(),this.options.silentErrors&&Wr.length>0&&(Gr.errors=Wr),Bo(Gr)}async executeParsers(){let{output:Gr}=this;await this.fileParser.parse();let Wr=Object.values(this.parsers).map(async Kr=>{let Yr=await Kr.parse();Kr.assignToOutput(Gr,Yr)});this.options.silentErrors&&(Wr=Wr.map(Kr=>Kr.catch(this.pushToErrors))),await Promise.all(Wr)}async extractThumbnail(){this.setup();let{options:Gr,file:Wr}=this,Kr=bi.get("tiff",Gr);var Yr;if(Wr.tiff?Yr={start:0,type:"tiff"}:Wr.jpeg&&(Yr=await this.fileParser.getOrFindSegment("tiff")),Yr===void 0)return;let Zr=await this.fileParser.ensureSegmentChunk(Yr),tn=this.parsers.tiff=new Kr(Zr,Gr,Wr),ln=await tn.extractThumbnail();return Wr.close&&Wr.close(),ln}};async function Zo(Qr,Gr){let Wr=new $i(Gr);return await Wr.read(Qr),Wr.parse()}var Mu=Object.freeze({__proto__:null,parse:Zo,Exifr:$i,fileParsers:Yi,segmentParsers:bi,fileReaders:Ji,tagKeys:Si,tagValues:Wi,tagRevivers:da,createDictionary:pi,extendDictionary:wa,fetchUrlAsArrayBuffer:xa,readBlobAsArrayBuffer:Sa,chunkedProps:ua,otherSegments:Ha,segments:ka,tiffBlocks:di,segmentsAndBlocks:ca,tiffExtractables:fa,inheritables:Ga,allFormatters:ha,Options:ia}),Ua=class{constructor(Gr,Wr,Kr){Kn(this,"errors",[]),Kn(this,"ensureSegmentChunk",async Yr=>{let Zr=Yr.start,tn=Yr.size||65536;if(this.file.chunked)if(this.file.available(Zr,tn))Yr.chunk=this.file.subarray(Zr,tn);else try{Yr.chunk=await this.file.readChunk(Zr,tn)}catch(ln){Ei(`Couldn't read segment: ${JSON.stringify(Yr)}. ${ln.message}`)}else this.file.byteLength>Zr+tn?Yr.chunk=this.file.subarray(Zr,tn):Yr.size===void 0?Yr.chunk=this.file.subarray(Zr):Ei("Segment unreachable: "+JSON.stringify(Yr));return Yr.chunk}),this.extendOptions&&this.extendOptions(Gr),this.options=Gr,this.file=Wr,this.parsers=Kr}injectSegment(Gr,Wr){this.options[Gr].enabled&&this.createParser(Gr,Wr)}createParser(Gr,Wr){let Kr=new(bi.get(Gr))(Wr,this.options,this.file);return this.parsers[Gr]=Kr}createParsers(Gr){for(let Wr of Gr){let{type:Kr,chunk:Yr}=Wr,Zr=this.options[Kr];if(Zr&&Zr.enabled){let tn=this.parsers[Kr];tn&&tn.append||tn||this.createParser(Kr,Yr)}}}async readSegments(Gr){let Wr=Gr.map(this.ensureSegmentChunk);await Promise.all(Wr)}},qi=class{static findPosition(Gr,Wr){let Kr=Gr.getUint16(Wr+2)+2,Yr=typeof this.headerLength=="function"?this.headerLength(Gr,Wr,Kr):this.headerLength,Zr=Wr+Yr,tn=Kr-Yr;return{offset:Wr,length:Kr,headerLength:Yr,start:Zr,size:tn,end:Zr+tn}}static parse(Gr,Wr={}){return new this(Gr,new ia({[this.type]:Wr}),Gr).parse()}normalizeInput(Gr){return Gr instanceof Ki?Gr:new Ki(Gr)}constructor(Gr,Wr={},Kr){Kn(this,"errors",[]),Kn(this,"raw",new Map),Kn(this,"handleError",Yr=>{if(!this.options.silentErrors)throw Yr;this.errors.push(Yr.message)}),this.chunk=this.normalizeInput(Gr),this.file=Kr,this.type=this.constructor.type,this.globalOptions=this.options=Wr,this.localOptions=Wr[this.type],this.canTranslate=this.localOptions&&this.localOptions.translate}translate(){this.canTranslate&&(this.translated=this.translateBlock(this.raw,this.type))}get output(){return this.translated?this.translated:this.raw?Object.fromEntries(this.raw):void 0}translateBlock(Gr,Wr){let Kr=da.get(Wr),Yr=Wi.get(Wr),Zr=Si.get(Wr),tn=this.options[Wr],ln=tn.reviveValues&&!!Kr,dn=tn.translateValues&&!!Yr,yn=tn.translateKeys&&!!Zr,wn={};for(let[kn,An]of Gr)ln&&Kr.has(kn)?An=Kr.get(kn)(An):dn&&Yr.has(kn)&&(An=this.translateValue(An,Yr.get(kn))),yn&&Zr.has(kn)&&(kn=Zr.get(kn)||kn),wn[kn]=An;return wn}translateValue(Gr,Wr){return Wr[Gr]||Wr.DEFAULT||Gr}assignToOutput(Gr,Wr){this.assignObjectToOutput(Gr,this.constructor.type,Wr)}assignObjectToOutput(Gr,Wr,Kr){if(this.globalOptions.mergeOutput)return Object.assign(Gr,Kr);Gr[Wr]?Object.assign(Gr[Wr],Kr):Gr[Wr]=Kr}};Kn(qi,"headerLength",4),Kn(qi,"type",void 0),Kn(qi,"multiSegment",!1),Kn(qi,"canHandle",()=>!1);function Fu(Qr){return Qr===192||Qr===194||Qr===196||Qr===219||Qr===221||Qr===218||Qr===254}function qu(Qr){return Qr>=224&&Qr<=239}function Uu(Qr,Gr,Wr){for(let[Kr,Yr]of bi)if(Yr.canHandle(Qr,Gr,Wr))return Kr}var _o=class extends Ua{constructor(...Gr){super(...Gr),Kn(this,"appSegments",[]),Kn(this,"jpegSegments",[]),Kn(this,"unknownSegments",[])}static canHandle(Gr,Wr){return Wr===65496}async parse(){await this.findAppSegments(),await this.readSegments(this.appSegments),this.mergeMultiSegments(),this.createParsers(this.mergedAppSegments||this.appSegments)}setupSegmentFinderArgs(Gr){Gr===!0?(this.findAll=!0,this.wanted=new Set(bi.keyList())):(Gr=Gr===void 0?bi.keyList().filter(Wr=>this.options[Wr].enabled):Gr.filter(Wr=>this.options[Wr].enabled&&bi.has(Wr)),this.findAll=!1,this.remaining=new Set(Gr),this.wanted=new Set(Gr)),this.unfinishedMultiSegment=!1}async findAppSegments(Gr=0,Wr){this.setupSegmentFinderArgs(Wr);let{file:Kr,findAll:Yr,wanted:Zr,remaining:tn}=this;if(!Yr&&this.file.chunked&&(Yr=Array.from(Zr).some(ln=>{let dn=bi.get(ln),yn=this.options[ln];return dn.multiSegment&&yn.multiSegment}),Yr&&await this.file.readWhole()),Gr=this.findAppSegmentsInRange(Gr,Kr.byteLength),!this.options.onlyTiff&&Kr.chunked){let ln=!1;for(;tn.size>0&&!ln&&(Kr.canReadNextChunk||this.unfinishedMultiSegment);){let{nextChunkOffset:dn}=Kr,yn=this.appSegments.some(wn=>!this.file.available(wn.offset||wn.start,wn.length||wn.size));if(ln=Gr>dn&&!yn?!await Kr.readNextChunk(Gr):!await Kr.readNextChunk(dn),(Gr=this.findAppSegmentsInRange(Gr,Kr.byteLength))===void 0)return}}}findAppSegmentsInRange(Gr,Wr){Wr-=2;let Kr,Yr,Zr,tn,ln,dn,{file:yn,findAll:wn,wanted:kn,remaining:An,options:Gn}=this;for(;Gr<Wr;Gr++)if(yn.getUint8(Gr)===255){if(Kr=yn.getUint8(Gr+1),qu(Kr)){if(Yr=yn.getUint16(Gr+2),Zr=Uu(yn,Gr,Yr),Zr&&kn.has(Zr)&&(tn=bi.get(Zr),ln=tn.findPosition(yn,Gr),dn=Gn[Zr],ln.type=Zr,this.appSegments.push(ln),!wn&&(tn.multiSegment&&dn.multiSegment?(this.unfinishedMultiSegment=ln.chunkNumber<ln.chunkCount,this.unfinishedMultiSegment||An.delete(Zr)):An.delete(Zr),An.size===0)))break;Gn.recordUnknownSegments&&(ln=qi.findPosition(yn,Gr),ln.marker=Kr,this.unknownSegments.push(ln)),Gr+=Yr+1}else if(Fu(Kr)){if(Yr=yn.getUint16(Gr+2),Kr===218&&Gn.stopAfterSos!==!1)return;Gn.recordJpegSegments&&this.jpegSegments.push({offset:Gr,length:Yr,marker:Kr}),Gr+=Yr+1}}return Gr}mergeMultiSegments(){if(!this.appSegments.some(Wr=>Wr.multiSegment))return;let Gr=function(Wr,Kr){let Yr,Zr,tn,ln=new Map;for(let dn=0;dn<Wr.length;dn++)Yr=Wr[dn],Zr=Yr[Kr],ln.has(Zr)?tn=ln.get(Zr):ln.set(Zr,tn=[]),tn.push(Yr);return Array.from(ln)}(this.appSegments,"type");this.mergedAppSegments=Gr.map(([Wr,Kr])=>{let Yr=bi.get(Wr,this.options);return Yr.handleMultiSegments?{type:Wr,chunk:Yr.handleMultiSegments(Kr)}:Kr[0]})}getSegment(Gr){return this.appSegments.find(Wr=>Wr.type===Gr)}async getOrFindSegment(Gr){let Wr=this.getSegment(Gr);return Wr===void 0&&(await this.findAppSegments(0,[Gr]),Wr=this.getSegment(Gr)),Wr}};Kn(_o,"type","jpeg"),Yi.set("jpeg",_o);var Bu=[void 0,1,1,2,4,8,1,1,2,4,8,4,8,4],Ps=class extends qi{parseHeader(){var Gr=this.chunk.getUint16();Gr===18761?this.le=!0:Gr===19789&&(this.le=!1),this.chunk.le=this.le,this.headerParsed=!0}parseTags(Gr,Wr,Kr=new Map){let{pick:Yr,skip:Zr}=this.options[Wr];Yr=new Set(Yr);let tn=Yr.size>0,ln=Zr.size===0,dn=this.chunk.getUint16(Gr);Gr+=2;for(let yn=0;yn<dn;yn++){let wn=this.chunk.getUint16(Gr);if(tn){if(Yr.has(wn)&&(Kr.set(wn,this.parseTag(Gr,wn,Wr)),Yr.delete(wn),Yr.size===0))break}else!ln&&Zr.has(wn)||Kr.set(wn,this.parseTag(Gr,wn,Wr));Gr+=12}return Kr}parseTag(Gr,Wr,Kr){let{chunk:Yr}=this,Zr=Yr.getUint16(Gr+2),tn=Yr.getUint32(Gr+4),ln=Bu[Zr];if(ln*tn<=4?Gr+=8:Gr=Yr.getUint32(Gr+8),(Zr<1||Zr>13)&&Ei(`Invalid TIFF value type. block: ${Kr.toUpperCase()}, tag: ${Wr.toString(16)}, type: ${Zr}, offset ${Gr}`),Gr>Yr.byteLength&&Ei(`Invalid TIFF value offset. block: ${Kr.toUpperCase()}, tag: ${Wr.toString(16)}, type: ${Zr}, offset ${Gr} is outside of chunk size ${Yr.byteLength}`),Zr===1)return Yr.getUint8Array(Gr,tn);if(Zr===2)return ba(Yr.getString(Gr,tn));if(Zr===7)return Yr.getUint8Array(Gr,tn);if(tn===1)return this.parseTagValue(Zr,Gr);{let dn=new(function(wn){switch(wn){case 1:return Uint8Array;case 3:return Uint16Array;case 4:return Uint32Array;case 5:return Array;case 6:return Int8Array;case 8:return Int16Array;case 9:return Int32Array;case 10:return Array;case 11:return Float32Array;case 12:return Float64Array;default:return Array}}(Zr))(tn),yn=ln;for(let wn=0;wn<tn;wn++)dn[wn]=this.parseTagValue(Zr,Gr),Gr+=yn;return dn}}parseTagValue(Gr,Wr){let{chunk:Kr}=this;switch(Gr){case 1:return Kr.getUint8(Wr);case 3:return Kr.getUint16(Wr);case 4:return Kr.getUint32(Wr);case 5:return Kr.getUint32(Wr)/Kr.getUint32(Wr+4);case 6:return Kr.getInt8(Wr);case 8:return Kr.getInt16(Wr);case 9:return Kr.getInt32(Wr);case 10:return Kr.getInt32(Wr)/Kr.getInt32(Wr+4);case 11:return Kr.getFloat(Wr);case 12:return Kr.getDouble(Wr);case 13:return Kr.getUint32(Wr);default:Ei(`Invalid tiff type ${Gr}`)}}},no=class extends Ps{static canHandle(Gr,Wr){return Gr.getUint8(Wr+1)===225&&Gr.getUint32(Wr+4)===1165519206&&Gr.getUint16(Wr+8)===0}async parse(){this.parseHeader();let{options:Gr}=this;return Gr.ifd0.enabled&&await this.parseIfd0Block(),Gr.exif.enabled&&await this.safeParse("parseExifBlock"),Gr.gps.enabled&&await this.safeParse("parseGpsBlock"),Gr.interop.enabled&&await this.safeParse("parseInteropBlock"),Gr.ifd1.enabled&&await this.safeParse("parseThumbnailBlock"),this.createOutput()}safeParse(Gr){let Wr=this[Gr]();return Wr.catch!==void 0&&(Wr=Wr.catch(this.handleError)),Wr}findIfd0Offset(){this.ifd0Offset===void 0&&(this.ifd0Offset=this.chunk.getUint32(4))}findIfd1Offset(){if(this.ifd1Offset===void 0){this.findIfd0Offset();let Gr=this.chunk.getUint16(this.ifd0Offset),Wr=this.ifd0Offset+2+12*Gr;this.ifd1Offset=this.chunk.getUint32(Wr)}}parseBlock(Gr,Wr){let Kr=new Map;return this[Wr]=Kr,this.parseTags(Gr,Wr,Kr),Kr}async parseIfd0Block(){if(this.ifd0)return;let{file:Gr}=this;this.findIfd0Offset(),this.ifd0Offset<8&&Ei("Malformed EXIF data"),!Gr.chunked&&this.ifd0Offset>Gr.byteLength&&Ei(`IFD0 offset points to outside of file.
15
+
this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${Gr.byteLength}`),Gr.tiff&&await Gr.ensureChunk(this.ifd0Offset,ks(this.options));let Wr=this.parseBlock(this.ifd0Offset,"ifd0");return Wr.size!==0?(this.exifOffset=Wr.get(34665),this.interopOffset=Wr.get(40965),this.gpsOffset=Wr.get(34853),this.xmp=Wr.get(700),this.iptc=Wr.get(33723),this.icc=Wr.get(34675),this.options.sanitize&&(Wr.delete(34665),Wr.delete(40965),Wr.delete(34853),Wr.delete(700),Wr.delete(33723),Wr.delete(34675)),Wr):void 0}async parseExifBlock(){if(this.exif||(this.ifd0||await this.parseIfd0Block(),this.exifOffset===void 0))return;this.file.tiff&&await this.file.ensureChunk(this.exifOffset,ks(this.options));let Gr=this.parseBlock(this.exifOffset,"exif");return this.interopOffset||(this.interopOffset=Gr.get(40965)),this.makerNote=Gr.get(37500),this.userComment=Gr.get(37510),this.options.sanitize&&(Gr.delete(40965),Gr.delete(37500),Gr.delete(37510)),this.unpack(Gr,41728),this.unpack(Gr,41729),Gr}unpack(Gr,Wr){let Kr=Gr.get(Wr);Kr&&Kr.length===1&&Gr.set(Wr,Kr[0])}async parseGpsBlock(){if(this.gps||(this.ifd0||await this.parseIfd0Block(),this.gpsOffset===void 0))return;let Gr=this.parseBlock(this.gpsOffset,"gps");return Gr&&Gr.has(2)&&Gr.has(4)&&(Gr.set("latitude",Il(...Gr.get(2),Gr.get(1))),Gr.set("longitude",Il(...Gr.get(4),Gr.get(3)))),Gr}async parseInteropBlock(){if(!this.interop&&(this.ifd0||await this.parseIfd0Block(),this.interopOffset!==void 0||this.exif||await this.parseExifBlock(),this.interopOffset!==void 0))return this.parseBlock(this.interopOffset,"interop")}async parseThumbnailBlock(Gr=!1){if(!this.ifd1&&!this.ifd1Parsed&&(!this.options.mergeOutput||Gr))return this.findIfd1Offset(),this.ifd1Offset>0&&(this.parseBlock(this.ifd1Offset,"ifd1"),this.ifd1Parsed=!0),this.ifd1}async extractThumbnail(){if(this.headerParsed||this.parseHeader(),this.ifd1Parsed||await this.parseThumbnailBlock(!0),this.ifd1===void 0)return;let Gr=this.ifd1.get(513),Wr=this.ifd1.get(514);return this.chunk.getUint8Array(Gr,Wr)}get image(){return this.ifd0}get thumbnail(){return this.ifd1}createOutput(){let Gr,Wr,Kr,Yr={};for(Wr of di)if(Gr=this[Wr],!Fl(Gr))if(Kr=this.canTranslate?this.translateBlock(Gr,Wr):Object.fromEntries(Gr),this.options.mergeOutput){if(Wr==="ifd1")continue;Object.assign(Yr,Kr)}else Yr[Wr]=Kr;return this.makerNote&&(Yr.makerNote=this.makerNote),this.userComment&&(Yr.userComment=this.userComment),Yr}assignToOutput(Gr,Wr){if(this.globalOptions.mergeOutput)Object.assign(Gr,Wr);else for(let[Kr,Yr]of Object.entries(Wr))this.assignObjectToOutput(Gr,Kr,Yr)}};function Il(Qr,Gr,Wr,Kr){var Yr=Qr+Gr/60+Wr/3600;return Kr!=="S"&&Kr!=="W"||(Yr*=-1),Yr}Kn(no,"type","tiff"),Kn(no,"headerLength",10),bi.set("tiff",no);var Hu=Object.freeze({__proto__:null,default:Mu,Exifr:$i,fileParsers:Yi,segmentParsers:bi,fileReaders:Ji,tagKeys:Si,tagValues:Wi,tagRevivers:da,createDictionary:pi,extendDictionary:wa,fetchUrlAsArrayBuffer:xa,readBlobAsArrayBuffer:Sa,chunkedProps:ua,otherSegments:Ha,segments:ka,tiffBlocks:di,segmentsAndBlocks:ca,tiffExtractables:fa,inheritables:Ga,allFormatters:ha,Options:ia,parse:Zo}),qs={ifd0:!1,ifd1:!1,exif:!1,gps:!1,interop:!1,sanitize:!1,reviveValues:!0,translateKeys:!1,translateValues:!1,mergeOutput:!1},Us=Object.assign({},qs,{firstChunkSize:4e4,gps:[1,2,3,4]});async function Ul(Qr){let Gr=new $i(Us);await Gr.read(Qr);let Wr=await Gr.parse();if(Wr&&Wr.gps){let{latitude:Kr,longitude:Yr}=Wr.gps;return{latitude:Kr,longitude:Yr}}}var Bs=Object.assign({},qs,{tiff:!1,ifd1:!0,mergeOutput:!1});async function Bl(Qr){let Gr=new $i(Bs);await Gr.read(Qr);let Wr=await Gr.extractThumbnail();return Wr&&Jo?$o.from(Wr):Wr}async function Hl(Qr){let Gr=await this.thumbnail(Qr);if(Gr!==void 0){let Wr=new Blob([Gr]);return URL.createObjectURL(Wr)}}var Hs=Object.assign({},qs,{firstChunkSize:4e4,ifd0:[274]});async function Gs(Qr){let Gr=new $i(Hs);await Gr.read(Qr);let Wr=await Gr.parse();if(Wr&&Wr.ifd0)return Wr.ifd0[274]}var _s=Object.freeze({1:{dimensionSwapped:!1,scaleX:1,scaleY:1,deg:0,rad:0},2:{dimensionSwapped:!1,scaleX:-1,scaleY:1,deg:0,rad:0},3:{dimensionSwapped:!1,scaleX:1,scaleY:1,deg:180,rad:180*Math.PI/180},4:{dimensionSwapped:!1,scaleX:-1,scaleY:1,deg:180,rad:180*Math.PI/180},5:{dimensionSwapped:!0,scaleX:1,scaleY:-1,deg:90,rad:90*Math.PI/180},6:{dimensionSwapped:!0,scaleX:1,scaleY:1,deg:90,rad:90*Math.PI/180},7:{dimensionSwapped:!0,scaleX:1,scaleY:-1,deg:270,rad:270*Math.PI/180},8:{dimensionSwapped:!0,scaleX:1,scaleY:1,deg:270,rad:270*Math.PI/180}}),Ea=!0,Ta=!0;if(typeof navigator=="object"){let Qr=navigator.userAgent;if(Qr.includes("iPad")||Qr.includes("iPhone")){let Gr=Qr.match(/OS (\d+)_(\d+)/);if(Gr){let[,Wr,Kr]=Gr;Ea=Number(Wr)+.1*Number(Kr)<13.4,Ta=!1}}else if(Qr.includes("OS X 10")){let[,Gr]=Qr.match(/OS X 10[_.](\d+)/);Ea=Ta=Number(Gr)<15}if(Qr.includes("Chrome/")){let[,Gr]=Qr.match(/Chrome\/(\d+)/);Ea=Ta=Number(Gr)<81}else if(Qr.includes("Firefox/")){let[,Gr]=Qr.match(/Firefox\/(\d+)/);Ea=Ta=Number(Gr)<77}}async function Gl(Qr){let Gr=await Gs(Qr);return Object.assign({canvas:Ea,css:Ta},_s[Gr])}var Rs=class extends Ki{constructor(...Gr){super(...Gr),Kn(this,"ranges",new Ns),this.byteLength!==0&&this.ranges.add(0,this.byteLength)}_tryExtend(Gr,Wr,Kr){if(Gr===0&&this.byteLength===0&&Kr){let Yr=new DataView(Kr.buffer||Kr,Kr.byteOffset,Kr.byteLength);this._swapDataView(Yr)}else{let Yr=Gr+Wr;if(Yr>this.byteLength){let{dataView:Zr}=this._extend(Yr);this._swapDataView(Zr)}}}_extend(Gr){let Wr;Wr=Jo?$o.allocUnsafe(Gr):new Uint8Array(Gr);let Kr=new DataView(Wr.buffer,Wr.byteOffset,Wr.byteLength);return Wr.set(new Uint8Array(this.buffer,this.byteOffset,this.byteLength),0),{uintView:Wr,dataView:Kr}}subarray(Gr,Wr,Kr=!1){return Wr=Wr||this._lengthToEnd(Gr),Kr&&this._tryExtend(Gr,Wr),this.ranges.add(Gr,Wr),super.subarray(Gr,Wr)}set(Gr,Wr,Kr=!1){Kr&&this._tryExtend(Wr,Gr.byteLength,Gr);let Yr=super.set(Gr,Wr);return this.ranges.add(Wr,Yr.byteLength),Yr}async ensureChunk(Gr,Wr){this.chunked&&(this.ranges.available(Gr,Wr)||await this.readChunk(Gr,Wr))}available(Gr,Wr){return this.ranges.available(Gr,Wr)}},Ns=class{constructor(){Kn(this,"list",[])}get length(){return this.list.length}add(Gr,Wr,Kr=0){let Yr=Gr+Wr,Zr=this.list.filter(tn=>Ol(Gr,tn.offset,Yr)||Ol(Gr,tn.end,Yr));if(Zr.length>0){Gr=Math.min(Gr,...Zr.map(ln=>ln.offset)),Yr=Math.max(Yr,...Zr.map(ln=>ln.end)),Wr=Yr-Gr;let tn=Zr.shift();tn.offset=Gr,tn.length=Wr,tn.end=Yr,this.list=this.list.filter(ln=>!Zr.includes(ln))}else this.list.push({offset:Gr,length:Wr,end:Yr})}available(Gr,Wr){let Kr=Gr+Wr;return this.list.some(Yr=>Yr.offset<=Gr&&Kr<=Yr.end)}};function Ol(Qr,Gr,Wr){return Qr<=Gr&&Gr<=Wr}var Ba=class extends Rs{constructor(Gr,Wr){super(0),Kn(this,"chunksRead",0),this.input=Gr,this.options=Wr}async readWhole(){this.chunked=!1,await this.readChunk(this.nextChunkOffset)}async readChunked(){this.chunked=!0,await this.readChunk(0,this.options.firstChunkSize)}async readNextChunk(Gr=this.nextChunkOffset){if(this.fullyRead)return this.chunksRead++,!1;let Wr=this.options.chunkSize,Kr=await this.readChunk(Gr,Wr);return!!Kr&&Kr.byteLength===Wr}async readChunk(Gr,Wr){if(this.chunksRead++,(Wr=this.safeWrapAddress(Gr,Wr))!==0)return this._readChunk(Gr,Wr)}safeWrapAddress(Gr,Wr){return this.size!==void 0&&Gr+Wr>this.size?Math.max(0,this.size-Gr):Wr}get nextChunkOffset(){if(this.ranges.list.length!==0)return this.ranges.list[0].length}get canReadNextChunk(){return this.chunksRead<this.options.chunkLimit}get fullyRead(){return this.size!==void 0&&this.nextChunkOffset===this.size}read(){return this.options.chunked?this.readChunked():this.readWhole()}close(){}};Ji.set("blob",class extends Ba{async readWhole(){this.chunked=!1;let Qr=await Sa(this.input);this._swapArrayBuffer(Qr)}readChunked(){return this.chunked=!0,this.size=this.input.size,super.readChunked()}async _readChunk(Qr,Gr){let Wr=Gr?Qr+Gr:void 0,Kr=this.input.slice(Qr,Wr),Yr=await Sa(Kr);return this.set(Yr,Qr,!0)}});var Gu=Object.freeze({__proto__:null,default:Hu,Exifr:$i,fileParsers:Yi,segmentParsers:bi,fileReaders:Ji,tagKeys:Si,tagValues:Wi,tagRevivers:da,createDictionary:pi,extendDictionary:wa,fetchUrlAsArrayBuffer:xa,readBlobAsArrayBuffer:Sa,chunkedProps:ua,otherSegments:Ha,segments:ka,tiffBlocks:di,segmentsAndBlocks:ca,tiffExtractables:fa,inheritables:Ga,allFormatters:ha,Options:ia,parse:Zo,gpsOnlyOptions:Us,gps:Ul,thumbnailOnlyOptions:Bs,thumbnail:Bl,thumbnailUrl:Hl,orientationOnlyOptions:Hs,orientation:Gs,rotations:_s,get rotateCanvas(){return Ea},get rotateCss(){return Ta},rotation:Gl});Ji.set("url",class extends Ba{async readWhole(){this.chunked=!1;let Qr=await xa(this.input);Qr instanceof ArrayBuffer?this._swapArrayBuffer(Qr):Qr instanceof Uint8Array&&this._swapBuffer(Qr)}async _readChunk(Qr,Gr){let Wr=Gr?Qr+Gr-1:void 0,Kr=this.options.httpHeaders||{};(Qr||Wr)&&(Kr.range=`bytes=${[Qr,Wr].join("-")}`);let Yr=await Fs(this.input,{headers:Kr}),Zr=await Yr.arrayBuffer(),tn=Zr.byteLength;if(Yr.status!==416)return tn!==Gr&&(this.size=Qr+tn),this.set(Zr,Qr,!0)}});Ki.prototype.getUint64=function(Qr){let Gr=this.getUint32(Qr),Wr=this.getUint32(Qr+4);return Gr<1048575?Gr<<32|Wr:typeof Mo!==void 0?(console.warn("Using BigInt because of type 64uint but JS can only handle 53b numbers."),Mo(Gr)<<Mo(32)|Mo(Wr)):void Ei("Trying to read 64b value but JS can only handle 53b numbers.")};var Ls=class extends Ua{parseBoxes(Gr=0){let Wr=[];for(;Gr<this.file.byteLength-4;){let Kr=this.parseBoxHead(Gr);if(Wr.push(Kr),Kr.length===0)break;Gr+=Kr.length}return Wr}parseSubBoxes(Gr){Gr.boxes=this.parseBoxes(Gr.start)}findBox(Gr,Wr){return Gr.boxes===void 0&&this.parseSubBoxes(Gr),Gr.boxes.find(Kr=>Kr.kind===Wr)}parseBoxHead(Gr){let Wr=this.file.getUint32(Gr),Kr=this.file.getString(Gr+4,4),Yr=Gr+8;return Wr===1&&(Wr=this.file.getUint64(Gr+8),Yr+=8),{offset:Gr,length:Wr,kind:Kr,start:Yr}}parseBoxFullHead(Gr){if(Gr.version!==void 0)return;let Wr=this.file.getUint32(Gr.start);Gr.version=Wr>>24,Gr.start+=4}},jo=class extends Ls{static canHandle(Gr,Wr){if(Wr!==0)return!1;let Kr=Gr.getUint16(2);if(Kr>50)return!1;let Yr=16,Zr=[];for(;Yr<Kr;)Zr.push(Gr.getString(Yr,4)),Yr+=4;return Zr.includes(this.type)}async parse(){let Gr=this.file.getUint32(0),Wr=this.parseBoxHead(Gr);for(;Wr.kind!=="meta";)Gr+=Wr.length,await this.file.ensureChunk(Gr,16),Wr=this.parseBoxHead(Gr);await this.file.ensureChunk(Wr.offset,Wr.length),this.parseBoxFullHead(Wr),this.parseSubBoxes(Wr),this.options.icc.enabled&&await this.findIcc(Wr),this.options.tiff.enabled&&await this.findExif(Wr)}async registerSegment(Gr,Wr,Kr){await this.file.ensureChunk(Wr,Kr);let Yr=this.file.subarray(Wr,Kr);this.createParser(Gr,Yr)}async findIcc(Gr){let Wr=this.findBox(Gr,"iprp");if(Wr===void 0)return;let Kr=this.findBox(Wr,"ipco");if(Kr===void 0)return;let Yr=this.findBox(Kr,"colr");Yr!==void 0&&await this.registerSegment("icc",Yr.offset+12,Yr.length)}async findExif(Gr){let Wr=this.findBox(Gr,"iinf");if(Wr===void 0)return;let Kr=this.findBox(Gr,"iloc");if(Kr===void 0)return;let Yr=this.findExifLocIdInIinf(Wr),Zr=this.findExtentInIloc(Kr,Yr);if(Zr===void 0)return;let[tn,ln]=Zr;await this.file.ensureChunk(tn,ln);let dn=4+this.file.getUint32(tn);tn+=dn,ln-=dn,await this.registerSegment("tiff",tn,ln)}findExifLocIdInIinf(Gr){this.parseBoxFullHead(Gr);let Wr,Kr,Yr,Zr,tn=Gr.start,ln=this.file.getUint16(tn);for(tn+=2;ln--;){if(Wr=this.parseBoxHead(tn),this.parseBoxFullHead(Wr),Kr=Wr.start,Wr.version>=2&&(Yr=Wr.version===3?4:2,Zr=this.file.getString(Kr+Yr+2,4),Zr==="Exif"))return this.file.getUintBytes(Kr,Yr);tn+=Wr.length}}get8bits(Gr){let Wr=this.file.getUint8(Gr);return[Wr>>4,15&Wr]}findExtentInIloc(Gr,Wr){this.parseBoxFullHead(Gr);let Kr=Gr.start,[Yr,Zr]=this.get8bits(Kr++),[tn,ln]=this.get8bits(Kr++),dn=Gr.version===2?4:2,yn=Gr.version===1||Gr.version===2?2:0,wn=ln+Yr+Zr,kn=Gr.version===2?4:2,An=this.file.getUintBytes(Kr,kn);for(Kr+=kn;An--;){let Gn=this.file.getUintBytes(Kr,dn);Kr+=dn+yn+2+tn;let jn=this.file.getUint16(Kr);if(Kr+=2,Gn===Wr)return jn>1&&console.warn(`ILOC box has more than one extent but we're only processing one
16
+
Please create an issue at https://github.com/MikeKovarik/exifr with this file`),[this.file.getUintBytes(Kr+ln,Yr),this.file.getUintBytes(Kr+ln+Yr,Zr)];Kr+=jn*wn}}},zo=class extends jo{};Kn(zo,"type","heic");var Vo=class extends jo{};Kn(Vo,"type","avif"),Yi.set("heic",zo),Yi.set("avif",Vo),pi(Si,["ifd0","ifd1"],[[256,"ImageWidth"],[257,"ImageHeight"],[258,"BitsPerSample"],[259,"Compression"],[262,"PhotometricInterpretation"],[270,"ImageDescription"],[271,"Make"],[272,"Model"],[273,"StripOffsets"],[274,"Orientation"],[277,"SamplesPerPixel"],[278,"RowsPerStrip"],[279,"StripByteCounts"],[282,"XResolution"],[283,"YResolution"],[284,"PlanarConfiguration"],[296,"ResolutionUnit"],[301,"TransferFunction"],[305,"Software"],[306,"ModifyDate"],[315,"Artist"],[316,"HostComputer"],[317,"Predictor"],[318,"WhitePoint"],[319,"PrimaryChromaticities"],[513,"ThumbnailOffset"],[514,"ThumbnailLength"],[529,"YCbCrCoefficients"],[530,"YCbCrSubSampling"],[531,"YCbCrPositioning"],[532,"ReferenceBlackWhite"],[700,"ApplicationNotes"],[33432,"Copyright"],[33723,"IPTC"],[34665,"ExifIFD"],[34675,"ICC"],[34853,"GpsIFD"],[330,"SubIFD"],[40965,"InteropIFD"],[40091,"XPTitle"],[40092,"XPComment"],[40093,"XPAuthor"],[40094,"XPKeywords"],[40095,"XPSubject"]]),pi(Si,"exif",[[33434,"ExposureTime"],[33437,"FNumber"],[34850,"ExposureProgram"],[34852,"SpectralSensitivity"],[34855,"ISO"],[34858,"TimeZoneOffset"],[34859,"SelfTimerMode"],[34864,"SensitivityType"],[34865,"StandardOutputSensitivity"],[34866,"RecommendedExposureIndex"],[34867,"ISOSpeed"],[34868,"ISOSpeedLatitudeyyy"],[34869,"ISOSpeedLatitudezzz"],[36864,"ExifVersion"],[36867,"DateTimeOriginal"],[36868,"CreateDate"],[36873,"GooglePlusUploadCode"],[36880,"OffsetTime"],[36881,"OffsetTimeOriginal"],[36882,"OffsetTimeDigitized"],[37121,"ComponentsConfiguration"],[37122,"CompressedBitsPerPixel"],[37377,"ShutterSpeedValue"],[37378,"ApertureValue"],[37379,"BrightnessValue"],[37380,"ExposureCompensation"],[37381,"MaxApertureValue"],[37382,"SubjectDistance"],[37383,"MeteringMode"],[37384,"LightSource"],[37385,"Flash"],[37386,"FocalLength"],[37393,"ImageNumber"],[37394,"SecurityClassification"],[37395,"ImageHistory"],[37396,"SubjectArea"],[37500,"MakerNote"],[37510,"UserComment"],[37520,"SubSecTime"],[37521,"SubSecTimeOriginal"],[37522,"SubSecTimeDigitized"],[37888,"AmbientTemperature"],[37889,"Humidity"],[37890,"Pressure"],[37891,"WaterDepth"],[37892,"Acceleration"],[37893,"CameraElevationAngle"],[40960,"FlashpixVersion"],[40961,"ColorSpace"],[40962,"ExifImageWidth"],[40963,"ExifImageHeight"],[40964,"RelatedSoundFile"],[41483,"FlashEnergy"],[41486,"FocalPlaneXResolution"],[41487,"FocalPlaneYResolution"],[41488,"FocalPlaneResolutionUnit"],[41492,"SubjectLocation"],[41493,"ExposureIndex"],[41495,"SensingMethod"],[41728,"FileSource"],[41729,"SceneType"],[41730,"CFAPattern"],[41985,"CustomRendered"],[41986,"ExposureMode"],[41987,"WhiteBalance"],[41988,"DigitalZoomRatio"],[41989,"FocalLengthIn35mmFormat"],[41990,"SceneCaptureType"],[41991,"GainControl"],[41992,"Contrast"],[41993,"Saturation"],[41994,"Sharpness"],[41996,"SubjectDistanceRange"],[42016,"ImageUniqueID"],[42032,"OwnerName"],[42033,"SerialNumber"],[42034,"LensInfo"],[42035,"LensMake"],[42036,"LensModel"],[42037,"LensSerialNumber"],[42080,"CompositeImage"],[42081,"CompositeImageCount"],[42082,"CompositeImageExposureTimes"],[42240,"Gamma"],[59932,"Padding"],[59933,"OffsetSchema"],[65e3,"OwnerName"],[65001,"SerialNumber"],[65002,"Lens"],[65100,"RawFile"],[65101,"Converter"],[65102,"WhiteBalance"],[65105,"Exposure"],[65106,"Shadows"],[65107,"Brightness"],[65108,"Contrast"],[65109,"Saturation"],[65110,"Sharpness"],[65111,"Smoothness"],[65112,"MoireFilter"],[40965,"InteropIFD"]]),pi(Si,"gps",[[0,"GPSVersionID"],[1,"GPSLatitudeRef"],[2,"GPSLatitude"],[3,"GPSLongitudeRef"],[4,"GPSLongitude"],[5,"GPSAltitudeRef"],[6,"GPSAltitude"],[7,"GPSTimeStamp"],[8,"GPSSatellites"],[9,"GPSStatus"],[10,"GPSMeasureMode"],[11,"GPSDOP"],[12,"GPSSpeedRef"],[13,"GPSSpeed"],[14,"GPSTrackRef"],[15,"GPSTrack"],[16,"GPSImgDirectionRef"],[17,"GPSImgDirection"],[18,"GPSMapDatum"],[19,"GPSDestLatitudeRef"],[20,"GPSDestLatitude"],[21,"GPSDestLongitudeRef"],[22,"GPSDestLongitude"],[23,"GPSDestBearingRef"],[24,"GPSDestBearing"],[25,"GPSDestDistanceRef"],[26,"GPSDestDistance"],[27,"GPSProcessingMethod"],[28,"GPSAreaInformation"],[29,"GPSDateStamp"],[30,"GPSDifferential"],[31,"GPSHPositioningError"]]),pi(Wi,["ifd0","ifd1"],[[274,{1:"Horizontal (normal)",2:"Mirror horizontal",3:"Rotate 180",4:"Mirror vertical",5:"Mirror horizontal and rotate 270 CW",6:"Rotate 90 CW",7:"Mirror horizontal and rotate 90 CW",8:"Rotate 270 CW"}],[296,{1:"None",2:"inches",3:"cm"}]]);var io=pi(Wi,"exif",[[34850,{0:"Not defined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"}],[37121,{0:"-",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"}],[37383,{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"}],[37384,{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"}],[37385,{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"}],[41495,{1:"Not defined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"}],[41728,{1:"Film Scanner",2:"Reflection Print Scanner",3:"Digital Camera"}],[41729,{1:"Directly photographed"}],[41985,{0:"Normal",1:"Custom",2:"HDR (no original saved)",3:"HDR (original saved)",4:"Original (for HDR)",6:"Panorama",7:"Portrait HDR",8:"Portrait"}],[41986,{0:"Auto",1:"Manual",2:"Auto bracket"}],[41987,{0:"Auto",1:"Manual"}],[41990,{0:"Standard",1:"Landscape",2:"Portrait",3:"Night",4:"Other"}],[41991,{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"}],[41996,{0:"Unknown",1:"Macro",2:"Close",3:"Distant"}],[42080,{0:"Unknown",1:"Not a Composite Image",2:"General Composite Image",3:"Composite Image Captured While Shooting"}]]),Al={1:"No absolute unit of measurement",2:"Inch",3:"Centimeter"};io.set(37392,Al),io.set(41488,Al);var Ss={0:"Normal",1:"Low",2:"High"};function Dl(Qr){return typeof Qr=="object"&&Qr.length!==void 0?Qr[0]:Qr}function Pl(Qr){let Gr=Array.from(Qr).slice(1);return Gr[1]>15&&(Gr=Gr.map(Wr=>String.fromCharCode(Wr))),Gr[2]!=="0"&&Gr[2]!==0||Gr.pop(),Gr.join(".")}function ws(Qr){if(typeof Qr=="string"){var[Gr,Wr,Kr,Yr,Zr,tn]=Qr.trim().split(/[-: ]/g).map(Number),ln=new Date(Gr,Wr-1,Kr);return Number.isNaN(Yr)||Number.isNaN(Zr)||Number.isNaN(tn)||(ln.setHours(Yr),ln.setMinutes(Zr),ln.setSeconds(tn)),Number.isNaN(+ln)?Qr:ln}}function ro(Qr){if(typeof Qr=="string")return Qr;let Gr=[];if(Qr[1]===0&&Qr[Qr.length-1]===0)for(let Wr=0;Wr<Qr.length;Wr+=2)Gr.push(Rl(Qr[Wr+1],Qr[Wr]));else for(let Wr=0;Wr<Qr.length;Wr+=2)Gr.push(Rl(Qr[Wr],Qr[Wr+1]));return ba(String.fromCodePoint(...Gr))}function Rl(Qr,Gr){return Qr<<8|Gr}io.set(41992,Ss),io.set(41993,Ss),io.set(41994,Ss),pi(da,["ifd0","ifd1"],[[50827,function(Qr){return typeof Qr!="string"?ql(Qr):Qr}],[306,ws],[40091,ro],[40092,ro],[40093,ro],[40094,ro],[40095,ro]]),pi(da,"exif",[[40960,Pl],[36864,Pl],[36867,ws],[36868,ws],[40962,Dl],[40963,Dl]]),pi(da,"gps",[[0,Qr=>Array.from(Qr).join(".")],[7,Qr=>Array.from(Qr).join(":")]]);var ao=class extends qi{static canHandle(Gr,Wr){return Gr.getUint8(Wr+1)===225&&Gr.getUint32(Wr+4)===1752462448&&Gr.getString(Wr+4,20)==="http://ns.adobe.com/"}static headerLength(Gr,Wr){return Gr.getString(Wr+4,34)==="http://ns.adobe.com/xmp/extension/"?79:33}static findPosition(Gr,Wr){let Kr=super.findPosition(Gr,Wr);return Kr.multiSegment=Kr.extended=Kr.headerLength===79,Kr.multiSegment?(Kr.chunkCount=Gr.getUint8(Wr+72),Kr.chunkNumber=Gr.getUint8(Wr+76),Gr.getUint8(Wr+77)!==0&&Kr.chunkNumber++):(Kr.chunkCount=1/0,Kr.chunkNumber=-1),Kr}static handleMultiSegments(Gr){return Gr.map(Wr=>Wr.chunk.getString()).join("")}normalizeInput(Gr){return typeof Gr=="string"?Gr:Ki.from(Gr).getString()}parse(Gr=this.chunk){if(!this.localOptions.parse)return Gr;Gr=function(Zr){let tn={},ln={};for(let dn of Vl)tn[dn]=[],ln[dn]=0;return Zr.replace(Vu,(dn,yn,wn)=>{if(yn==="<"){let kn=++ln[wn];return tn[wn].push(kn),`${dn}#${kn}`}return`${dn}#${tn[wn].pop()}`})}(Gr);let Wr=Wo.findAll(Gr,"rdf","Description");Wr.length===0&&Wr.push(new Wo("rdf","Description",void 0,Gr));let Kr,Yr={};for(let Zr of Wr)for(let tn of Zr.properties)Kr=zu(tn.ns,Yr),_l(tn,Kr);return function(Zr){let tn;for(let ln in Zr)tn=Zr[ln]=Bo(Zr[ln]),tn===void 0&&delete Zr[ln];return Bo(Zr)}(Yr)}assignToOutput(Gr,Wr){if(this.localOptions.parse)for(let[Kr,Yr]of Object.entries(Wr))switch(Kr){case"tiff":this.assignObjectToOutput(Gr,"ifd0",Yr);break;case"exif":this.assignObjectToOutput(Gr,"exif",Yr);break;case"xmlns":break;default:this.assignObjectToOutput(Gr,Kr,Yr)}else Gr.xmp=Wr}};Kn(ao,"type","xmp"),Kn(ao,"multiSegment",!0),bi.set("xmp",ao);var Ms=class Qr{static findAll(Gr){return jl(Gr,/([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)=("[^"]*"|'[^']*')/gm).map(Qr.unpackMatch)}static unpackMatch(Gr){let Wr=Gr[1],Kr=Gr[2],Yr=Gr[3].slice(1,-1);return Yr=zl(Yr),new Qr(Wr,Kr,Yr)}constructor(Gr,Wr,Kr){this.ns=Gr,this.name=Wr,this.value=Kr}serialize(){return this.value}},Wo=class Qr{static findAll(Gr,Wr,Kr){if(Wr!==void 0||Kr!==void 0){Wr=Wr||"[\\w\\d-]+",Kr=Kr||"[\\w\\d-]+";var Yr=new RegExp(`<(${Wr}):(${Kr})(#\\d+)?((\\s+?[\\w\\d-:]+=("[^"]*"|'[^']*'))*\\s*)(\\/>|>([\\s\\S]*?)<\\/\\1:\\2\\3>)`,"gm")}else Yr=/<([\w\d-]+):([\w\d-]+)(#\d+)?((\s+?[\w\d-:]+=("[^"]*"|'[^']*'))*\s*)(\/>|>([\s\S]*?)<\/\1:\2\3>)/gm;return jl(Gr,Yr).map(Qr.unpackMatch)}static unpackMatch(Gr){let Wr=Gr[1],Kr=Gr[2],Yr=Gr[4],Zr=Gr[8];return new Qr(Wr,Kr,Yr,Zr)}constructor(Gr,Wr,Kr,Yr){this.ns=Gr,this.name=Wr,this.attrString=Kr,this.innerXml=Yr,this.attrs=Ms.findAll(Kr),this.children=Qr.findAll(Yr),this.value=this.children.length===0?zl(Yr):void 0,this.properties=[...this.attrs,...this.children]}get isPrimitive(){return this.value!==void 0&&this.attrs.length===0&&this.children.length===0}get isListContainer(){return this.children.length===1&&this.children[0].isList}get isList(){let{ns:Gr,name:Wr}=this;return Gr==="rdf"&&(Wr==="Seq"||Wr==="Bag"||Wr==="Alt")}get isListItem(){return this.ns==="rdf"&&this.name==="li"}serialize(){if(this.properties.length===0&&this.value===void 0)return;if(this.isPrimitive)return this.value;if(this.isListContainer)return this.children[0].serialize();if(this.isList)return ju(this.children.map(_u));if(this.isListItem&&this.children.length===1&&this.attrs.length===0)return this.children[0].serialize();let Gr={};for(let Wr of this.properties)_l(Wr,Gr);return this.value!==void 0&&(Gr.value=this.value),Bo(Gr)}};function _l(Qr,Gr){let Wr=Qr.serialize();Wr!==void 0&&(Gr[Qr.name]=Wr)}var _u=Qr=>Qr.serialize(),ju=Qr=>Qr.length===1?Qr[0]:Qr,zu=(Qr,Gr)=>Gr[Qr]?Gr[Qr]:Gr[Qr]={};function jl(Qr,Gr){let Wr,Kr=[];if(!Qr)return Kr;for(;(Wr=Gr.exec(Qr))!==null;)Kr.push(Wr);return Kr}function zl(Qr){if(function(Kr){return Kr==null||Kr==="null"||Kr==="undefined"||Kr===""||Kr.trim()===""}(Qr))return;let Gr=Number(Qr);if(!Number.isNaN(Gr))return Gr;let Wr=Qr.toLowerCase();return Wr==="true"||Wr!=="false"&&Qr.trim()}var Vl=["rdf:li","rdf:Seq","rdf:Bag","rdf:Alt","rdf:Description"],Vu=new RegExp(`(<|\\/)(${Vl.join("|")})`,"g"),Wu=Object.freeze({__proto__:null,default:Gu,Exifr:$i,fileParsers:Yi,segmentParsers:bi,fileReaders:Ji,tagKeys:Si,tagValues:Wi,tagRevivers:da,createDictionary:pi,extendDictionary:wa,fetchUrlAsArrayBuffer:xa,readBlobAsArrayBuffer:Sa,chunkedProps:ua,otherSegments:Ha,segments:ka,tiffBlocks:di,segmentsAndBlocks:ca,tiffExtractables:fa,inheritables:Ga,allFormatters:ha,Options:ia,parse:Zo,gpsOnlyOptions:Us,gps:Ul,thumbnailOnlyOptions:Bs,thumbnail:Bl,thumbnailUrl:Hl,orientationOnlyOptions:Hs,orientation:Gs,rotations:_s,get rotateCanvas(){return Ea},get rotateCss(){return Ta},rotation:Gl});var Nl=Uo("fs",Qr=>Qr.promises);Ji.set("fs",class extends Ba{async readWhole(){this.chunked=!1,this.fs=await Nl;let Qr=await this.fs.readFile(this.input);this._swapBuffer(Qr)}async readChunked(){this.chunked=!0,this.fs=await Nl,await this.open(),await this.readChunk(0,this.options.firstChunkSize)}async open(){this.fh===void 0&&(this.fh=await this.fs.open(this.input,"r"),this.size=(await this.fh.stat(this.input)).size)}async _readChunk(Qr,Gr){this.fh===void 0&&await this.open(),Qr+Gr>this.size&&(Gr=this.size-Qr);var Wr=this.subarray(Qr,Gr,!0);return await this.fh.read(Wr.dataView,0,Gr,Qr),Wr}async close(){if(this.fh){let Qr=this.fh;this.fh=void 0,await Qr.close()}}});Ji.set("base64",class extends Ba{constructor(...Qr){super(...Qr),this.input=this.input.replace(/^data:([^;]+);base64,/gim,""),this.size=this.input.length/4*3,this.input.endsWith("==")?this.size-=2:this.input.endsWith("=")&&(this.size-=1)}async _readChunk(Qr,Gr){let Wr,Kr,Yr=this.input;Qr===void 0?(Qr=0,Wr=0,Kr=0):(Wr=4*Math.floor(Qr/3),Kr=Qr-Wr/4*3),Gr===void 0&&(Gr=this.size);let Zr=Qr+Gr,tn=Wr+4*Math.ceil(Zr/3);Yr=Yr.slice(Wr,tn);let ln=Math.min(Gr,this.size-Qr);if(Jo){let dn=$o.from(Yr,"base64").slice(Kr,Kr+ln);return this.set(dn,Qr,!0)}{let dn=this.subarray(Qr,ln,!0),yn=atob(Yr),wn=dn.toUint8();for(let kn=0;kn<ln;kn++)wn[kn]=yn.charCodeAt(Kr+kn);return dn}}});var Xo=class extends Ua{static canHandle(Gr,Wr){return Wr===18761||Wr===19789}extendOptions(Gr){let{ifd0:Wr,xmp:Kr,iptc:Yr,icc:Zr}=Gr;Kr.enabled&&Wr.deps.add(700),Yr.enabled&&Wr.deps.add(33723),Zr.enabled&&Wr.deps.add(34675),Wr.finalizeFilters()}async parse(){let{tiff:Gr,xmp:Wr,iptc:Kr,icc:Yr}=this.options;if(Gr.enabled||Wr.enabled||Kr.enabled||Yr.enabled){let Zr=Math.max(ks(this.options),this.options.chunkSize);await this.file.ensureChunk(0,Zr),this.createParser("tiff",this.file),this.parsers.tiff.parseHeader(),await this.parsers.tiff.parseIfd0Block(),this.adaptTiffPropAsSegment("xmp"),this.adaptTiffPropAsSegment("iptc"),this.adaptTiffPropAsSegment("icc")}}adaptTiffPropAsSegment(Gr){if(this.parsers.tiff[Gr]){let Wr=this.parsers.tiff[Gr];this.injectSegment(Gr,Wr)}}};Kn(Xo,"type","tiff"),Yi.set("tiff",Xo);var Xu=Uo("zlib"),Qu=["ihdr","iccp","text","itxt","exif"],Qo=class extends Ua{constructor(...Gr){super(...Gr),Kn(this,"catchError",Wr=>this.errors.push(Wr)),Kn(this,"metaChunks",[]),Kn(this,"unknownChunks",[])}static canHandle(Gr,Wr){return Wr===35152&&Gr.getUint32(0)===2303741511&&Gr.getUint32(4)===218765834}async parse(){let{file:Gr}=this;await this.findPngChunksInRange(8,Gr.byteLength),await this.readSegments(this.metaChunks),this.findIhdr(),this.parseTextChunks(),await this.findExif().catch(this.catchError),await this.findXmp().catch(this.catchError),await this.findIcc().catch(this.catchError)}async findPngChunksInRange(Gr,Wr){let{file:Kr}=this;for(;Gr<Wr;){let Yr=Kr.getUint32(Gr),Zr=Kr.getUint32(Gr+4),tn=Kr.getString(Gr+4,4).toLowerCase(),ln=Yr+4+4+4,dn={type:tn,offset:Gr,length:ln,start:Gr+4+4,size:Yr,marker:Zr};Qu.includes(tn)?this.metaChunks.push(dn):this.unknownChunks.push(dn),Gr+=ln}}parseTextChunks(){let Gr=this.metaChunks.filter(Wr=>Wr.type==="text");for(let Wr of Gr){let[Kr,Yr]=this.file.getString(Wr.start,Wr.size).split("\0");this.injectKeyValToIhdr(Kr,Yr)}}injectKeyValToIhdr(Gr,Wr){let Kr=this.parsers.ihdr;Kr&&Kr.raw.set(Gr,Wr)}findIhdr(){let Gr=this.metaChunks.find(Wr=>Wr.type==="ihdr");Gr&&this.options.ihdr.enabled!==!1&&this.createParser("ihdr",Gr.chunk)}async findExif(){let Gr=this.metaChunks.find(Wr=>Wr.type==="exif");Gr&&this.injectSegment("tiff",Gr.chunk)}async findXmp(){let Gr=this.metaChunks.filter(Wr=>Wr.type==="itxt");for(let Wr of Gr)Wr.chunk.getString(0,17)==="XML:com.adobe.xmp"&&this.injectSegment("xmp",Wr.chunk)}async findIcc(){let Gr=this.metaChunks.find(ln=>ln.type==="iccp");if(!Gr)return;let{chunk:Wr}=Gr,Kr=Wr.getUint8Array(0,81),Yr=0;for(;Yr<80&&Kr[Yr]!==0;)Yr++;let Zr=Yr+2,tn=Wr.getString(0,Yr);if(this.injectKeyValToIhdr("ProfileName",tn),qo){let ln=await Xu,dn=Wr.getUint8Array(Zr);dn=ln.inflateSync(dn),this.injectSegment("icc",dn)}}};Kn(Qo,"type","png"),Yi.set("png",Qo),pi(Si,"interop",[[1,"InteropIndex"],[2,"InteropVersion"],[4096,"RelatedImageFileFormat"],[4097,"RelatedImageWidth"],[4098,"RelatedImageHeight"]]),wa(Si,"ifd0",[[11,"ProcessingSoftware"],[254,"SubfileType"],[255,"OldSubfileType"],[263,"Thresholding"],[264,"CellWidth"],[265,"CellLength"],[266,"FillOrder"],[269,"DocumentName"],[280,"MinSampleValue"],[281,"MaxSampleValue"],[285,"PageName"],[286,"XPosition"],[287,"YPosition"],[290,"GrayResponseUnit"],[297,"PageNumber"],[321,"HalftoneHints"],[322,"TileWidth"],[323,"TileLength"],[332,"InkSet"],[337,"TargetPrinter"],[18246,"Rating"],[18249,"RatingPercent"],[33550,"PixelScale"],[34264,"ModelTransform"],[34377,"PhotoshopSettings"],[50706,"DNGVersion"],[50707,"DNGBackwardVersion"],[50708,"UniqueCameraModel"],[50709,"LocalizedCameraModel"],[50736,"DNGLensInfo"],[50739,"ShadowScale"],[50740,"DNGPrivateData"],[33920,"IntergraphMatrix"],[33922,"ModelTiePoint"],[34118,"SEMInfo"],[34735,"GeoTiffDirectory"],[34736,"GeoTiffDoubleParams"],[34737,"GeoTiffAsciiParams"],[50341,"PrintIM"],[50721,"ColorMatrix1"],[50722,"ColorMatrix2"],[50723,"CameraCalibration1"],[50724,"CameraCalibration2"],[50725,"ReductionMatrix1"],[50726,"ReductionMatrix2"],[50727,"AnalogBalance"],[50728,"AsShotNeutral"],[50729,"AsShotWhiteXY"],[50730,"BaselineExposure"],[50731,"BaselineNoise"],[50732,"BaselineSharpness"],[50734,"LinearResponseLimit"],[50735,"CameraSerialNumber"],[50741,"MakerNoteSafety"],[50778,"CalibrationIlluminant1"],[50779,"CalibrationIlluminant2"],[50781,"RawDataUniqueID"],[50827,"OriginalRawFileName"],[50828,"OriginalRawFileData"],[50831,"AsShotICCProfile"],[50832,"AsShotPreProfileMatrix"],[50833,"CurrentICCProfile"],[50834,"CurrentPreProfileMatrix"],[50879,"ColorimetricReference"],[50885,"SRawType"],[50898,"PanasonicTitle"],[50899,"PanasonicTitle2"],[50931,"CameraCalibrationSig"],[50932,"ProfileCalibrationSig"],[50933,"ProfileIFD"],[50934,"AsShotProfileName"],[50936,"ProfileName"],[50937,"ProfileHueSatMapDims"],[50938,"ProfileHueSatMapData1"],[50939,"ProfileHueSatMapData2"],[50940,"ProfileToneCurve"],[50941,"ProfileEmbedPolicy"],[50942,"ProfileCopyright"],[50964,"ForwardMatrix1"],[50965,"ForwardMatrix2"],[50966,"PreviewApplicationName"],[50967,"PreviewApplicationVersion"],[50968,"PreviewSettingsName"],[50969,"PreviewSettingsDigest"],[50970,"PreviewColorSpace"],[50971,"PreviewDateTime"],[50972,"RawImageDigest"],[50973,"OriginalRawFileDigest"],[50981,"ProfileLookTableDims"],[50982,"ProfileLookTableData"],[51043,"TimeCodes"],[51044,"FrameRate"],[51058,"TStop"],[51081,"ReelName"],[51089,"OriginalDefaultFinalSize"],[51090,"OriginalBestQualitySize"],[51091,"OriginalDefaultCropSize"],[51105,"CameraLabel"],[51107,"ProfileHueSatMapEncoding"],[51108,"ProfileLookTableEncoding"],[51109,"BaselineExposureOffset"],[51110,"DefaultBlackRender"],[51111,"NewRawImageDigest"],[51112,"RawToPreviewGain"]]);var Ll=[[273,"StripOffsets"],[279,"StripByteCounts"],[288,"FreeOffsets"],[289,"FreeByteCounts"],[291,"GrayResponseCurve"],[292,"T4Options"],[293,"T6Options"],[300,"ColorResponseUnit"],[320,"ColorMap"],[324,"TileOffsets"],[325,"TileByteCounts"],[326,"BadFaxLines"],[327,"CleanFaxData"],[328,"ConsecutiveBadFaxLines"],[330,"SubIFD"],[333,"InkNames"],[334,"NumberofInks"],[336,"DotRange"],[338,"ExtraSamples"],[339,"SampleFormat"],[340,"SMinSampleValue"],[341,"SMaxSampleValue"],[342,"TransferRange"],[343,"ClipPath"],[344,"XClipPathUnits"],[345,"YClipPathUnits"],[346,"Indexed"],[347,"JPEGTables"],[351,"OPIProxy"],[400,"GlobalParametersIFD"],[401,"ProfileType"],[402,"FaxProfile"],[403,"CodingMethods"],[404,"VersionYear"],[405,"ModeNumber"],[433,"Decode"],[434,"DefaultImageColor"],[435,"T82Options"],[437,"JPEGTables"],[512,"JPEGProc"],[515,"JPEGRestartInterval"],[517,"JPEGLosslessPredictors"],[518,"JPEGPointTransforms"],[519,"JPEGQTables"],[520,"JPEGDCTables"],[521,"JPEGACTables"],[559,"StripRowCounts"],[999,"USPTOMiscellaneous"],[18247,"XP_DIP_XML"],[18248,"StitchInfo"],[28672,"SonyRawFileType"],[28688,"SonyToneCurve"],[28721,"VignettingCorrection"],[28722,"VignettingCorrParams"],[28724,"ChromaticAberrationCorrection"],[28725,"ChromaticAberrationCorrParams"],[28726,"DistortionCorrection"],[28727,"DistortionCorrParams"],[29895,"SonyCropTopLeft"],[29896,"SonyCropSize"],[32781,"ImageID"],[32931,"WangTag1"],[32932,"WangAnnotation"],[32933,"WangTag3"],[32934,"WangTag4"],[32953,"ImageReferencePoints"],[32954,"RegionXformTackPoint"],[32955,"WarpQuadrilateral"],[32956,"AffineTransformMat"],[32995,"Matteing"],[32996,"DataType"],[32997,"ImageDepth"],[32998,"TileDepth"],[33300,"ImageFullWidth"],[33301,"ImageFullHeight"],[33302,"TextureFormat"],[33303,"WrapModes"],[33304,"FovCot"],[33305,"MatrixWorldToScreen"],[33306,"MatrixWorldToCamera"],[33405,"Model2"],[33421,"CFARepeatPatternDim"],[33422,"CFAPattern2"],[33423,"BatteryLevel"],[33424,"KodakIFD"],[33445,"MDFileTag"],[33446,"MDScalePixel"],[33447,"MDColorTable"],[33448,"MDLabName"],[33449,"MDSampleInfo"],[33450,"MDPrepDate"],[33451,"MDPrepTime"],[33452,"MDFileUnits"],[33589,"AdventScale"],[33590,"AdventRevision"],[33628,"UIC1Tag"],[33629,"UIC2Tag"],[33630,"UIC3Tag"],[33631,"UIC4Tag"],[33918,"IntergraphPacketData"],[33919,"IntergraphFlagRegisters"],[33921,"INGRReserved"],[34016,"Site"],[34017,"ColorSequence"],[34018,"IT8Header"],[34019,"RasterPadding"],[34020,"BitsPerRunLength"],[34021,"BitsPerExtendedRunLength"],[34022,"ColorTable"],[34023,"ImageColorIndicator"],[34024,"BackgroundColorIndicator"],[34025,"ImageColorValue"],[34026,"BackgroundColorValue"],[34027,"PixelIntensityRange"],[34028,"TransparencyIndicator"],[34029,"ColorCharacterization"],[34030,"HCUsage"],[34031,"TrapIndicator"],[34032,"CMYKEquivalent"],[34152,"AFCP_IPTC"],[34232,"PixelMagicJBIGOptions"],[34263,"JPLCartoIFD"],[34306,"WB_GRGBLevels"],[34310,"LeafData"],[34687,"TIFF_FXExtensions"],[34688,"MultiProfiles"],[34689,"SharedData"],[34690,"T88Options"],[34732,"ImageLayer"],[34750,"JBIGOptions"],[34856,"Opto-ElectricConvFactor"],[34857,"Interlace"],[34908,"FaxRecvParams"],[34909,"FaxSubAddress"],[34910,"FaxRecvTime"],[34929,"FedexEDR"],[34954,"LeafSubIFD"],[37387,"FlashEnergy"],[37388,"SpatialFrequencyResponse"],[37389,"Noise"],[37390,"FocalPlaneXResolution"],[37391,"FocalPlaneYResolution"],[37392,"FocalPlaneResolutionUnit"],[37397,"ExposureIndex"],[37398,"TIFF-EPStandardID"],[37399,"SensingMethod"],[37434,"CIP3DataFile"],[37435,"CIP3Sheet"],[37436,"CIP3Side"],[37439,"StoNits"],[37679,"MSDocumentText"],[37680,"MSPropertySetStorage"],[37681,"MSDocumentTextPosition"],[37724,"ImageSourceData"],[40965,"InteropIFD"],[40976,"SamsungRawPointersOffset"],[40977,"SamsungRawPointersLength"],[41217,"SamsungRawByteOrder"],[41218,"SamsungRawUnknown"],[41484,"SpatialFrequencyResponse"],[41485,"Noise"],[41489,"ImageNumber"],[41490,"SecurityClassification"],[41491,"ImageHistory"],[41494,"TIFF-EPStandardID"],[41995,"DeviceSettingDescription"],[42112,"GDALMetadata"],[42113,"GDALNoData"],[44992,"ExpandSoftware"],[44993,"ExpandLens"],[44994,"ExpandFilm"],[44995,"ExpandFilterLens"],[44996,"ExpandScanner"],[44997,"ExpandFlashLamp"],[46275,"HasselbladRawImage"],[48129,"PixelFormat"],[48130,"Transformation"],[48131,"Uncompressed"],[48132,"ImageType"],[48256,"ImageWidth"],[48257,"ImageHeight"],[48258,"WidthResolution"],[48259,"HeightResolution"],[48320,"ImageOffset"],[48321,"ImageByteCount"],[48322,"AlphaOffset"],[48323,"AlphaByteCount"],[48324,"ImageDataDiscard"],[48325,"AlphaDataDiscard"],[50215,"OceScanjobDesc"],[50216,"OceApplicationSelector"],[50217,"OceIDNumber"],[50218,"OceImageLogic"],[50255,"Annotations"],[50459,"HasselbladExif"],[50547,"OriginalFileName"],[50560,"USPTOOriginalContentType"],[50656,"CR2CFAPattern"],[50710,"CFAPlaneColor"],[50711,"CFALayout"],[50712,"LinearizationTable"],[50713,"BlackLevelRepeatDim"],[50714,"BlackLevel"],[50715,"BlackLevelDeltaH"],[50716,"BlackLevelDeltaV"],[50717,"WhiteLevel"],[50718,"DefaultScale"],[50719,"DefaultCropOrigin"],[50720,"DefaultCropSize"],[50733,"BayerGreenSplit"],[50737,"ChromaBlurRadius"],[50738,"AntiAliasStrength"],[50752,"RawImageSegmentation"],[50780,"BestQualityScale"],[50784,"AliasLayerMetadata"],[50829,"ActiveArea"],[50830,"MaskedAreas"],[50935,"NoiseReductionApplied"],[50974,"SubTileBlockSize"],[50975,"RowInterleaveFactor"],[51008,"OpcodeList1"],[51009,"OpcodeList2"],[51022,"OpcodeList3"],[51041,"NoiseProfile"],[51114,"CacheVersion"],[51125,"DefaultUserCrop"],[51157,"NikonNEFInfo"],[65024,"KdcIFD"]];wa(Si,"ifd0",Ll),wa(Si,"exif",Ll),pi(Wi,"gps",[[23,{M:"Magnetic North",T:"True North"}],[25,{K:"Kilometers",M:"Miles",N:"Nautical Miles"}]]);var oo=class extends qi{static canHandle(Gr,Wr){return Gr.getUint8(Wr+1)===224&&Gr.getUint32(Wr+4)===1246120262&&Gr.getUint8(Wr+8)===0}parse(){return this.parseTags(),this.translate(),this.output}parseTags(){this.raw=new Map([[0,this.chunk.getUint16(0)],[2,this.chunk.getUint8(2)],[3,this.chunk.getUint16(3)],[5,this.chunk.getUint16(5)],[7,this.chunk.getUint8(7)],[8,this.chunk.getUint8(8)]])}};Kn(oo,"type","jfif"),Kn(oo,"headerLength",9),bi.set("jfif",oo),pi(Si,"jfif",[[0,"JFIFVersion"],[2,"ResolutionUnit"],[3,"XResolution"],[5,"YResolution"],[7,"ThumbnailWidth"],[8,"ThumbnailHeight"]]);var Ko=class extends qi{parse(){return this.parseTags(),this.translate(),this.output}parseTags(){this.raw=new Map([[0,this.chunk.getUint32(0)],[4,this.chunk.getUint32(4)],[8,this.chunk.getUint8(8)],[9,this.chunk.getUint8(9)],[10,this.chunk.getUint8(10)],[11,this.chunk.getUint8(11)],[12,this.chunk.getUint8(12)],...Array.from(this.raw)])}};Kn(Ko,"type","ihdr"),bi.set("ihdr",Ko),pi(Si,"ihdr",[[0,"ImageWidth"],[4,"ImageHeight"],[8,"BitDepth"],[9,"ColorType"],[10,"Compression"],[11,"Filter"],[12,"Interlace"]]),pi(Wi,"ihdr",[[9,{0:"Grayscale",2:"RGB",3:"Palette",4:"Grayscale with Alpha",6:"RGB with Alpha",DEFAULT:"Unknown"}],[10,{0:"Deflate/Inflate",DEFAULT:"Unknown"}],[11,{0:"Adaptive",DEFAULT:"Unknown"}],[12,{0:"Noninterlaced",1:"Adam7 Interlace",DEFAULT:"Unknown"}]]);var qa=class extends qi{static canHandle(Gr,Wr){return Gr.getUint8(Wr+1)===226&&Gr.getUint32(Wr+4)===1229144927}static findPosition(Gr,Wr){let Kr=super.findPosition(Gr,Wr);return Kr.chunkNumber=Gr.getUint8(Wr+16),Kr.chunkCount=Gr.getUint8(Wr+17),Kr.multiSegment=Kr.chunkCount>1,Kr}static handleMultiSegments(Gr){return function(Wr){let Kr=function(Yr){let Zr=Yr[0].constructor,tn=0;for(let yn of Yr)tn+=yn.length;let ln=new Zr(tn),dn=0;for(let yn of Yr)ln.set(yn,dn),dn+=yn.length;return ln}(Wr.map(Yr=>Yr.chunk.toUint8()));return new Ki(Kr)}(Gr)}parse(){return this.raw=new Map,this.parseHeader(),this.parseTags(),this.translate(),this.output}parseHeader(){let{raw:Gr}=this;this.chunk.byteLength<84&&Ei("ICC header is too short");for(let[Wr,Kr]of Object.entries(Ku)){Wr=parseInt(Wr,10);let Yr=Kr(this.chunk,Wr);Yr!=="\0\0\0\0"&&Gr.set(Wr,Yr)}}parseTags(){let Gr,Wr,Kr,Yr,Zr,{raw:tn}=this,ln=this.chunk.getUint32(128),dn=132,yn=this.chunk.byteLength;for(;ln--;){if(Gr=this.chunk.getString(dn,4),Wr=this.chunk.getUint32(dn+4),Kr=this.chunk.getUint32(dn+8),Yr=this.chunk.getString(Wr,4),Wr+Kr>yn)return void console.warn("reached the end of the first ICC chunk. Enable options.tiff.multiSegment to read all ICC segments.");Zr=this.parseTag(Yr,Wr,Kr),Zr!==void 0&&Zr!=="\0\0\0\0"&&tn.set(Gr,Zr),dn+=12}}parseTag(Gr,Wr,Kr){switch(Gr){case"desc":return this.parseDesc(Wr);case"mluc":return this.parseMluc(Wr);case"text":return this.parseText(Wr,Kr);case"sig ":return this.parseSig(Wr)}if(!(Wr+Kr>this.chunk.byteLength))return this.chunk.getUint8Array(Wr,Kr)}parseDesc(Gr){let Wr=this.chunk.getUint32(Gr+8)-1;return ba(this.chunk.getString(Gr+12,Wr))}parseText(Gr,Wr){return ba(this.chunk.getString(Gr+8,Wr-8))}parseSig(Gr){return ba(this.chunk.getString(Gr+8,4))}parseMluc(Gr){let{chunk:Wr}=this,Kr=Wr.getUint32(Gr+8),Yr=Wr.getUint32(Gr+12),Zr=Gr+16,tn=[];for(let ln=0;ln<Kr;ln++){let dn=Wr.getString(Zr+0,2),yn=Wr.getString(Zr+2,2),wn=Wr.getUint32(Zr+4),kn=Wr.getUint32(Zr+8)+Gr,An=ba(Wr.getUnicodeString(kn,wn));tn.push({lang:dn,country:yn,text:An}),Zr+=Yr}return Kr===1?tn[0].text:tn}translateValue(Gr,Wr){return typeof Gr=="string"?Wr[Gr]||Wr[Gr.toLowerCase()]||Gr:Wr[Gr]||Gr}};Kn(qa,"type","icc"),Kn(qa,"multiSegment",!0),Kn(qa,"headerLength",18);var Ku={4:na,8:function(Qr,Gr){return[Qr.getUint8(Gr),Qr.getUint8(Gr+1)>>4,Qr.getUint8(Gr+1)%16].map(Wr=>Wr.toString(10)).join(".")},12:na,16:na,20:na,24:function(Qr,Gr){let Wr=Qr.getUint16(Gr),Kr=Qr.getUint16(Gr+2)-1,Yr=Qr.getUint16(Gr+4),Zr=Qr.getUint16(Gr+6),tn=Qr.getUint16(Gr+8),ln=Qr.getUint16(Gr+10);return new Date(Date.UTC(Wr,Kr,Yr,Zr,tn,ln))},36:na,40:na,48:na,52:na,64:(Qr,Gr)=>Qr.getUint32(Gr),80:na};function na(Qr,Gr){return ba(Qr.getString(Gr,4))}bi.set("icc",qa),pi(Si,"icc",[[4,"ProfileCMMType"],[8,"ProfileVersion"],[12,"ProfileClass"],[16,"ColorSpaceData"],[20,"ProfileConnectionSpace"],[24,"ProfileDateTime"],[36,"ProfileFileSignature"],[40,"PrimaryPlatform"],[44,"CMMFlags"],[48,"DeviceManufacturer"],[52,"DeviceModel"],[56,"DeviceAttributes"],[64,"RenderingIntent"],[68,"ConnectionSpaceIlluminant"],[80,"ProfileCreator"],[84,"ProfileID"],["Header","ProfileHeader"],["MS00","WCSProfiles"],["bTRC","BlueTRC"],["bXYZ","BlueMatrixColumn"],["bfd","UCRBG"],["bkpt","MediaBlackPoint"],["calt","CalibrationDateTime"],["chad","ChromaticAdaptation"],["chrm","Chromaticity"],["ciis","ColorimetricIntentImageState"],["clot","ColorantTableOut"],["clro","ColorantOrder"],["clrt","ColorantTable"],["cprt","ProfileCopyright"],["crdi","CRDInfo"],["desc","ProfileDescription"],["devs","DeviceSettings"],["dmdd","DeviceModelDesc"],["dmnd","DeviceMfgDesc"],["dscm","ProfileDescriptionML"],["fpce","FocalPlaneColorimetryEstimates"],["gTRC","GreenTRC"],["gXYZ","GreenMatrixColumn"],["gamt","Gamut"],["kTRC","GrayTRC"],["lumi","Luminance"],["meas","Measurement"],["meta","Metadata"],["mmod","MakeAndModel"],["ncl2","NamedColor2"],["ncol","NamedColor"],["ndin","NativeDisplayInfo"],["pre0","Preview0"],["pre1","Preview1"],["pre2","Preview2"],["ps2i","PS2RenderingIntent"],["ps2s","PostScript2CSA"],["psd0","PostScript2CRD0"],["psd1","PostScript2CRD1"],["psd2","PostScript2CRD2"],["psd3","PostScript2CRD3"],["pseq","ProfileSequenceDesc"],["psid","ProfileSequenceIdentifier"],["psvm","PS2CRDVMSize"],["rTRC","RedTRC"],["rXYZ","RedMatrixColumn"],["resp","OutputResponse"],["rhoc","ReflectionHardcopyOrigColorimetry"],["rig0","PerceptualRenderingIntentGamut"],["rig2","SaturationRenderingIntentGamut"],["rpoc","ReflectionPrintOutputColorimetry"],["sape","SceneAppearanceEstimates"],["scoe","SceneColorimetryEstimates"],["scrd","ScreeningDesc"],["scrn","Screening"],["targ","CharTarget"],["tech","Technology"],["vcgt","VideoCardGamma"],["view","ViewingConditions"],["vued","ViewingCondDesc"],["wtpt","MediaWhitePoint"]]);var Fo={"4d2p":"Erdt Systems",AAMA:"Aamazing Technologies",ACER:"Acer",ACLT:"Acolyte Color Research",ACTI:"Actix Sytems",ADAR:"Adara Technology",ADBE:"Adobe",ADI:"ADI Systems",AGFA:"Agfa Graphics",ALMD:"Alps Electric",ALPS:"Alps Electric",ALWN:"Alwan Color Expertise",AMTI:"Amiable Technologies",AOC:"AOC International",APAG:"Apago",APPL:"Apple Computer",AST:"AST","AT&T":"AT&T",BAEL:"BARBIERI electronic",BRCO:"Barco NV",BRKP:"Breakpoint",BROT:"Brother",BULL:"Bull",BUS:"Bus Computer Systems","C-IT":"C-Itoh",CAMR:"Intel",CANO:"Canon",CARR:"Carroll Touch",CASI:"Casio",CBUS:"Colorbus PL",CEL:"Crossfield",CELx:"Crossfield",CGS:"CGS Publishing Technologies International",CHM:"Rochester Robotics",CIGL:"Colour Imaging Group, London",CITI:"Citizen",CL00:"Candela",CLIQ:"Color IQ",CMCO:"Chromaco",CMiX:"CHROMiX",COLO:"Colorgraphic Communications",COMP:"Compaq",COMp:"Compeq/Focus Technology",CONR:"Conrac Display Products",CORD:"Cordata Technologies",CPQ:"Compaq",CPRO:"ColorPro",CRN:"Cornerstone",CTX:"CTX International",CVIS:"ColorVision",CWC:"Fujitsu Laboratories",DARI:"Darius Technology",DATA:"Dataproducts",DCP:"Dry Creek Photo",DCRC:"Digital Contents Resource Center, Chung-Ang University",DELL:"Dell Computer",DIC:"Dainippon Ink and Chemicals",DICO:"Diconix",DIGI:"Digital","DL&C":"Digital Light & Color",DPLG:"Doppelganger",DS:"Dainippon Screen",DSOL:"DOOSOL",DUPN:"DuPont",EPSO:"Epson",ESKO:"Esko-Graphics",ETRI:"Electronics and Telecommunications Research Institute",EVER:"Everex Systems",EXAC:"ExactCODE",Eizo:"Eizo",FALC:"Falco Data Products",FF:"Fuji Photo Film",FFEI:"FujiFilm Electronic Imaging",FNRD:"Fnord Software",FORA:"Fora",FORE:"Forefront Technology",FP:"Fujitsu",FPA:"WayTech Development",FUJI:"Fujitsu",FX:"Fuji Xerox",GCC:"GCC Technologies",GGSL:"Global Graphics Software",GMB:"Gretagmacbeth",GMG:"GMG",GOLD:"GoldStar Technology",GOOG:"Google",GPRT:"Giantprint",GTMB:"Gretagmacbeth",GVC:"WayTech Development",GW2K:"Sony",HCI:"HCI",HDM:"Heidelberger Druckmaschinen",HERM:"Hermes",HITA:"Hitachi America",HP:"Hewlett-Packard",HTC:"Hitachi",HiTi:"HiTi Digital",IBM:"IBM",IDNT:"Scitex",IEC:"Hewlett-Packard",IIYA:"Iiyama North America",IKEG:"Ikegami Electronics",IMAG:"Image Systems",IMI:"Ingram Micro",INTC:"Intel",INTL:"N/A (INTL)",INTR:"Intra Electronics",IOCO:"Iocomm International Technology",IPS:"InfoPrint Solutions Company",IRIS:"Scitex",ISL:"Ichikawa Soft Laboratory",ITNL:"N/A (ITNL)",IVM:"IVM",IWAT:"Iwatsu Electric",Idnt:"Scitex",Inca:"Inca Digital Printers",Iris:"Scitex",JPEG:"Joint Photographic Experts Group",JSFT:"Jetsoft Development",JVC:"JVC Information Products",KART:"Scitex",KFC:"KFC Computek Components",KLH:"KLH Computers",KMHD:"Konica Minolta",KNCA:"Konica",KODA:"Kodak",KYOC:"Kyocera",Kart:"Scitex",LCAG:"Leica",LCCD:"Leeds Colour",LDAK:"Left Dakota",LEAD:"Leading Technology",LEXM:"Lexmark International",LINK:"Link Computer",LINO:"Linotronic",LITE:"Lite-On",Leaf:"Leaf",Lino:"Linotronic",MAGC:"Mag Computronic",MAGI:"MAG Innovision",MANN:"Mannesmann",MICN:"Micron Technology",MICR:"Microtek",MICV:"Microvitec",MINO:"Minolta",MITS:"Mitsubishi Electronics America",MITs:"Mitsuba",MNLT:"Minolta",MODG:"Modgraph",MONI:"Monitronix",MONS:"Monaco Systems",MORS:"Morse Technology",MOTI:"Motive Systems",MSFT:"Microsoft",MUTO:"MUTOH INDUSTRIES",Mits:"Mitsubishi Electric",NANA:"NANAO",NEC:"NEC",NEXP:"NexPress Solutions",NISS:"Nissei Sangyo America",NKON:"Nikon",NONE:"none",OCE:"Oce Technologies",OCEC:"OceColor",OKI:"Oki",OKID:"Okidata",OKIP:"Okidata",OLIV:"Olivetti",OLYM:"Olympus",ONYX:"Onyx Graphics",OPTI:"Optiquest",PACK:"Packard Bell",PANA:"Matsushita Electric Industrial",PANT:"Pantone",PBN:"Packard Bell",PFU:"PFU",PHIL:"Philips Consumer Electronics",PNTX:"HOYA",POne:"Phase One A/S",PREM:"Premier Computer Innovations",PRIN:"Princeton Graphic Systems",PRIP:"Princeton Publishing Labs",QLUX:"Hong Kong",QMS:"QMS",QPCD:"QPcard AB",QUAD:"QuadLaser",QUME:"Qume",RADI:"Radius",RDDx:"Integrated Color Solutions",RDG:"Roland DG",REDM:"REDMS Group",RELI:"Relisys",RGMS:"Rolf Gierling Multitools",RICO:"Ricoh",RNLD:"Edmund Ronald",ROYA:"Royal",RPC:"Ricoh Printing Systems",RTL:"Royal Information Electronics",SAMP:"Sampo",SAMS:"Samsung",SANT:"Jaime Santana Pomares",SCIT:"Scitex",SCRN:"Dainippon Screen",SDP:"Scitex",SEC:"Samsung",SEIK:"Seiko Instruments",SEIk:"Seikosha",SGUY:"ScanGuy.com",SHAR:"Sharp Laboratories",SICC:"International Color Consortium",SONY:"Sony",SPCL:"SpectraCal",STAR:"Star",STC:"Sampo Technology",Scit:"Scitex",Sdp:"Scitex",Sony:"Sony",TALO:"Talon Technology",TAND:"Tandy",TATU:"Tatung",TAXA:"TAXAN America",TDS:"Tokyo Denshi Sekei",TECO:"TECO Information Systems",TEGR:"Tegra",TEKT:"Tektronix",TI:"Texas Instruments",TMKR:"TypeMaker",TOSB:"Toshiba",TOSH:"Toshiba",TOTK:"TOTOKU ELECTRIC",TRIU:"Triumph",TSBT:"Toshiba",TTX:"TTX Computer Products",TVM:"TVM Professional Monitor",TW:"TW Casper",ULSX:"Ulead Systems",UNIS:"Unisys",UTZF:"Utz Fehlau & Sohn",VARI:"Varityper",VIEW:"Viewsonic",VISL:"Visual communication",VIVO:"Vivo Mobile Communication",WANG:"Wang",WLBR:"Wilbur Imaging",WTG2:"Ware To Go",WYSE:"WYSE Technology",XERX:"Xerox",XRIT:"X-Rite",ZRAN:"Zoran",Zebr:"Zebra Technologies",appl:"Apple Computer",bICC:"basICColor",berg:"bergdesign",ceyd:"Integrated Color Solutions",clsp:"MacDermid ColorSpan",ds:"Dainippon Screen",dupn:"DuPont",ffei:"FujiFilm Electronic Imaging",flux:"FluxData",iris:"Scitex",kart:"Scitex",lcms:"Little CMS",lino:"Linotronic",none:"none",ob4d:"Erdt Systems",obic:"Medigraph",quby:"Qubyx Sarl",scit:"Scitex",scrn:"Dainippon Screen",sdp:"Scitex",siwi:"SIWI GRAFIKA",yxym:"YxyMaster"},Ml={scnr:"Scanner",mntr:"Monitor",prtr:"Printer",link:"Device Link",abst:"Abstract",spac:"Color Space Conversion Profile",nmcl:"Named Color",cenc:"ColorEncodingSpace profile",mid:"MultiplexIdentification profile",mlnk:"MultiplexLink profile",mvis:"MultiplexVisualization profile",nkpf:"Nikon Input Device Profile (NON-STANDARD!)"};pi(Wi,"icc",[[4,Fo],[12,Ml],[40,Object.assign({},Fo,Ml)],[48,Fo],[80,Fo],[64,{0:"Perceptual",1:"Relative Colorimetric",2:"Saturation",3:"Absolute Colorimetric"}],["tech",{amd:"Active Matrix Display",crt:"Cathode Ray Tube Display",kpcd:"Photo CD",pmd:"Passive Matrix Display",dcam:"Digital Camera",dcpj:"Digital Cinema Projector",dmpc:"Digital Motion Picture Camera",dsub:"Dye Sublimation Printer",epho:"Electrophotographic Printer",esta:"Electrostatic Printer",flex:"Flexography",fprn:"Film Writer",fscn:"Film Scanner",grav:"Gravure",ijet:"Ink Jet Printer",imgs:"Photo Image Setter",mpfr:"Motion Picture Film Recorder",mpfs:"Motion Picture Film Scanner",offs:"Offset Lithography",pjtv:"Projection Television",rpho:"Photographic Paper Printer",rscn:"Reflective Scanner",silk:"Silkscreen",twax:"Thermal Wax Printer",vidc:"Video Camera",vidm:"Video Monitor"}]]);var Fa=class extends qi{static canHandle(Gr,Wr,Kr){return Gr.getUint8(Wr+1)===237&&Gr.getString(Wr+4,9)==="Photoshop"&&this.containsIptc8bim(Gr,Wr,Kr)!==void 0}static headerLength(Gr,Wr,Kr){let Yr,Zr=this.containsIptc8bim(Gr,Wr,Kr);if(Zr!==void 0)return Yr=Gr.getUint8(Wr+Zr+7),Yr%2!=0&&(Yr+=1),Yr===0&&(Yr=4),Zr+8+Yr}static containsIptc8bim(Gr,Wr,Kr){for(let Yr=0;Yr<Kr;Yr++)if(this.isIptcSegmentHead(Gr,Wr+Yr))return Yr}static isIptcSegmentHead(Gr,Wr){return Gr.getUint8(Wr)===56&&Gr.getUint32(Wr)===943868237&&Gr.getUint16(Wr+4)===1028}parse(){let{raw:Gr}=this,Wr=this.chunk.byteLength-1,Kr=!1;for(let Yr=0;Yr<Wr;Yr++)if(this.chunk.getUint8(Yr)===28&&this.chunk.getUint8(Yr+1)===2){Kr=!0;let Zr=this.chunk.getUint16(Yr+3),tn=this.chunk.getUint8(Yr+2),ln=this.chunk.getLatin1String(Yr+5,Zr);Gr.set(tn,this.pluralizeValue(Gr.get(tn),ln)),Yr+=4+Zr}else if(Kr)break;return this.translate(),this.output}pluralizeValue(Gr,Wr){return Gr!==void 0?Gr instanceof Array?(Gr.push(Wr),Gr):[Gr,Wr]:Wr}};Kn(Fa,"type","iptc"),Kn(Fa,"translateValues",!1),Kn(Fa,"reviveValues",!1),bi.set("iptc",Fa),pi(Si,"iptc",[[0,"ApplicationRecordVersion"],[3,"ObjectTypeReference"],[4,"ObjectAttributeReference"],[5,"ObjectName"],[7,"EditStatus"],[8,"EditorialUpdate"],[10,"Urgency"],[12,"SubjectReference"],[15,"Category"],[20,"SupplementalCategories"],[22,"FixtureIdentifier"],[25,"Keywords"],[26,"ContentLocationCode"],[27,"ContentLocationName"],[30,"ReleaseDate"],[35,"ReleaseTime"],[37,"ExpirationDate"],[38,"ExpirationTime"],[40,"SpecialInstructions"],[42,"ActionAdvised"],[45,"ReferenceService"],[47,"ReferenceDate"],[50,"ReferenceNumber"],[55,"DateCreated"],[60,"TimeCreated"],[62,"DigitalCreationDate"],[63,"DigitalCreationTime"],[65,"OriginatingProgram"],[70,"ProgramVersion"],[75,"ObjectCycle"],[80,"Byline"],[85,"BylineTitle"],[90,"City"],[92,"Sublocation"],[95,"State"],[100,"CountryCode"],[101,"Country"],[103,"OriginalTransmissionReference"],[105,"Headline"],[110,"Credit"],[115,"Source"],[116,"CopyrightNotice"],[118,"Contact"],[120,"Caption"],[121,"LocalCaption"],[122,"Writer"],[125,"RasterizedCaption"],[130,"ImageType"],[131,"ImageOrientation"],[135,"LanguageIdentifier"],[150,"AudioType"],[151,"AudioSamplingRate"],[152,"AudioSamplingResolution"],[153,"AudioDuration"],[154,"AudioOutcue"],[184,"JobID"],[185,"MasterDocumentID"],[186,"ShortDocumentID"],[187,"UniqueDocumentID"],[188,"OwnerID"],[200,"ObjectPreviewFileFormat"],[201,"ObjectPreviewFileVersion"],[202,"ObjectPreviewData"],[221,"Prefs"],[225,"ClassifyState"],[228,"SimilarityIndex"],[230,"DocumentNotes"],[231,"DocumentHistory"],[232,"ExifCameraInfo"],[255,"CatalogSets"]]),pi(Wi,"iptc",[[10,{0:"0 (reserved)",1:"1 (most urgent)",2:"2",3:"3",4:"4",5:"5 (normal urgency)",6:"6",7:"7",8:"8 (least urgent)",9:"9 (user-defined priority)"}],[75,{a:"Morning",b:"Both Morning and Evening",p:"Evening"}],[131,{L:"Landscape",P:"Portrait",S:"Square"}]]);var js=Wu;var Xl=po(mo());var Wl=["DateTimeOriginal","ExposureTime","FNumber","Flash","FocalLengthIn35mmFormat","ISO","LensMake","LensModel","Make","Model"];var es=class{async uploadPhotos(Gr){let Wr=new FormData(Gr),Kr=Wr.getAll("files")??[],Yr=Wr.get("parseExif")==="on";if(Kr.length>10){alert("You can only upload 10 photos at a time");return}let Zr=Kr.map(async tn=>{let ln,dn,yn;try{if(ln=await Ma(tn),ln===null||typeof ln!="string"){console.error("File data URL is not a string:",ln),alert("Error reading file.");return}}catch(An){console.error("Error reading file as Data URL:",An),alert("Error reading file.");return}if(Yr)try{let An=await js.parse(tn,{pick:Wl});console.log("EXIF tags:",await js.parse(tn)),dn=$u(An)}catch(An){console.error("Error reading EXIF data:",An)}try{yn=await to(ln,{width:2e3,height:2e3,maxSize:1e3*1e3,mode:"contain"})}catch(An){console.error("Error resizing image:",An),alert("Error resizing image.");return}let wn=eo(yn.path),kn=new FormData;kn.append("file",wn,tn.name),kn.append("width",String(yn.width)),kn.append("height",String(yn.height)),dn&&kn.append("exif",JSON.stringify(dn)),Xl.default.ajax("POST","/actions/photo",{swap:"afterbegin",target:"#image-preview",values:Object.fromEntries(kn),source:Gr})});await Promise.all(Zr),Gr.querySelector("input[type='file']").value=""}},Yu=1e6;function $u(Qr,Gr=Yu){let Wr={};for(let[Kr,Yr]of Object.entries(Qr)){let Zr=Kr[0].toLowerCase()+Kr.slice(1);typeof Yr=="number"?Wr[Zr]=Math.round(Yr*Gr):Array.isArray(Yr)?Wr[Zr]=Yr.map(tn=>typeof tn=="number"?Math.round(tn*Gr):tn):Yr instanceof Date?Wr[Zr]=Yr.toISOString():typeof Yr=="string"||typeof Yr=="boolean"?Wr[Zr]=Yr:Yr===void 0?Wr[Zr]=void 0:Wr[Zr]=String(Yr)}return Wr}var Ql=new Ro({layoutMode:"justified"});Ql.init();zs.default.onLoad(function(Qr){if(No.maybeInitForElement(Qr),Qr.id==="gallery-sort-dialog"){let Gr=Qr.querySelectorAll(".sortable");for(let Wr of Array.from(Gr))new El(Wr,{animation:150})}});Vs.default.browserInit();var Zi=globalThis;Zi.htmx=Zi.htmx??zs.default;Zi._hyperscript=Zi._hyperscript??Vs.default;Zi.Grain=Zi.Grain??{};Zi.Grain.uploadPage=new es;Zi.Grain.profileDialog=new Lo;Zi.Grain.galleryLayout=Ql;Zi.Grain.photoManip=Ts;
17
+
/*! Bundled license information:
18
+
19
+
sortablejs/modular/sortable.esm.js:
20
+
(**!
21
+
* Sortable 1.15.6
22
+
* @author RubaXa <trash@rubaxa.org>
23
+
* @author owenm <owen23355@gmail.com>
24
+
* @license MIT
25
+
*)
26
+
*/
-170
static/masonry.js
-170
static/masonry.js
···
1
-
// deno-lint-ignore-file
2
-
3
-
let masonryObserverInitialized = false;
4
-
let layoutMode = "justified";
5
-
6
-
function computeLayout() {
7
-
if (layoutMode === "masonry") {
8
-
computeMasonry();
9
-
} else {
10
-
computeJustified();
11
-
}
12
-
}
13
-
14
-
function toggleLayout(layout = "justified") {
15
-
layoutMode = layout;
16
-
computeLayout();
17
-
}
18
-
19
-
function computeMasonry() {
20
-
const container = document.getElementById("masonry-container");
21
-
if (!container) return;
22
-
23
-
const spacing = 8;
24
-
const containerWidth = container.offsetWidth;
25
-
26
-
if (containerWidth === 0) {
27
-
requestAnimationFrame(computeMasonry);
28
-
return;
29
-
}
30
-
31
-
const columns = containerWidth < 640 ? 1 : 3;
32
-
33
-
const columnWidth = (containerWidth + spacing) / columns - spacing;
34
-
const columnHeights = new Array(columns).fill(0);
35
-
const tiles = container.querySelectorAll(".masonry-tile");
36
-
37
-
tiles.forEach((tile) => {
38
-
const imgW = parseFloat(tile.dataset.width);
39
-
const imgH = parseFloat(tile.dataset.height);
40
-
if (!imgW || !imgH) return;
41
-
42
-
const aspectRatio = imgH / imgW;
43
-
const renderedHeight = aspectRatio * columnWidth;
44
-
45
-
let shortestIndex = 0;
46
-
for (let i = 1; i < columns; i++) {
47
-
if (columnHeights[i] < columnHeights[shortestIndex]) {
48
-
shortestIndex = i;
49
-
}
50
-
}
51
-
52
-
const left = (columnWidth + spacing) * shortestIndex;
53
-
const top = columnHeights[shortestIndex];
54
-
55
-
Object.assign(tile.style, {
56
-
position: "absolute",
57
-
width: `${columnWidth}px`,
58
-
height: `${renderedHeight}px`,
59
-
left: `${left}px`,
60
-
top: `${top}px`,
61
-
});
62
-
63
-
columnHeights[shortestIndex] = top + renderedHeight + spacing;
64
-
});
65
-
66
-
container.style.height = `${Math.max(...columnHeights)}px`;
67
-
}
68
-
69
-
function computeJustified() {
70
-
const container = document.getElementById("masonry-container");
71
-
if (!container) return;
72
-
73
-
const spacing = 8;
74
-
const containerWidth = container.offsetWidth;
75
-
76
-
if (containerWidth === 0) {
77
-
requestAnimationFrame(computeJustified);
78
-
return;
79
-
}
80
-
81
-
const tiles = Array.from(container.querySelectorAll(".masonry-tile"));
82
-
let currentRow = [];
83
-
let rowAspectRatioSum = 0;
84
-
let yOffset = 0;
85
-
86
-
// Clear all styles before layout
87
-
tiles.forEach((tile) => {
88
-
Object.assign(tile.style, {
89
-
position: "absolute",
90
-
left: "0px",
91
-
top: "0px",
92
-
width: "auto",
93
-
height: "auto",
94
-
});
95
-
});
96
-
97
-
for (let i = 0; i < tiles.length; i++) {
98
-
const tile = tiles[i];
99
-
const imgW = parseFloat(tile.dataset.width);
100
-
const imgH = parseFloat(tile.dataset.height);
101
-
if (!imgW || !imgH) continue;
102
-
103
-
const aspectRatio = imgW / imgH;
104
-
currentRow.push({ tile, aspectRatio, imgW, imgH });
105
-
rowAspectRatioSum += aspectRatio;
106
-
107
-
// Estimate if row is "full" enough
108
-
const estimatedRowHeight =
109
-
(containerWidth - (currentRow.length - 1) * spacing) / rowAspectRatioSum;
110
-
111
-
// If height is reasonable or we're at the end, render the row
112
-
if (estimatedRowHeight < 300 || i === tiles.length - 1) {
113
-
let xOffset = 0;
114
-
115
-
for (const item of currentRow) {
116
-
const width = estimatedRowHeight * item.aspectRatio;
117
-
Object.assign(item.tile.style, {
118
-
position: "absolute",
119
-
top: `${yOffset}px`,
120
-
left: `${xOffset}px`,
121
-
width: `${width}px`,
122
-
height: `${estimatedRowHeight}px`,
123
-
});
124
-
xOffset += width + spacing;
125
-
}
126
-
127
-
yOffset += estimatedRowHeight + spacing;
128
-
currentRow = [];
129
-
rowAspectRatioSum = 0;
130
-
}
131
-
}
132
-
133
-
container.style.position = "relative";
134
-
container.style.height = `${yOffset}px`;
135
-
}
136
-
137
-
function observeMasonry() {
138
-
if (masonryObserverInitialized) return;
139
-
masonryObserverInitialized = true;
140
-
141
-
const container = document.getElementById("masonry-container");
142
-
if (!container) return;
143
-
144
-
// Observe parent resize
145
-
if (typeof ResizeObserver !== "undefined") {
146
-
const resizeObserver = new ResizeObserver(() => computeLayout());
147
-
if (container.parentElement) {
148
-
resizeObserver.observe(container.parentElement);
149
-
}
150
-
}
151
-
152
-
// Observe inner content changes (tiles being added/removed)
153
-
const mutationObserver = new MutationObserver(() => {
154
-
computeLayout();
155
-
});
156
-
157
-
mutationObserver.observe(container, {
158
-
childList: true,
159
-
subtree: true,
160
-
});
161
-
}
162
-
163
-
document.addEventListener("DOMContentLoaded", () => {
164
-
computeMasonry();
165
-
observeMasonry();
166
-
});
167
-
168
-
window.Grain = window.Grain || {};
169
-
window.Grain.toggleLayout = toggleLayout;
170
-
window.Grain.computeLayout = computeLayout;
-31
static/photo_dialog.js
-31
static/photo_dialog.js
···
1
-
let startX = 0;
2
-
const threshold = 50;
3
-
const onTouchStart = (e) => {
4
-
startX = e.touches[0].clientX;
5
-
};
6
-
const onTouchEnd = (e) => {
7
-
const endX = e.changedTouches[0].clientX;
8
-
const diffX = endX - startX;
9
-
10
-
if (Math.abs(diffX) > threshold) {
11
-
const direction = diffX > 0 ? "swiperight" : "swipeleft";
12
-
e.target.dispatchEvent(new CustomEvent(direction, { bubbles: true }));
13
-
}
14
-
};
15
-
const observer = new MutationObserver(() => {
16
-
const modal = document.getElementById("photo-dialog");
17
-
if (!modal) {
18
-
console.log("Photo Dialog not found, removing event listeners");
19
-
document.body.removeEventListener("touchstart", onTouchStart);
20
-
document.body.removeEventListener("touchend", onTouchEnd);
21
-
observer.disconnect();
22
-
}
23
-
});
24
-
htmx.onLoad((evt) => {
25
-
if (evt.id === "photo-dialog") {
26
-
document.body.addEventListener("touchstart", onTouchStart);
27
-
document.body.addEventListener("touchend", onTouchEnd);
28
-
}
29
-
const parent = document.body;
30
-
observer.observe(parent, { childList: true, subtree: true });
31
-
});
+47
-14
static/photo_manip.js
src/static/photo_manip.ts
+47
-14
static/photo_manip.js
src/static/photo_manip.ts
···
1
-
// deno-lint-ignore-file no-window
2
-
function readFileAsDataURL(file) {
1
+
type ResizeOptions = {
2
+
width: number;
3
+
height: number;
4
+
quality: number;
5
+
mode: "cover" | "contain" | "stretch";
6
+
};
7
+
8
+
type ResizeResult = {
9
+
dataUrl: string;
10
+
width: number;
11
+
height: number;
12
+
};
13
+
14
+
type DoResizeOptions = {
15
+
width: number;
16
+
height: number;
17
+
maxSize: number;
18
+
mode: "cover" | "contain" | "stretch";
19
+
};
20
+
21
+
type DoResizeResult = {
22
+
path: string;
23
+
mime: string;
24
+
size: number;
25
+
width: number;
26
+
height: number;
27
+
};
28
+
29
+
export function readFileAsDataURL(
30
+
file: File,
31
+
): Promise<string | ArrayBuffer | null> {
3
32
return new Promise((resolve, reject) => {
4
33
const reader = new FileReader();
5
34
reader.onload = () => resolve(reader.result);
···
8
37
});
9
38
}
10
39
11
-
function dataURLToBlob(dataUrl) {
40
+
export function dataURLToBlob(dataUrl: string): Blob {
12
41
const [meta, base64] = dataUrl.split(",");
13
-
const mime = meta.match(/:(.*?);/)[1];
42
+
// Use RegExp.exec instead of match for type safety
43
+
const mimeMatch = /:(.*?);/.exec(meta);
44
+
if (!mimeMatch) throw new Error("Invalid data URL");
45
+
const mime = mimeMatch[1];
14
46
const binary = atob(base64);
15
47
const array = new Uint8Array(binary.length);
16
48
for (let i = 0; i < binary.length; i++) {
···
19
51
return new Blob([array], { type: mime });
20
52
}
21
53
22
-
function getDataUriSize(dataUri) {
54
+
function getDataUriSize(dataUri: string): number {
23
55
const base64 = dataUri.split(",")[1];
24
56
return Math.ceil((base64.length * 3) / 4);
25
57
}
26
58
27
-
function createResizedImage(dataUri, options) {
59
+
function createResizedImage(
60
+
dataUri: string,
61
+
options: ResizeOptions,
62
+
): Promise<ResizeResult> {
28
63
return new Promise((resolve, reject) => {
29
64
const img = new Image();
30
65
img.onload = () => {
31
-
let scale = 1;
66
+
let scale: number;
32
67
if (options.mode === "cover") {
33
68
scale = Math.max(
34
69
options.width / img.width,
···
70
105
});
71
106
}
72
107
73
-
async function doResize(dataUri, opts) {
74
-
let bestResult = null;
108
+
export async function doResize(
109
+
dataUri: string,
110
+
opts: DoResizeOptions,
111
+
): Promise<DoResizeResult> {
112
+
let bestResult: ResizeResult | null = null;
75
113
let minQuality = 0;
76
114
let maxQuality = 101;
77
115
···
106
144
height: bestResult.height,
107
145
};
108
146
}
109
-
110
-
window.Grain = window.Grain || {};
111
-
window.Grain.readFileAsDataURL = readFileAsDataURL;
112
-
window.Grain.dataURLToBlob = dataURLToBlob;
113
-
window.Grain.doResize = doResize;
-55
static/profile_dialog.js
-55
static/profile_dialog.js
···
1
-
// deno-lint-ignore-file no-window
2
-
3
-
function handleAvatarImageSelect(fileInput) {
4
-
if (fileInput.files.length > 0) {
5
-
const file = fileInput.files[0];
6
-
Grain.readFileAsDataURL(file).then((dataUrl) => {
7
-
const previewImg = document.createElement("img");
8
-
previewImg.src = dataUrl;
9
-
previewImg.className = "rounded-full w-full h-full object-cover";
10
-
previewImg.alt = "Avatar preview";
11
-
12
-
const imagePreview = fileInput.closest("form").querySelector(
13
-
"#image-preview",
14
-
);
15
-
if (imagePreview) {
16
-
imagePreview.innerHTML = "";
17
-
imagePreview.appendChild(previewImg);
18
-
}
19
-
});
20
-
}
21
-
}
22
-
23
-
async function updateProfile(formElement) {
24
-
const formData = new FormData(formElement);
25
-
26
-
const avatarFile = formData.get("file");
27
-
if (avatarFile && avatarFile.type.startsWith("image/")) {
28
-
try {
29
-
const dataUrl = await Grain.readFileAsDataURL(file);
30
-
31
-
const resized = await Grain.doResize(dataUrl, {
32
-
width: 2000,
33
-
height: 2000,
34
-
maxSize: 1000 * 1000, // 1MB
35
-
mode: "contain",
36
-
});
37
-
38
-
const blob = Grain.dataURLToBlob(resized.path);
39
-
formData.set("file", blob, avatarFile.name);
40
-
} catch (err) {
41
-
console.error("Error resizing image:", err);
42
-
formData.delete("file");
43
-
}
44
-
}
45
-
46
-
htmx.ajax("POST", "/actions/profile/update", {
47
-
"swap": "none",
48
-
"values": Object.fromEntries(formData),
49
-
"source": formElement,
50
-
});
51
-
}
52
-
53
-
window.Grain = window.Grain || {};
54
-
window.Grain.handleAvatarImageSelect = handleAvatarImageSelect;
55
-
window.Grain.updateProfile = updateProfile;
-8
static/sortable.js
-8
static/sortable.js
+277
-1
static/styles.css
+277
-1
static/styles.css
···
1
-
/*! tailwindcss v4.1.7 | MIT License | https://tailwindcss.com */
1
+
/*! tailwindcss v4.1.8 | MIT License | https://tailwindcss.com */
2
2
@layer properties;
3
3
@layer theme, base, components, utilities;
4
4
@layer theme {
···
8
8
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
9
9
"Courier New", monospace;
10
10
--color-sky-500: oklch(68.5% 0.169 237.323);
11
+
--color-sky-600: oklch(58.8% 0.158 241.966);
11
12
--color-zinc-50: oklch(98.5% 0 0);
12
13
--color-zinc-100: oklch(96.7% 0.001 286.375);
13
14
--color-zinc-200: oklch(92% 0.004 286.32);
···
32
33
--text-xl--line-height: calc(1.75 / 1.25);
33
34
--text-2xl: 1.5rem;
34
35
--text-2xl--line-height: calc(2 / 1.5);
36
+
--text-3xl: 1.875rem;
37
+
--text-3xl--line-height: calc(2.25 / 1.875);
35
38
--text-4xl: 2.25rem;
36
39
--text-4xl--line-height: calc(2.5 / 2.25);
37
40
--font-weight-medium: 500;
38
41
--font-weight-semibold: 600;
39
42
--font-weight-bold: 700;
43
+
--ease-in: cubic-bezier(0.4, 0, 1, 1);
44
+
--default-transition-duration: 150ms;
45
+
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
40
46
--default-font-family: var(--font-sans);
41
47
--default-mono-font-family: var(--font-mono);
42
48
}
···
187
193
}
188
194
}
189
195
@layer utilities {
196
+
.invisible {
197
+
visibility: hidden;
198
+
}
199
+
.visible {
200
+
visibility: visible;
201
+
}
190
202
.sr-only {
191
203
position: absolute;
192
204
width: 1px;
···
261
273
.z-100 {
262
274
z-index: 100;
263
275
}
276
+
.z-101 {
277
+
z-index: 101;
278
+
}
264
279
.container {
265
280
width: 100%;
266
281
@media (width >= 40rem) {
···
279
294
max-width: 96rem;
280
295
}
281
296
}
297
+
.-m-1 {
298
+
margin: calc(var(--spacing) * -1);
299
+
}
300
+
.-m-2 {
301
+
margin: calc(var(--spacing) * -2);
302
+
}
303
+
.mx-1 {
304
+
margin-inline: calc(var(--spacing) * 1);
305
+
}
306
+
.mx-2 {
307
+
margin-inline: calc(var(--spacing) * 2);
308
+
}
282
309
.mx-auto {
283
310
margin-inline: auto;
284
311
}
312
+
.my-1 {
313
+
margin-block: calc(var(--spacing) * 1);
314
+
}
285
315
.my-2 {
286
316
margin-block: calc(var(--spacing) * 2);
287
317
}
288
318
.my-4 {
289
319
margin-block: calc(var(--spacing) * 4);
320
+
}
321
+
.my-10 {
322
+
margin-block: calc(var(--spacing) * 10);
290
323
}
291
324
.my-30 {
292
325
margin-block: calc(var(--spacing) * 30);
293
326
}
327
+
.mt-1 {
328
+
margin-top: calc(var(--spacing) * 1);
329
+
}
294
330
.mt-2 {
295
331
margin-top: calc(var(--spacing) * 2);
296
332
}
···
303
339
.mr-2 {
304
340
margin-right: calc(var(--spacing) * 2);
305
341
}
342
+
.mb-1 {
343
+
margin-bottom: calc(var(--spacing) * 1);
344
+
}
306
345
.mb-2 {
307
346
margin-bottom: calc(var(--spacing) * 2);
308
347
}
309
348
.mb-4 {
310
349
margin-bottom: calc(var(--spacing) * 4);
311
350
}
351
+
.mb-6 {
352
+
margin-bottom: calc(var(--spacing) * 6);
353
+
}
354
+
.mb-8 {
355
+
margin-bottom: calc(var(--spacing) * 8);
356
+
}
357
+
.ml-0 {
358
+
margin-left: calc(var(--spacing) * 0);
359
+
}
312
360
.block {
313
361
display: block;
314
362
}
···
329
377
}
330
378
.inline-flex {
331
379
display: inline-flex;
380
+
}
381
+
.table {
382
+
display: table;
332
383
}
333
384
.aspect-\[3\/2\] {
334
385
aspect-ratio: 3/2;
···
370
421
.h-full {
371
422
height: 100%;
372
423
}
424
+
.max-h-\[calc\(100vh-100px\)\] {
425
+
max-height: calc(100vh - 100px);
426
+
}
373
427
.min-h-screen {
374
428
min-height: 100vh;
375
429
}
···
424
478
.min-w-0 {
425
479
min-width: calc(var(--spacing) * 0);
426
480
}
481
+
.min-w-\[120px\] {
482
+
min-width: 120px;
483
+
}
427
484
.flex-1 {
428
485
flex: 1;
429
486
}
···
453
510
}
454
511
.flex-col {
455
512
flex-direction: column;
513
+
}
514
+
.flex-row {
515
+
flex-direction: row;
456
516
}
457
517
.flex-wrap {
458
518
flex-wrap: wrap;
···
463
523
.items-center {
464
524
align-items: center;
465
525
}
526
+
.items-start {
527
+
align-items: flex-start;
528
+
}
466
529
.justify-between {
467
530
justify-content: space-between;
468
531
}
···
498
561
.gap-x-4 {
499
562
column-gap: calc(var(--spacing) * 4);
500
563
}
564
+
.space-x-1 {
565
+
:where(& > :not(:last-child)) {
566
+
--tw-space-x-reverse: 0;
567
+
margin-inline-start: calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));
568
+
margin-inline-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)));
569
+
}
570
+
}
501
571
.space-x-2 {
502
572
:where(& > :not(:last-child)) {
503
573
--tw-space-x-reverse: 0;
···
529
599
border-color: var(--color-zinc-200);
530
600
}
531
601
}
602
+
.self-end {
603
+
align-self: flex-end;
604
+
}
532
605
.self-start {
533
606
align-self: flex-start;
534
607
}
···
540
613
.overflow-hidden {
541
614
overflow: hidden;
542
615
}
616
+
.overflow-x-auto {
617
+
overflow-x: auto;
618
+
}
619
+
.rounded {
620
+
border-radius: 0.25rem;
621
+
}
543
622
.rounded-full {
544
623
border-radius: calc(infinity * 1px);
545
624
}
···
572
651
.bg-sky-500 {
573
652
background-color: var(--color-sky-500);
574
653
}
654
+
.bg-transparent {
655
+
background-color: transparent;
656
+
}
575
657
.bg-white {
576
658
background-color: var(--color-white);
577
659
}
···
587
669
.bg-zinc-950 {
588
670
background-color: var(--color-zinc-950);
589
671
}
672
+
.bg-zinc-950\/70 {
673
+
background-color: color-mix(in srgb, oklch(14.1% 0.005 285.823) 70%, transparent);
674
+
@supports (color: color-mix(in lab, red, red)) {
675
+
background-color: color-mix(in oklab, var(--color-zinc-950) 70%, transparent);
676
+
}
677
+
}
590
678
.fill-zinc-950 {
591
679
fill: var(--color-zinc-950);
592
680
}
···
602
690
.p-4 {
603
691
padding: calc(var(--spacing) * 4);
604
692
}
693
+
.px-1 {
694
+
padding-inline: calc(var(--spacing) * 1);
695
+
}
696
+
.px-2 {
697
+
padding-inline: calc(var(--spacing) * 2);
698
+
}
605
699
.px-4 {
606
700
padding-inline: calc(var(--spacing) * 4);
607
701
}
608
702
.px-\[3px\] {
609
703
padding-inline: 3px;
610
704
}
705
+
.py-1 {
706
+
padding-block: calc(var(--spacing) * 1);
707
+
}
611
708
.py-2 {
612
709
padding-block: calc(var(--spacing) * 2);
613
710
}
···
641
738
.font-\[\'Jersey_20\'\] {
642
739
font-family: 'Jersey 20';
643
740
}
741
+
.font-mono {
742
+
font-family: var(--font-mono);
743
+
}
644
744
.text-2xl {
645
745
font-size: var(--text-2xl);
646
746
line-height: var(--tw-leading, var(--text-2xl--line-height));
647
747
}
748
+
.text-3xl {
749
+
font-size: var(--text-3xl);
750
+
line-height: var(--tw-leading, var(--text-3xl--line-height));
751
+
}
648
752
.text-4xl {
649
753
font-size: var(--text-4xl);
650
754
line-height: var(--tw-leading, var(--text-4xl--line-height));
···
652
756
.text-sm {
653
757
font-size: var(--text-sm);
654
758
line-height: var(--tw-leading, var(--text-sm--line-height));
759
+
}
760
+
.text-sm\! {
761
+
font-size: var(--text-sm) !important;
762
+
line-height: var(--tw-leading, var(--text-sm--line-height)) !important;
655
763
}
656
764
.text-xl {
657
765
font-size: var(--text-xl);
···
691
799
.text-white {
692
800
color: var(--color-white);
693
801
}
802
+
.text-zinc-50 {
803
+
color: var(--color-zinc-50);
804
+
}
805
+
.text-zinc-500 {
806
+
color: var(--color-zinc-500);
807
+
}
694
808
.text-zinc-600 {
695
809
color: var(--color-zinc-600);
696
810
}
697
811
.text-zinc-700 {
698
812
color: var(--color-zinc-700);
699
813
}
814
+
.text-zinc-800 {
815
+
color: var(--color-zinc-800);
816
+
}
700
817
.text-zinc-900 {
701
818
color: var(--color-zinc-900);
702
819
}
···
706
823
.lowercase {
707
824
text-transform: lowercase;
708
825
}
826
+
.underline {
827
+
text-decoration-line: underline;
828
+
}
829
+
.accent-sky-600 {
830
+
accent-color: var(--color-sky-600);
831
+
}
832
+
.shadow {
833
+
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
834
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
835
+
}
709
836
.ring-sky-500 {
710
837
--tw-ring-color: var(--color-sky-500);
711
838
}
839
+
.invert {
840
+
--tw-invert: invert(100%);
841
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
842
+
}
843
+
.filter {
844
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
845
+
}
846
+
.transition {
847
+
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
848
+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
849
+
transition-duration: var(--tw-duration, var(--default-transition-duration));
850
+
}
851
+
.ease-in {
852
+
--tw-ease: var(--ease-in);
853
+
transition-timing-function: var(--ease-in);
854
+
}
712
855
.group-data-\[added\=true\]\:block {
713
856
&:is(:where(.group)[data-added="true"] *) {
714
857
display: block;
858
+
}
859
+
}
860
+
.hover\:no-underline {
861
+
&:hover {
862
+
@media (hover: hover) {
863
+
text-decoration-line: none;
864
+
}
715
865
}
716
866
}
717
867
.hover\:underline {
···
750
900
.sm\:fixed {
751
901
@media (width >= 40rem) {
752
902
position: fixed;
903
+
}
904
+
}
905
+
.sm\:static {
906
+
@media (width >= 40rem) {
907
+
position: static;
753
908
}
754
909
}
755
910
.sm\:top-0 {
···
777
932
left: calc(var(--spacing) * 0);
778
933
}
779
934
}
935
+
.sm\:mt-0 {
936
+
@media (width >= 40rem) {
937
+
margin-top: calc(var(--spacing) * 0);
938
+
}
939
+
}
940
+
.sm\:ml-1 {
941
+
@media (width >= 40rem) {
942
+
margin-left: calc(var(--spacing) * 1);
943
+
}
944
+
}
945
+
.sm\:ml-2 {
946
+
@media (width >= 40rem) {
947
+
margin-left: calc(var(--spacing) * 2);
948
+
}
949
+
}
950
+
.sm\:block {
951
+
@media (width >= 40rem) {
952
+
display: block;
953
+
}
954
+
}
955
+
.sm\:hidden {
956
+
@media (width >= 40rem) {
957
+
display: none;
958
+
}
959
+
}
780
960
.sm\:h-screen {
781
961
@media (width >= 40rem) {
782
962
height: 100vh;
···
802
982
max-width: 500px;
803
983
}
804
984
}
985
+
.sm\:min-w-\[120px\] {
986
+
@media (width >= 40rem) {
987
+
min-width: 120px;
988
+
}
989
+
}
805
990
.sm\:grid-cols-3 {
806
991
@media (width >= 40rem) {
807
992
grid-template-columns: repeat(3, minmax(0, 1fr));
···
825
1010
.sm\:items-center {
826
1011
@media (width >= 40rem) {
827
1012
align-items: center;
1013
+
}
1014
+
}
1015
+
.sm\:items-end {
1016
+
@media (width >= 40rem) {
1017
+
align-items: flex-end;
828
1018
}
829
1019
}
830
1020
.sm\:justify-between {
···
837
1027
justify-content: flex-end;
838
1028
}
839
1029
}
1030
+
.sm\:gap-0 {
1031
+
@media (width >= 40rem) {
1032
+
gap: calc(var(--spacing) * 0);
1033
+
}
1034
+
}
840
1035
.sm\:space-x-2 {
841
1036
@media (width >= 40rem) {
842
1037
:where(& > :not(:last-child)) {
···
869
1064
}
870
1065
}
871
1066
}
1067
+
.dark\:border-zinc-700 {
1068
+
@media (prefers-color-scheme: dark) {
1069
+
border-color: var(--color-zinc-700);
1070
+
}
1071
+
}
872
1072
.dark\:border-zinc-800 {
873
1073
@media (prefers-color-scheme: dark) {
874
1074
border-color: var(--color-zinc-800);
···
902
1102
.dark\:text-zinc-50 {
903
1103
@media (prefers-color-scheme: dark) {
904
1104
color: var(--color-zinc-50);
1105
+
}
1106
+
}
1107
+
.dark\:text-zinc-100 {
1108
+
@media (prefers-color-scheme: dark) {
1109
+
color: var(--color-zinc-100);
905
1110
}
906
1111
}
907
1112
.dark\:text-zinc-300 {
···
1024
1229
inherits: false;
1025
1230
initial-value: 0 0 #0000;
1026
1231
}
1232
+
@property --tw-blur {
1233
+
syntax: "*";
1234
+
inherits: false;
1235
+
}
1236
+
@property --tw-brightness {
1237
+
syntax: "*";
1238
+
inherits: false;
1239
+
}
1240
+
@property --tw-contrast {
1241
+
syntax: "*";
1242
+
inherits: false;
1243
+
}
1244
+
@property --tw-grayscale {
1245
+
syntax: "*";
1246
+
inherits: false;
1247
+
}
1248
+
@property --tw-hue-rotate {
1249
+
syntax: "*";
1250
+
inherits: false;
1251
+
}
1252
+
@property --tw-invert {
1253
+
syntax: "*";
1254
+
inherits: false;
1255
+
}
1256
+
@property --tw-opacity {
1257
+
syntax: "*";
1258
+
inherits: false;
1259
+
}
1260
+
@property --tw-saturate {
1261
+
syntax: "*";
1262
+
inherits: false;
1263
+
}
1264
+
@property --tw-sepia {
1265
+
syntax: "*";
1266
+
inherits: false;
1267
+
}
1268
+
@property --tw-drop-shadow {
1269
+
syntax: "*";
1270
+
inherits: false;
1271
+
}
1272
+
@property --tw-drop-shadow-color {
1273
+
syntax: "*";
1274
+
inherits: false;
1275
+
}
1276
+
@property --tw-drop-shadow-alpha {
1277
+
syntax: "<percentage>";
1278
+
inherits: false;
1279
+
initial-value: 100%;
1280
+
}
1281
+
@property --tw-drop-shadow-size {
1282
+
syntax: "*";
1283
+
inherits: false;
1284
+
}
1285
+
@property --tw-ease {
1286
+
syntax: "*";
1287
+
inherits: false;
1288
+
}
1027
1289
@layer properties {
1028
1290
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
1029
1291
*, ::before, ::after, ::backdrop {
···
1051
1313
--tw-ring-offset-width: 0px;
1052
1314
--tw-ring-offset-color: #fff;
1053
1315
--tw-ring-offset-shadow: 0 0 #0000;
1316
+
--tw-blur: initial;
1317
+
--tw-brightness: initial;
1318
+
--tw-contrast: initial;
1319
+
--tw-grayscale: initial;
1320
+
--tw-hue-rotate: initial;
1321
+
--tw-invert: initial;
1322
+
--tw-opacity: initial;
1323
+
--tw-saturate: initial;
1324
+
--tw-sepia: initial;
1325
+
--tw-drop-shadow: initial;
1326
+
--tw-drop-shadow-color: initial;
1327
+
--tw-drop-shadow-alpha: 100%;
1328
+
--tw-drop-shadow-size: initial;
1329
+
--tw-ease: initial;
1054
1330
}
1055
1331
}
1056
1332
}
-106
static/upload_page.js
-106
static/upload_page.js
···
1
-
// deno-lint-ignore-file no-window
2
-
3
-
async function uploadPhotos(inputElement) {
4
-
const fileList = Array.from(inputElement.files);
5
-
6
-
if (fileList.length > 10) {
7
-
alert("You can only upload 10 photos at a time");
8
-
return;
9
-
}
10
-
11
-
const preview = document.querySelector("#image-preview");
12
-
13
-
const uploadPromises = fileList.map(async (file) => {
14
-
let dataUrl = "";
15
-
let exif;
16
-
let resized;
17
-
18
-
try {
19
-
dataUrl = await Grain.readFileAsDataURL(file);
20
-
} catch (err) {
21
-
console.error("Error reading file as Data URL:", err);
22
-
alert("Error reading file.");
23
-
return;
24
-
}
25
-
26
-
try {
27
-
const rawExif = await window.exifr.parse(file, { gps: false });
28
-
exif = normalizeExif(rawExif);
29
-
} catch (err) {
30
-
console.error("Error reading EXIF data:", err);
31
-
}
32
-
33
-
try {
34
-
resized = await Grain.doResize(dataUrl, {
35
-
width: 2000,
36
-
height: 2000,
37
-
maxSize: 1000 * 1000, // 1MB
38
-
mode: "contain",
39
-
});
40
-
} catch (err) {
41
-
console.error("Error resizing image:", err);
42
-
alert("Error resizing image.");
43
-
return;
44
-
}
45
-
46
-
const blob = Grain.dataURLToBlob(resized.path);
47
-
48
-
const fd = new FormData();
49
-
fd.append("file", blob, file.name);
50
-
fd.append("width", resized.width);
51
-
fd.append("height", resized.height);
52
-
fd.append("exif", JSON.stringify(exif));
53
-
54
-
try {
55
-
const response = await fetch("/actions/photo/upload", {
56
-
method: "POST",
57
-
body: fd,
58
-
});
59
-
60
-
if (!response.ok) {
61
-
alert(await response.text());
62
-
return;
63
-
}
64
-
65
-
const html = await response.text();
66
-
const temp = document.createElement("div");
67
-
temp.innerHTML = html;
68
-
preview.insertBefore(temp.firstElementChild, preview.firstChild);
69
-
htmx.process(preview);
70
-
} catch (err) {
71
-
console.error("Error uploading photo:", err);
72
-
alert("Error uploading photo");
73
-
}
74
-
});
75
-
76
-
await Promise.all(uploadPromises);
77
-
inputElement.value = "";
78
-
}
79
-
80
-
const SCALE_FACTOR = 1000000;
81
-
82
-
function normalizeExif(
83
-
exif,
84
-
scale = SCALE_FACTOR,
85
-
) {
86
-
const normalized = {};
87
-
88
-
for (const [key, value] of Object.entries(exif)) {
89
-
const camelKey = key[0].toLowerCase() + key.slice(1);
90
-
91
-
if (typeof value === "number") {
92
-
normalized[camelKey] = Math.round(value * scale);
93
-
} else if (Array.isArray(value)) {
94
-
normalized[camelKey] = value.map((v) =>
95
-
typeof v === "number" ? Math.round(v * scale) : v
96
-
);
97
-
} else {
98
-
normalized[camelKey] = value;
99
-
}
100
-
}
101
-
102
-
return normalized;
103
-
}
104
-
105
-
window.Grain = window.Grain || {};
106
-
window.Grain.uploadPhotos = uploadPhotos;