+155
.gitignore
+155
.gitignore
···
1
+
# Logs
2
+
logs
3
+
*.log
4
+
npm-debug.log*
5
+
yarn-debug.log*
6
+
yarn-error.log*
7
+
pnpm-debug.log*
8
+
lerna-debug.log*
9
+
10
+
node_modules
11
+
dist
12
+
dist-ssr
13
+
*.local
14
+
15
+
# Editor directories and files
16
+
.vscode/*
17
+
!.vscode/extensions.json
18
+
.idea
19
+
.DS_Store
20
+
*.suo
21
+
*.ntvs*
22
+
*.njsproj
23
+
*.sln
24
+
*.sw?
25
+
26
+
# Diagnostic reports (https://nodejs.org/api/report.html)
27
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
28
+
29
+
# Runtime data
30
+
pids
31
+
*.pid
32
+
*.seed
33
+
*.pid.lock
34
+
35
+
# Directory for instrumented libs generated by jscoverage/JSCover
36
+
lib-cov
37
+
38
+
# Coverage directory used by tools like istanbul
39
+
coverage
40
+
*.lcov
41
+
42
+
# nyc test coverage
43
+
.nyc_output
44
+
45
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
46
+
.grunt
47
+
48
+
# Bower dependency directory (https://bower.io/)
49
+
bower_components
50
+
51
+
# node-waf configuration
52
+
.lock-wscript
53
+
54
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
55
+
build/Release
56
+
57
+
# Dependency directories
58
+
node_modules/
59
+
jspm_packages/
60
+
61
+
# Snowpack dependency directory (https://snowpack.dev/)
62
+
web_modules/
63
+
64
+
# TypeScript cache
65
+
*.tsbuildinfo
66
+
67
+
# Optional npm cache directory
68
+
.npm
69
+
70
+
# Optional eslint cache
71
+
.eslintcache
72
+
73
+
# Optional stylelint cache
74
+
.stylelintcache
75
+
76
+
# Microbundle cache
77
+
.rpt2_cache/
78
+
.rts2_cache_cjs/
79
+
.rts2_cache_es/
80
+
.rts2_cache_umd/
81
+
82
+
# Optional REPL history
83
+
.node_repl_history
84
+
85
+
# Output of 'npm pack'
86
+
*.tgz
87
+
88
+
# Yarn Integrity file
89
+
.yarn-integrity
90
+
91
+
# dotenv environment variable files
92
+
.env
93
+
.env.development.local
94
+
.env.test.local
95
+
.env.production.local
96
+
.env.local
97
+
98
+
# parcel-bundler cache (https://parceljs.org/)
99
+
.cache
100
+
.parcel-cache
101
+
102
+
# Next.js build output
103
+
.next
104
+
out
105
+
106
+
# Nuxt.js build / generate output
107
+
.nuxt
108
+
dist
109
+
110
+
# Gatsby files
111
+
.cache/
112
+
# Comment in the public line in if your project uses Gatsby and not Next.js
113
+
# https://nextjs.org/blog/next-9-1#public-directory-support
114
+
# public
115
+
116
+
# vuepress build output
117
+
.vuepress/dist
118
+
119
+
# vuepress v2.x temp and cache directory
120
+
.temp
121
+
.cache
122
+
123
+
# vitepress build output
124
+
**/.vitepress/dist
125
+
126
+
# vitepress cache directory
127
+
**/.vitepress/cache
128
+
129
+
# Docusaurus cache and generated files
130
+
.docusaurus
131
+
132
+
# Serverless directories
133
+
.serverless/
134
+
135
+
# FuseBox cache
136
+
.fusebox/
137
+
138
+
# DynamoDB Local files
139
+
.dynamodb/
140
+
141
+
# TernJS port file
142
+
.tern-port
143
+
144
+
# Stores VSCode versions used for testing VSCode extensions
145
+
.vscode-test
146
+
147
+
# yarn v2
148
+
.yarn/cache
149
+
.yarn/unplugged
150
+
.yarn/build-state.yml
151
+
.yarn/install-state.gz
152
+
.pnp.*
153
+
154
+
# Config files
155
+
config.ts
+21
LICENSE
+21
LICENSE
···
1
+
# MIT License
2
+
3
+
Copyright (c) 2025 Witchcraft Systems
4
+
5
+
Permission is hereby granted, free of charge, to any person obtaining a copy
6
+
of this software and associated documentation files (the "Software"), to deal
7
+
in the Software without restriction, including without limitation the rights
8
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+
copies of the Software, and to permit persons to whom the Software is
10
+
furnished to do so, subject to the following conditions:
11
+
12
+
The above copyright notice and this permission notice shall be included in all
13
+
copies or substantial portions of the Software.
14
+
15
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+
SOFTWARE.
+59
README.md
+59
README.md
···
1
+
# pds-dash
2
+
3
+
a frontend dashboard with stats for your ATProto PDS.
4
+
5
+
## setup
6
+
7
+
### prerequisites
8
+
9
+
- [deno](https://deno.com/manual/getting_started/installation)
10
+
11
+
### installing
12
+
13
+
clone the repo, copy `config.ts.example` to `config.ts` and edit it to your liking.
14
+
15
+
then, install dependencies using deno:
16
+
17
+
```sh
18
+
deno install
19
+
```
20
+
21
+
### development server
22
+
23
+
local develompent server with hot reloading:
24
+
25
+
```sh
26
+
deno task dev
27
+
```
28
+
29
+
### building
30
+
31
+
to build the optimized bundle run:
32
+
33
+
```sh
34
+
deno task build
35
+
```
36
+
37
+
the output will be in the `dist/` directory.
38
+
39
+
## deploying
40
+
41
+
we use our own CI/CD workflow at [`.forgejo/workflows/deploy.yaml`](.forgejo/workflows/deploy.yaml), but it boils down to building the project bundle and deploying it to a web server. it'll probably make more sense to host it on the same domain as your PDS, but it doesn't affect anything if you host it somewhere else.
42
+
43
+
## configuring
44
+
45
+
[`config.ts`](config.ts) is the main configuration file, you can find more information in the file itself.
46
+
47
+
## theming
48
+
49
+
themes are located in the `themes/` directory, you can create your own theme by copying one of the existing themes and modifying it to your liking.
50
+
51
+
currently, the name of the theme is determined by the directory name, and the theme itself is defined in `theme.css` inside that directory.
52
+
53
+
you can switch themes by changing the `theme` property in `config.ts`.
54
+
55
+
the favicon is located at [`public/favicon.ico`](public/favicon.ico)
56
+
57
+
## license
58
+
59
+
MIT
+223
bun.lock
+223
bun.lock
···
1
+
{
2
+
"lockfileVersion": 1,
3
+
"workspaces": {
4
+
"": {
5
+
"name": "web",
6
+
"dependencies": {
7
+
"@atcute/bluesky": "^2.0.2",
8
+
"@atcute/client": "^3.0.1",
9
+
"@atcute/identity-resolver": "^0.1.2",
10
+
"moment": "^2.30.1",
11
+
"mutex-ts": "^1.2.1",
12
+
"svelte-infinite-loading": "^1.4.0",
13
+
},
14
+
"devDependencies": {
15
+
"@sveltejs/vite-plugin-svelte": "^5.0.3",
16
+
"@tsconfig/svelte": "^5.0.4",
17
+
"svelte": "^5.23.1",
18
+
"svelte-check": "^4.1.5",
19
+
"typescript": "~5.7.2",
20
+
"vite": "^6.3.1",
21
+
},
22
+
},
23
+
},
24
+
"packages": {
25
+
"@atcute/bluesky": ["@atcute/bluesky@2.1.1", "", { "peerDependencies": { "@atcute/client": "^3.0.0" } }, "sha512-wEZfFW58J6yC1SqHcVJOn4qbHENTTzjeCEWthRT5HvKovADLqk54HSMSAuXDMBUbintSTBr0khQNZQ3ZdgzDdQ=="],
26
+
27
+
"@atcute/client": ["@atcute/client@3.1.0", "", {}, "sha512-+rQPsHXSf0DUm8XoHoaH7Y2E8tIpbsW84djyPj7dqAyrFIjvGuJ1X1DvMufwbTIcmLerdy+dzl34iZcz/h3Vhg=="],
28
+
29
+
"@atcute/identity": ["@atcute/identity@0.1.3", "", { "dependencies": { "@badrap/valita": "^0.4.2" } }, "sha512-ndlD8nypHt8G00wixbozKdSNL0O8HTzBjFGEXeAcBUCXSZPBjRWbqtgyJxhgUWnr7swgxgw1mSbZwRB5b7xCiQ=="],
30
+
31
+
"@atcute/identity-resolver": ["@atcute/identity-resolver@0.1.2", "", { "dependencies": { "@atcute/util-fetch": "^1.0.0", "@badrap/valita": "^0.4.2" }, "peerDependencies": { "@atcute/identity": "^0.1.0" } }, "sha512-fP2VbHD04kVcCdNi/Kszo6jFzqM7Pg3p33oGhfp2zVkwFKaVBlwCaFRWEga/Xvu/IDLwNdASGWnLqoA34SFeSg=="],
32
+
33
+
"@atcute/util-fetch": ["@atcute/util-fetch@1.0.1", "", { "dependencies": { "@badrap/valita": "^0.4.2" } }, "sha512-Clc0E/5ufyGBVfYBUwWNlHONlZCoblSr4Ho50l1LhmRPGB1Wu/AQ9Sz+rsBg7fdaW/auve8ulmwhRhnX2cGRow=="],
34
+
35
+
"@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="],
36
+
37
+
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="],
38
+
39
+
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="],
40
+
41
+
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.9", "", { "os": "android", "cpu": "arm64" }, "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg=="],
42
+
43
+
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.9", "", { "os": "android", "cpu": "x64" }, "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw=="],
44
+
45
+
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg=="],
46
+
47
+
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ=="],
48
+
49
+
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.9", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q=="],
50
+
51
+
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg=="],
52
+
53
+
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.9", "", { "os": "linux", "cpu": "arm" }, "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw=="],
54
+
55
+
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw=="],
56
+
57
+
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.9", "", { "os": "linux", "cpu": "ia32" }, "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A=="],
58
+
59
+
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ=="],
60
+
61
+
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA=="],
62
+
63
+
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w=="],
64
+
65
+
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg=="],
66
+
67
+
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA=="],
68
+
69
+
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.9", "", { "os": "linux", "cpu": "x64" }, "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg=="],
70
+
71
+
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q=="],
72
+
73
+
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.9", "", { "os": "none", "cpu": "x64" }, "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g=="],
74
+
75
+
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.9", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ=="],
76
+
77
+
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.9", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA=="],
78
+
79
+
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg=="],
80
+
81
+
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.9", "", { "os": "sunos", "cpu": "x64" }, "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw=="],
82
+
83
+
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ=="],
84
+
85
+
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.9", "", { "os": "win32", "cpu": "ia32" }, "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww=="],
86
+
87
+
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="],
88
+
89
+
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
90
+
91
+
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
92
+
93
+
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
94
+
95
+
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
96
+
97
+
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
98
+
99
+
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.1", "", { "os": "android", "cpu": "arm" }, "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag=="],
100
+
101
+
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.1", "", { "os": "android", "cpu": "arm64" }, "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw=="],
102
+
103
+
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.50.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw=="],
104
+
105
+
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.50.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw=="],
106
+
107
+
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.50.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA=="],
108
+
109
+
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.50.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ=="],
110
+
111
+
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg=="],
112
+
113
+
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw=="],
114
+
115
+
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw=="],
116
+
117
+
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w=="],
118
+
119
+
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q=="],
120
+
121
+
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.50.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q=="],
122
+
123
+
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ=="],
124
+
125
+
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg=="],
126
+
127
+
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.50.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg=="],
128
+
129
+
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA=="],
130
+
131
+
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg=="],
132
+
133
+
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.50.1", "", { "os": "none", "cpu": "arm64" }, "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA=="],
134
+
135
+
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.50.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ=="],
136
+
137
+
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.50.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A=="],
138
+
139
+
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.50.1", "", { "os": "win32", "cpu": "x64" }, "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA=="],
140
+
141
+
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="],
142
+
143
+
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ=="],
144
+
145
+
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="],
146
+
147
+
"@tsconfig/svelte": ["@tsconfig/svelte@5.0.5", "", {}, "sha512-48fAnUjKye38FvMiNOj0J9I/4XlQQiZlpe9xaNPfe8vy2Y1hFBt8g1yqf2EGjVvHavo4jf2lC+TQyENCr4BJBQ=="],
148
+
149
+
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
150
+
151
+
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
152
+
153
+
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
154
+
155
+
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
156
+
157
+
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
158
+
159
+
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
160
+
161
+
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
162
+
163
+
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
164
+
165
+
"esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="],
166
+
167
+
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
168
+
169
+
"esrap": ["esrap@2.1.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="],
170
+
171
+
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
172
+
173
+
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
174
+
175
+
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
176
+
177
+
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
178
+
179
+
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
180
+
181
+
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
182
+
183
+
"moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
184
+
185
+
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
186
+
187
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
188
+
189
+
"mutex-ts": ["mutex-ts@1.2.1", "", {}, "sha512-OkcXgf0viuCgYdnm48kiNQ9PzC5OzISQ261svHr/Ybc2vBYC/5xfLXn44hQ+dYRX74v7MCSqV/LKPEbpYdDybw=="],
190
+
191
+
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
192
+
193
+
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
194
+
195
+
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
196
+
197
+
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
198
+
199
+
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
200
+
201
+
"rollup": ["rollup@4.50.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.1", "@rollup/rollup-android-arm64": "4.50.1", "@rollup/rollup-darwin-arm64": "4.50.1", "@rollup/rollup-darwin-x64": "4.50.1", "@rollup/rollup-freebsd-arm64": "4.50.1", "@rollup/rollup-freebsd-x64": "4.50.1", "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", "@rollup/rollup-linux-arm-musleabihf": "4.50.1", "@rollup/rollup-linux-arm64-gnu": "4.50.1", "@rollup/rollup-linux-arm64-musl": "4.50.1", "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", "@rollup/rollup-linux-ppc64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-musl": "4.50.1", "@rollup/rollup-linux-s390x-gnu": "4.50.1", "@rollup/rollup-linux-x64-gnu": "4.50.1", "@rollup/rollup-linux-x64-musl": "4.50.1", "@rollup/rollup-openharmony-arm64": "4.50.1", "@rollup/rollup-win32-arm64-msvc": "4.50.1", "@rollup/rollup-win32-ia32-msvc": "4.50.1", "@rollup/rollup-win32-x64-msvc": "4.50.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA=="],
202
+
203
+
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
204
+
205
+
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
206
+
207
+
"svelte": ["svelte@5.38.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-UY+OhrWK7WI22bCZ00P/M3HtyWgwJPi9IxSRkoAE2MeAy6kl7ZlZWJZ8RaB+X4KD/G+wjis+cGVnVYaoqbzBqg=="],
208
+
209
+
"svelte-check": ["svelte-check@4.3.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg=="],
210
+
211
+
"svelte-infinite-loading": ["svelte-infinite-loading@1.4.0", "", {}, "sha512-Jo+f/yr/HmZQuIiiKKzAHVFXdAUWHW2RBbrcQTil8JVk1sCm/riy7KTJVzjBgQvHasrFQYKF84zvtc9/Y4lFYg=="],
212
+
213
+
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
214
+
215
+
"typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
216
+
217
+
"vite": ["vite@6.3.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA=="],
218
+
219
+
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
220
+
221
+
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
222
+
}
223
+
}
+605
deno.lock
+605
deno.lock
···
1
+
{
2
+
"version": "5",
3
+
"specifiers": {
4
+
"npm:@atcute/bluesky@^2.0.2": "2.0.2_@atcute+client@3.0.1",
5
+
"npm:@atcute/client@^3.0.1": "3.0.1",
6
+
"npm:@atcute/identity-resolver@~0.1.2": "0.1.2_@atcute+identity@0.1.3",
7
+
"npm:@sveltejs/vite-plugin-svelte@^5.0.3": "5.0.3_svelte@5.28.1__acorn@8.14.1_vite@6.3.2__picomatch@4.0.2",
8
+
"npm:@tsconfig/svelte@^5.0.4": "5.0.4",
9
+
"npm:moment@^2.30.1": "2.30.1",
10
+
"npm:mutex-ts@^1.2.1": "1.2.1",
11
+
"npm:svelte-check@^4.1.5": "4.1.6_svelte@5.28.1__acorn@8.14.1_typescript@5.7.3",
12
+
"npm:svelte-infinite-loading@^1.4.0": "1.4.0",
13
+
"npm:svelte@^5.23.1": "5.28.1_acorn@8.14.1",
14
+
"npm:typescript@~5.7.2": "5.7.3",
15
+
"npm:vite@^6.3.1": "6.3.2_picomatch@4.0.2"
16
+
},
17
+
"npm": {
18
+
"@ampproject/remapping@2.3.0": {
19
+
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
20
+
"dependencies": [
21
+
"@jridgewell/gen-mapping",
22
+
"@jridgewell/trace-mapping"
23
+
]
24
+
},
25
+
"@atcute/bluesky@2.0.2_@atcute+client@3.0.1": {
26
+
"integrity": "sha512-xU+9Rp8bzc9AOnWSc11M1urRppDt3BiWR7v2QrLt3Qoysa5jvL8j2p2w4iRT8vLByz8Q+Xgk5Kz4zWVx1zCiug==",
27
+
"dependencies": [
28
+
"@atcute/client"
29
+
]
30
+
},
31
+
"@atcute/client@3.0.1": {
32
+
"integrity": "sha512-j51SuQYQj5jeKrx1DCXx+Q3fpVvO0JYGnKnJAdDSlesSLjPXjvnx1abC+hkuro58KRHHJvRA6P1MC0pmJsWfcg=="
33
+
},
34
+
"@atcute/identity-resolver@0.1.2_@atcute+identity@0.1.3": {
35
+
"integrity": "sha512-fP2VbHD04kVcCdNi/Kszo6jFzqM7Pg3p33oGhfp2zVkwFKaVBlwCaFRWEga/Xvu/IDLwNdASGWnLqoA34SFeSg==",
36
+
"dependencies": [
37
+
"@atcute/identity",
38
+
"@atcute/util-fetch",
39
+
"@badrap/valita"
40
+
]
41
+
},
42
+
"@atcute/identity@0.1.3": {
43
+
"integrity": "sha512-ndlD8nypHt8G00wixbozKdSNL0O8HTzBjFGEXeAcBUCXSZPBjRWbqtgyJxhgUWnr7swgxgw1mSbZwRB5b7xCiQ==",
44
+
"dependencies": [
45
+
"@badrap/valita"
46
+
]
47
+
},
48
+
"@atcute/util-fetch@1.0.1": {
49
+
"integrity": "sha512-Clc0E/5ufyGBVfYBUwWNlHONlZCoblSr4Ho50l1LhmRPGB1Wu/AQ9Sz+rsBg7fdaW/auve8ulmwhRhnX2cGRow==",
50
+
"dependencies": [
51
+
"@badrap/valita"
52
+
]
53
+
},
54
+
"@badrap/valita@0.4.4": {
55
+
"integrity": "sha512-GEhUCk9c4XbNxi+0YZHZsV4fYNd6HejfWuN4Ti4c02DauX+LyX5WY1Y3WfyZ8Pxxl0zqhs+MLtW98cMh86vv6g=="
56
+
},
57
+
"@esbuild/aix-ppc64@0.25.2": {
58
+
"integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
59
+
"os": ["aix"],
60
+
"cpu": ["ppc64"]
61
+
},
62
+
"@esbuild/android-arm64@0.25.2": {
63
+
"integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
64
+
"os": ["android"],
65
+
"cpu": ["arm64"]
66
+
},
67
+
"@esbuild/android-arm@0.25.2": {
68
+
"integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
69
+
"os": ["android"],
70
+
"cpu": ["arm"]
71
+
},
72
+
"@esbuild/android-x64@0.25.2": {
73
+
"integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
74
+
"os": ["android"],
75
+
"cpu": ["x64"]
76
+
},
77
+
"@esbuild/darwin-arm64@0.25.2": {
78
+
"integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
79
+
"os": ["darwin"],
80
+
"cpu": ["arm64"]
81
+
},
82
+
"@esbuild/darwin-x64@0.25.2": {
83
+
"integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
84
+
"os": ["darwin"],
85
+
"cpu": ["x64"]
86
+
},
87
+
"@esbuild/freebsd-arm64@0.25.2": {
88
+
"integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
89
+
"os": ["freebsd"],
90
+
"cpu": ["arm64"]
91
+
},
92
+
"@esbuild/freebsd-x64@0.25.2": {
93
+
"integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
94
+
"os": ["freebsd"],
95
+
"cpu": ["x64"]
96
+
},
97
+
"@esbuild/linux-arm64@0.25.2": {
98
+
"integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
99
+
"os": ["linux"],
100
+
"cpu": ["arm64"]
101
+
},
102
+
"@esbuild/linux-arm@0.25.2": {
103
+
"integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
104
+
"os": ["linux"],
105
+
"cpu": ["arm"]
106
+
},
107
+
"@esbuild/linux-ia32@0.25.2": {
108
+
"integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
109
+
"os": ["linux"],
110
+
"cpu": ["ia32"]
111
+
},
112
+
"@esbuild/linux-loong64@0.25.2": {
113
+
"integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
114
+
"os": ["linux"],
115
+
"cpu": ["loong64"]
116
+
},
117
+
"@esbuild/linux-mips64el@0.25.2": {
118
+
"integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
119
+
"os": ["linux"],
120
+
"cpu": ["mips64el"]
121
+
},
122
+
"@esbuild/linux-ppc64@0.25.2": {
123
+
"integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
124
+
"os": ["linux"],
125
+
"cpu": ["ppc64"]
126
+
},
127
+
"@esbuild/linux-riscv64@0.25.2": {
128
+
"integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
129
+
"os": ["linux"],
130
+
"cpu": ["riscv64"]
131
+
},
132
+
"@esbuild/linux-s390x@0.25.2": {
133
+
"integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
134
+
"os": ["linux"],
135
+
"cpu": ["s390x"]
136
+
},
137
+
"@esbuild/linux-x64@0.25.2": {
138
+
"integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
139
+
"os": ["linux"],
140
+
"cpu": ["x64"]
141
+
},
142
+
"@esbuild/netbsd-arm64@0.25.2": {
143
+
"integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
144
+
"os": ["netbsd"],
145
+
"cpu": ["arm64"]
146
+
},
147
+
"@esbuild/netbsd-x64@0.25.2": {
148
+
"integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
149
+
"os": ["netbsd"],
150
+
"cpu": ["x64"]
151
+
},
152
+
"@esbuild/openbsd-arm64@0.25.2": {
153
+
"integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
154
+
"os": ["openbsd"],
155
+
"cpu": ["arm64"]
156
+
},
157
+
"@esbuild/openbsd-x64@0.25.2": {
158
+
"integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
159
+
"os": ["openbsd"],
160
+
"cpu": ["x64"]
161
+
},
162
+
"@esbuild/sunos-x64@0.25.2": {
163
+
"integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
164
+
"os": ["sunos"],
165
+
"cpu": ["x64"]
166
+
},
167
+
"@esbuild/win32-arm64@0.25.2": {
168
+
"integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
169
+
"os": ["win32"],
170
+
"cpu": ["arm64"]
171
+
},
172
+
"@esbuild/win32-ia32@0.25.2": {
173
+
"integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
174
+
"os": ["win32"],
175
+
"cpu": ["ia32"]
176
+
},
177
+
"@esbuild/win32-x64@0.25.2": {
178
+
"integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
179
+
"os": ["win32"],
180
+
"cpu": ["x64"]
181
+
},
182
+
"@jridgewell/gen-mapping@0.3.8": {
183
+
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
184
+
"dependencies": [
185
+
"@jridgewell/set-array",
186
+
"@jridgewell/sourcemap-codec",
187
+
"@jridgewell/trace-mapping"
188
+
]
189
+
},
190
+
"@jridgewell/resolve-uri@3.1.2": {
191
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
192
+
},
193
+
"@jridgewell/set-array@1.2.1": {
194
+
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
195
+
},
196
+
"@jridgewell/sourcemap-codec@1.5.0": {
197
+
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
198
+
},
199
+
"@jridgewell/trace-mapping@0.3.25": {
200
+
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
201
+
"dependencies": [
202
+
"@jridgewell/resolve-uri",
203
+
"@jridgewell/sourcemap-codec"
204
+
]
205
+
},
206
+
"@rollup/rollup-android-arm-eabi@4.40.0": {
207
+
"integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==",
208
+
"os": ["android"],
209
+
"cpu": ["arm"]
210
+
},
211
+
"@rollup/rollup-android-arm64@4.40.0": {
212
+
"integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==",
213
+
"os": ["android"],
214
+
"cpu": ["arm64"]
215
+
},
216
+
"@rollup/rollup-darwin-arm64@4.40.0": {
217
+
"integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==",
218
+
"os": ["darwin"],
219
+
"cpu": ["arm64"]
220
+
},
221
+
"@rollup/rollup-darwin-x64@4.40.0": {
222
+
"integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==",
223
+
"os": ["darwin"],
224
+
"cpu": ["x64"]
225
+
},
226
+
"@rollup/rollup-freebsd-arm64@4.40.0": {
227
+
"integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==",
228
+
"os": ["freebsd"],
229
+
"cpu": ["arm64"]
230
+
},
231
+
"@rollup/rollup-freebsd-x64@4.40.0": {
232
+
"integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==",
233
+
"os": ["freebsd"],
234
+
"cpu": ["x64"]
235
+
},
236
+
"@rollup/rollup-linux-arm-gnueabihf@4.40.0": {
237
+
"integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==",
238
+
"os": ["linux"],
239
+
"cpu": ["arm"]
240
+
},
241
+
"@rollup/rollup-linux-arm-musleabihf@4.40.0": {
242
+
"integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==",
243
+
"os": ["linux"],
244
+
"cpu": ["arm"]
245
+
},
246
+
"@rollup/rollup-linux-arm64-gnu@4.40.0": {
247
+
"integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==",
248
+
"os": ["linux"],
249
+
"cpu": ["arm64"]
250
+
},
251
+
"@rollup/rollup-linux-arm64-musl@4.40.0": {
252
+
"integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==",
253
+
"os": ["linux"],
254
+
"cpu": ["arm64"]
255
+
},
256
+
"@rollup/rollup-linux-loongarch64-gnu@4.40.0": {
257
+
"integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==",
258
+
"os": ["linux"],
259
+
"cpu": ["loong64"]
260
+
},
261
+
"@rollup/rollup-linux-powerpc64le-gnu@4.40.0": {
262
+
"integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==",
263
+
"os": ["linux"],
264
+
"cpu": ["ppc64"]
265
+
},
266
+
"@rollup/rollup-linux-riscv64-gnu@4.40.0": {
267
+
"integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==",
268
+
"os": ["linux"],
269
+
"cpu": ["riscv64"]
270
+
},
271
+
"@rollup/rollup-linux-riscv64-musl@4.40.0": {
272
+
"integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==",
273
+
"os": ["linux"],
274
+
"cpu": ["riscv64"]
275
+
},
276
+
"@rollup/rollup-linux-s390x-gnu@4.40.0": {
277
+
"integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==",
278
+
"os": ["linux"],
279
+
"cpu": ["s390x"]
280
+
},
281
+
"@rollup/rollup-linux-x64-gnu@4.40.0": {
282
+
"integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==",
283
+
"os": ["linux"],
284
+
"cpu": ["x64"]
285
+
},
286
+
"@rollup/rollup-linux-x64-musl@4.40.0": {
287
+
"integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==",
288
+
"os": ["linux"],
289
+
"cpu": ["x64"]
290
+
},
291
+
"@rollup/rollup-win32-arm64-msvc@4.40.0": {
292
+
"integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==",
293
+
"os": ["win32"],
294
+
"cpu": ["arm64"]
295
+
},
296
+
"@rollup/rollup-win32-ia32-msvc@4.40.0": {
297
+
"integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==",
298
+
"os": ["win32"],
299
+
"cpu": ["ia32"]
300
+
},
301
+
"@rollup/rollup-win32-x64-msvc@4.40.0": {
302
+
"integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==",
303
+
"os": ["win32"],
304
+
"cpu": ["x64"]
305
+
},
306
+
"@sveltejs/acorn-typescript@1.0.5_acorn@8.14.1": {
307
+
"integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
308
+
"dependencies": [
309
+
"acorn"
310
+
]
311
+
},
312
+
"@sveltejs/vite-plugin-svelte-inspector@4.0.1_@sveltejs+vite-plugin-svelte@5.0.3__svelte@5.28.1___acorn@8.14.1__vite@6.3.2___picomatch@4.0.2_svelte@5.28.1__acorn@8.14.1_vite@6.3.2__picomatch@4.0.2": {
313
+
"integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==",
314
+
"dependencies": [
315
+
"@sveltejs/vite-plugin-svelte",
316
+
"debug",
317
+
"svelte",
318
+
"vite"
319
+
]
320
+
},
321
+
"@sveltejs/vite-plugin-svelte@5.0.3_svelte@5.28.1__acorn@8.14.1_vite@6.3.2__picomatch@4.0.2": {
322
+
"integrity": "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==",
323
+
"dependencies": [
324
+
"@sveltejs/vite-plugin-svelte-inspector",
325
+
"debug",
326
+
"deepmerge",
327
+
"kleur",
328
+
"magic-string",
329
+
"svelte",
330
+
"vite",
331
+
"vitefu"
332
+
]
333
+
},
334
+
"@tsconfig/svelte@5.0.4": {
335
+
"integrity": "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q=="
336
+
},
337
+
"@types/estree@1.0.7": {
338
+
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
339
+
},
340
+
"acorn@8.14.1": {
341
+
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
342
+
"bin": true
343
+
},
344
+
"aria-query@5.3.2": {
345
+
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="
346
+
},
347
+
"axobject-query@4.1.0": {
348
+
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="
349
+
},
350
+
"chokidar@4.0.3": {
351
+
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
352
+
"dependencies": [
353
+
"readdirp"
354
+
]
355
+
},
356
+
"clsx@2.1.1": {
357
+
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
358
+
},
359
+
"debug@4.4.0": {
360
+
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
361
+
"dependencies": [
362
+
"ms"
363
+
]
364
+
},
365
+
"deepmerge@4.3.1": {
366
+
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
367
+
},
368
+
"esbuild@0.25.2": {
369
+
"integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
370
+
"optionalDependencies": [
371
+
"@esbuild/aix-ppc64",
372
+
"@esbuild/android-arm",
373
+
"@esbuild/android-arm64",
374
+
"@esbuild/android-x64",
375
+
"@esbuild/darwin-arm64",
376
+
"@esbuild/darwin-x64",
377
+
"@esbuild/freebsd-arm64",
378
+
"@esbuild/freebsd-x64",
379
+
"@esbuild/linux-arm",
380
+
"@esbuild/linux-arm64",
381
+
"@esbuild/linux-ia32",
382
+
"@esbuild/linux-loong64",
383
+
"@esbuild/linux-mips64el",
384
+
"@esbuild/linux-ppc64",
385
+
"@esbuild/linux-riscv64",
386
+
"@esbuild/linux-s390x",
387
+
"@esbuild/linux-x64",
388
+
"@esbuild/netbsd-arm64",
389
+
"@esbuild/netbsd-x64",
390
+
"@esbuild/openbsd-arm64",
391
+
"@esbuild/openbsd-x64",
392
+
"@esbuild/sunos-x64",
393
+
"@esbuild/win32-arm64",
394
+
"@esbuild/win32-ia32",
395
+
"@esbuild/win32-x64"
396
+
],
397
+
"scripts": true,
398
+
"bin": true
399
+
},
400
+
"esm-env@1.2.2": {
401
+
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="
402
+
},
403
+
"esrap@1.4.6": {
404
+
"integrity": "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==",
405
+
"dependencies": [
406
+
"@jridgewell/sourcemap-codec"
407
+
]
408
+
},
409
+
"fdir@6.4.4_picomatch@4.0.2": {
410
+
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
411
+
"dependencies": [
412
+
"picomatch"
413
+
],
414
+
"optionalPeers": [
415
+
"picomatch"
416
+
]
417
+
},
418
+
"fsevents@2.3.3": {
419
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
420
+
"os": ["darwin"],
421
+
"scripts": true
422
+
},
423
+
"is-reference@3.0.3": {
424
+
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
425
+
"dependencies": [
426
+
"@types/estree"
427
+
]
428
+
},
429
+
"kleur@4.1.5": {
430
+
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="
431
+
},
432
+
"locate-character@3.0.0": {
433
+
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
434
+
},
435
+
"magic-string@0.30.17": {
436
+
"integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
437
+
"dependencies": [
438
+
"@jridgewell/sourcemap-codec"
439
+
]
440
+
},
441
+
"moment@2.30.1": {
442
+
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="
443
+
},
444
+
"mri@1.2.0": {
445
+
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="
446
+
},
447
+
"ms@2.1.3": {
448
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
449
+
},
450
+
"mutex-ts@1.2.1": {
451
+
"integrity": "sha512-OkcXgf0viuCgYdnm48kiNQ9PzC5OzISQ261svHr/Ybc2vBYC/5xfLXn44hQ+dYRX74v7MCSqV/LKPEbpYdDybw=="
452
+
},
453
+
"nanoid@3.3.11": {
454
+
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
455
+
"bin": true
456
+
},
457
+
"picocolors@1.1.1": {
458
+
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
459
+
},
460
+
"picomatch@4.0.2": {
461
+
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="
462
+
},
463
+
"postcss@8.5.3": {
464
+
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
465
+
"dependencies": [
466
+
"nanoid",
467
+
"picocolors",
468
+
"source-map-js"
469
+
]
470
+
},
471
+
"readdirp@4.1.2": {
472
+
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="
473
+
},
474
+
"rollup@4.40.0": {
475
+
"integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==",
476
+
"dependencies": [
477
+
"@types/estree"
478
+
],
479
+
"optionalDependencies": [
480
+
"@rollup/rollup-android-arm-eabi",
481
+
"@rollup/rollup-android-arm64",
482
+
"@rollup/rollup-darwin-arm64",
483
+
"@rollup/rollup-darwin-x64",
484
+
"@rollup/rollup-freebsd-arm64",
485
+
"@rollup/rollup-freebsd-x64",
486
+
"@rollup/rollup-linux-arm-gnueabihf",
487
+
"@rollup/rollup-linux-arm-musleabihf",
488
+
"@rollup/rollup-linux-arm64-gnu",
489
+
"@rollup/rollup-linux-arm64-musl",
490
+
"@rollup/rollup-linux-loongarch64-gnu",
491
+
"@rollup/rollup-linux-powerpc64le-gnu",
492
+
"@rollup/rollup-linux-riscv64-gnu",
493
+
"@rollup/rollup-linux-riscv64-musl",
494
+
"@rollup/rollup-linux-s390x-gnu",
495
+
"@rollup/rollup-linux-x64-gnu",
496
+
"@rollup/rollup-linux-x64-musl",
497
+
"@rollup/rollup-win32-arm64-msvc",
498
+
"@rollup/rollup-win32-ia32-msvc",
499
+
"@rollup/rollup-win32-x64-msvc",
500
+
"fsevents"
501
+
],
502
+
"bin": true
503
+
},
504
+
"sade@1.8.1": {
505
+
"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
506
+
"dependencies": [
507
+
"mri"
508
+
]
509
+
},
510
+
"source-map-js@1.2.1": {
511
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
512
+
},
513
+
"svelte-check@4.1.6_svelte@5.28.1__acorn@8.14.1_typescript@5.7.3": {
514
+
"integrity": "sha512-P7w/6tdSfk3zEVvfsgrp3h3DFC75jCdZjTQvgGJtjPORs1n7/v2VMPIoty3PWv7jnfEm3x0G/p9wH4pecTb0Wg==",
515
+
"dependencies": [
516
+
"@jridgewell/trace-mapping",
517
+
"chokidar",
518
+
"fdir",
519
+
"picocolors",
520
+
"sade",
521
+
"svelte",
522
+
"typescript"
523
+
],
524
+
"bin": true
525
+
},
526
+
"svelte-infinite-loading@1.4.0": {
527
+
"integrity": "sha512-Jo+f/yr/HmZQuIiiKKzAHVFXdAUWHW2RBbrcQTil8JVk1sCm/riy7KTJVzjBgQvHasrFQYKF84zvtc9/Y4lFYg=="
528
+
},
529
+
"svelte@5.28.1_acorn@8.14.1": {
530
+
"integrity": "sha512-iOa9WmfNG95lSOSJdMhdjJ4Afok7IRAQYXpbnxhd5EINnXseG0GVa9j6WPght4eX78XfFez45Fi+uRglGKPV/Q==",
531
+
"dependencies": [
532
+
"@ampproject/remapping",
533
+
"@jridgewell/sourcemap-codec",
534
+
"@sveltejs/acorn-typescript",
535
+
"@types/estree",
536
+
"acorn",
537
+
"aria-query",
538
+
"axobject-query",
539
+
"clsx",
540
+
"esm-env",
541
+
"esrap",
542
+
"is-reference",
543
+
"locate-character",
544
+
"magic-string",
545
+
"zimmerframe"
546
+
]
547
+
},
548
+
"tinyglobby@0.2.13_picomatch@4.0.2": {
549
+
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
550
+
"dependencies": [
551
+
"fdir",
552
+
"picomatch"
553
+
]
554
+
},
555
+
"typescript@5.7.3": {
556
+
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
557
+
"bin": true
558
+
},
559
+
"vite@6.3.2_picomatch@4.0.2": {
560
+
"integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==",
561
+
"dependencies": [
562
+
"esbuild",
563
+
"fdir",
564
+
"picomatch",
565
+
"postcss",
566
+
"rollup",
567
+
"tinyglobby"
568
+
],
569
+
"optionalDependencies": [
570
+
"fsevents"
571
+
],
572
+
"bin": true
573
+
},
574
+
"vitefu@1.0.6_vite@6.3.2__picomatch@4.0.2": {
575
+
"integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==",
576
+
"dependencies": [
577
+
"vite"
578
+
],
579
+
"optionalPeers": [
580
+
"vite"
581
+
]
582
+
},
583
+
"zimmerframe@1.1.2": {
584
+
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="
585
+
}
586
+
},
587
+
"workspace": {
588
+
"packageJson": {
589
+
"dependencies": [
590
+
"npm:@atcute/bluesky@^2.0.2",
591
+
"npm:@atcute/client@^3.0.1",
592
+
"npm:@atcute/identity-resolver@~0.1.2",
593
+
"npm:@sveltejs/vite-plugin-svelte@^5.0.3",
594
+
"npm:@tsconfig/svelte@^5.0.4",
595
+
"npm:moment@^2.30.1",
596
+
"npm:mutex-ts@^1.2.1",
597
+
"npm:svelte-check@^4.1.5",
598
+
"npm:svelte-infinite-loading@^1.4.0",
599
+
"npm:svelte@^5.23.1",
600
+
"npm:typescript@~5.7.2",
601
+
"npm:vite@^6.3.1"
602
+
]
603
+
}
604
+
}
605
+
}
+12
index.html
+12
index.html
···
1
+
<!DOCTYPE html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="UTF-8" />
5
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+
<title>shimaenaga pds @ veryroundbird.house</title>
7
+
</head>
8
+
<body>
9
+
<div id="app"></div>
10
+
<script type="module" src="/src/main.ts"></script>
11
+
</body>
12
+
</html>
+28
package.json
+28
package.json
···
1
+
{
2
+
"name": "web",
3
+
"private": true,
4
+
"version": "0.0.0",
5
+
"type": "module",
6
+
"scripts": {
7
+
"dev": "vite",
8
+
"build": "vite build",
9
+
"preview": "vite preview",
10
+
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
11
+
},
12
+
"dependencies": {
13
+
"@atcute/bluesky": "^2.0.2",
14
+
"@atcute/client": "^3.0.1",
15
+
"@atcute/identity-resolver": "^0.1.2",
16
+
"moment": "^2.30.1",
17
+
"mutex-ts": "^1.2.1",
18
+
"svelte-infinite-loading": "^1.4.0"
19
+
},
20
+
"devDependencies": {
21
+
"@sveltejs/vite-plugin-svelte": "^5.0.3",
22
+
"@tsconfig/svelte": "^5.0.4",
23
+
"svelte": "^5.23.1",
24
+
"svelte-check": "^4.1.5",
25
+
"typescript": "~5.7.2",
26
+
"vite": "^6.3.1"
27
+
}
28
+
}
public/favicon.ico
public/favicon.ico
This is a binary file and will not be displayed.
public/fonts/Recursive_VF_1.085.woff2
public/fonts/Recursive_VF_1.085.woff2
This is a binary file and will not be displayed.
+104
src/App.svelte
+104
src/App.svelte
···
1
+
<script lang="ts">
2
+
import PostComponent from "./lib/PostComponent.svelte";
3
+
import AccountComponent from "./lib/AccountComponent.svelte";
4
+
import InfiniteLoading from "svelte-infinite-loading";
5
+
import { getNextPosts, Post, getAllMetadataFromPds } from "./lib/pdsfetch";
6
+
import { Config } from "../config";
7
+
const accountsPromise = getAllMetadataFromPds();
8
+
import { onMount } from "svelte";
9
+
10
+
let posts: Post[] = [];
11
+
12
+
let hue: number = 1;
13
+
const cycleColors = async () => {
14
+
while (true) {
15
+
hue += 1;
16
+
if (hue > 360) {
17
+
hue = 0;
18
+
}
19
+
document.documentElement.style.setProperty("--primary-h", hue.toString());
20
+
await new Promise((resolve) => setTimeout(resolve, 10));
21
+
}
22
+
}
23
+
let clickCounter = 0;
24
+
const carameldansenfusion = async () => {
25
+
clickCounter++;
26
+
if (clickCounter >= 10) {
27
+
clickCounter = 0;
28
+
cycleColors();
29
+
}
30
+
};
31
+
32
+
onMount(() => {
33
+
// Fetch initial posts
34
+
getNextPosts().then((initialPosts) => {
35
+
posts = initialPosts;
36
+
});
37
+
});
38
+
// Infinite loading function
39
+
const onInfinite = ({
40
+
detail: { loaded, complete },
41
+
}: {
42
+
detail: { loaded: () => void; complete: () => void };
43
+
}) => {
44
+
getNextPosts().then((newPosts) => {
45
+
console.log("Loading next posts...");
46
+
if (newPosts.length > 0) {
47
+
posts = [...posts, ...newPosts];
48
+
loaded();
49
+
} else {
50
+
complete();
51
+
}
52
+
});
53
+
};
54
+
</script>
55
+
56
+
<main>
57
+
<div id="content">
58
+
{#await accountsPromise}
59
+
<p>Loading...</p>
60
+
{:then accountsData}
61
+
<div id="account">
62
+
<pre id="asciiart">
63
+
█▓░░░░░░░░░▒
64
+
█▓▒ ░▒
65
+
▓▒▒▒░ ▒
66
+
▓▒░░░░ ░▓
67
+
█▒ ░░ ░ ░ ▓▒ ░█
68
+
▓░ ░░ ▒▓░░▒▒░ ▒
69
+
███████ ██▓▒░▒▒░ ░░░░ ░░░ ▒
70
+
█▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▒▓▒▒▒▓░ ▓
71
+
███▓▒░░▒░░░░▓▓█▓▓▒░ ░
72
+
▓ ░
73
+
█ ░ ▒
74
+
█░ ▒█
75
+
▒ ▒
76
+
█▒ █
77
+
▓ ░█
78
+
▓▒ ░░ ░▒
79
+
▓▒▒░░▒▓░ ░░░▒▒▓▓▒▓▓
80
+
█▓█▓▓█ ███▓█
81
+
████ ████
82
+
</pre>
83
+
<h1 onclick={carameldansenfusion} id="header">shimaenaga pds</h1>
84
+
<p id="subtitle">a project of <a href="https://veryroundbird.house" target="_blank">veryroundbird.house</a></p>
85
+
<p>{accountsData.length} birds nesting here</p>
86
+
<div id="accountsList">
87
+
{#each accountsData as accountObject}
88
+
<AccountComponent account={accountObject} />
89
+
{/each}
90
+
</div>
91
+
<footer id="footer">{@html Config.FOOTER_TEXT}</footer>
92
+
</div>
93
+
{:catch error}
94
+
<p>Error: {error.message}</p>
95
+
{/await}
96
+
97
+
<div id="feed">
98
+
{#each posts as postObject}
99
+
<PostComponent post={postObject as Post} />
100
+
{/each}
101
+
<InfiniteLoading on:infinite={onInfinite} distance={3000} />
102
+
</div>
103
+
</div>
104
+
</main>
+4
src/app.css
+4
src/app.css
+37
src/lib/AccountComponent.svelte
+37
src/lib/AccountComponent.svelte
···
1
+
<script lang="ts">
2
+
import type { AccountMetadata } from "./pdsfetch";
3
+
const { account }: { account: AccountMetadata } = $props();
4
+
console.log(account);
5
+
import { Config } from "../../config";
6
+
</script>
7
+
8
+
<button class="accountBtn" type="button" popovertarget="{account.handle.replace('.')}" title="{account.handle}">
9
+
<div class="accountContainer">
10
+
{#if account.avatarCid}
11
+
<img
12
+
class="avatar"
13
+
alt="avatar of {account.displayName}"
14
+
width="24"
15
+
height="24"
16
+
src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.avatarCid}"
17
+
/>
18
+
{:else}
19
+
<img
20
+
class="avatar"
21
+
alt="no avatar for {account.displayName}"
22
+
width="24"
23
+
height="24"
24
+
src=""
25
+
/>
26
+
{/if}
27
+
</div>
28
+
<div class="accountTooltip" popover id="{account.handle.replace('.')}">
29
+
<div class="banner">
30
+
<img class="bannerImg" src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.bannerCid}" alt="{account.displayName}'s banner" width="300" height="100" />
31
+
<img class="avatarInsetImg" src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.avatarCid}" alt="{account.displayName}'s avatar" width="50" height="50" />
32
+
</div>
33
+
<div class="displayName">{account.displayName}</div>
34
+
<div class="handle"><a href="{Config.FRONTEND_URL}/profile/{account.did}" target="_blank">{account.handle}</a></div>
35
+
<div class="desc">{account.description}</div>
36
+
</div>
37
+
</button>
+156
src/lib/PostComponent.svelte
+156
src/lib/PostComponent.svelte
···
1
+
<script lang="ts">
2
+
import { Post } from "./pdsfetch";
3
+
import { Config } from "../../config";
4
+
import { onMount } from "svelte";
5
+
import moment from "moment";
6
+
7
+
let { post }: { post: Post } = $props();
8
+
9
+
// State for image carousel
10
+
let currentImageIndex = $state(0);
11
+
12
+
// Functions to navigate carousel
13
+
function nextImage() {
14
+
if (post.imagesCid && currentImageIndex < post.imagesCid.length - 1) {
15
+
currentImageIndex++;
16
+
}
17
+
}
18
+
19
+
function prevImage() {
20
+
if (currentImageIndex > 0) {
21
+
currentImageIndex--;
22
+
}
23
+
}
24
+
25
+
// Function to preload an image
26
+
function preloadImage(index: number): void {
27
+
if (!post.imagesCid || index < 0 || index >= post.imagesCid.length) return;
28
+
29
+
const img = new Image();
30
+
img.src = `${Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did=${post.authorDid}&cid=${post.imagesCid[index]}`;
31
+
}
32
+
33
+
// Preload adjacent images when current index changes
34
+
$effect(() => {
35
+
if (post.imagesCid && post.imagesCid.length > 1) {
36
+
// Preload next image if available
37
+
if (currentImageIndex < post.imagesCid.length - 1) {
38
+
preloadImage(currentImageIndex + 1);
39
+
}
40
+
41
+
// Preload previous image if available
42
+
if (currentImageIndex > 0) {
43
+
preloadImage(currentImageIndex - 1);
44
+
}
45
+
}
46
+
});
47
+
48
+
// Initial preload of images
49
+
onMount(() => {
50
+
if (post.imagesCid && post.imagesCid.length > 1) {
51
+
// Preload the next image if it exists
52
+
if (post.imagesCid.length > 1) {
53
+
preloadImage(1);
54
+
}
55
+
}
56
+
});
57
+
</script>
58
+
59
+
<div class="postContainer">
60
+
<div class="postHeader">
61
+
{#if post.authorAvatarCid}
62
+
<img
63
+
class="avatar"
64
+
src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={post.authorDid}&cid={post.authorAvatarCid}"
65
+
alt="avatar of {post.displayName}"
66
+
/>
67
+
{/if}
68
+
<div class="headerText">
69
+
<a class="displayName" href="{Config.FRONTEND_URL}/profile/{post.authorDid}"
70
+
>{post.displayName}</a
71
+
>
72
+
<p class="handle">
73
+
<a href="{Config.FRONTEND_URL}/profile/{post.authorHandle}"
74
+
>@{post.authorHandle}</a
75
+
>
76
+
77
+
<a
78
+
class="postLink"
79
+
href="{Config.FRONTEND_URL}/profile/{post.authorDid}/post/{post.recordName}"
80
+
>{moment(post.timenotstamp).isBefore(moment().subtract(1, "month"))
81
+
? moment(post.timenotstamp).format("MMM D, YYYY")
82
+
: moment(post.timenotstamp).fromNow()}</a
83
+
>
84
+
</p>
85
+
</div>
86
+
</div>
87
+
<div class="postContent">
88
+
{#if post.replyingUri}
89
+
<a
90
+
class="replyingText"
91
+
href="{Config.FRONTEND_URL}/profile/{post.replyingUri.repo}/post/{post
92
+
.replyingUri.rkey}">replying to {post.replyingUri.repo}</a
93
+
>
94
+
{/if}
95
+
{#if post.quotingUri}
96
+
<a
97
+
class="quotingText"
98
+
href="{Config.FRONTEND_URL}/profile/{post.quotingUri.repo}/post/{post
99
+
.quotingUri.rkey}">quoting {post.quotingUri.repo}</a
100
+
>
101
+
{/if}
102
+
<div class="postText">{post.text}</div>
103
+
{#if post.imagesCid && post.imagesCid.length > 0}
104
+
<div id="carouselContainer">
105
+
<img
106
+
class="embedImages"
107
+
alt="Post Image {currentImageIndex + 1} of {post.imagesCid.length}"
108
+
src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={post.authorDid}&cid={post
109
+
.imagesCid[currentImageIndex]}"
110
+
/>
111
+
112
+
{#if post.imagesCid.length > 1}
113
+
<div class="carouselControls">
114
+
<button
115
+
id="prevBtn"
116
+
onclick={prevImage}
117
+
disabled={currentImageIndex === 0}>←</button
118
+
>
119
+
<div class="carouselIndicators">
120
+
{#each post.imagesCid as _, i}
121
+
<div
122
+
class="indicator {i === currentImageIndex ? 'active' : ''}"
123
+
></div>
124
+
{/each}
125
+
</div>
126
+
<button
127
+
class="nextBtn"
128
+
onclick={nextImage}
129
+
disabled={currentImageIndex === post.imagesCid.length - 1}
130
+
>→</button
131
+
>
132
+
</div>
133
+
{/if}
134
+
</div>
135
+
{/if}
136
+
{#if post.videosLinkCid}
137
+
<!-- svelte-ignore a11y_media_has_caption -->
138
+
<video
139
+
class="embedVideo"
140
+
src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={post.authorDid}&cid={post.videosLinkCid}"
141
+
controls
142
+
></video>
143
+
{/if}
144
+
{#if post.gifLink}
145
+
<img
146
+
class="embedVideo"
147
+
src="{post.gifLink}"
148
+
alt="Post GIF"
149
+
/>
150
+
{/if}
151
+
</div>
152
+
</div>
153
+
154
+
<style>
155
+
156
+
</style>
+363
src/lib/pdsfetch.ts
+363
src/lib/pdsfetch.ts
···
1
+
import { simpleFetchHandler, XRPC } from "@atcute/client";
2
+
import "@atcute/bluesky/lexicons";
3
+
import type {
4
+
AppBskyActorDefs,
5
+
AppBskyActorProfile,
6
+
AppBskyFeedPost,
7
+
At,
8
+
ComAtprotoRepoListRecords,
9
+
} from "@atcute/client/lexicons";
10
+
import {
11
+
CompositeDidDocumentResolver,
12
+
PlcDidDocumentResolver,
13
+
WebDidDocumentResolver,
14
+
} from "@atcute/identity-resolver";
15
+
import { Config } from "../../config";
16
+
import { Mutex } from "mutex-ts"
17
+
// import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons";
18
+
// import { AppBskyFeedPost } from "@atcute/client/lexicons";
19
+
// import { AppBskyActorDefs } from "@atcute/client/lexicons";
20
+
21
+
interface AccountMetadata {
22
+
did: At.Did;
23
+
displayName: string;
24
+
handle: string;
25
+
avatarCid: string | null;
26
+
currentCursor?: string;
27
+
}
28
+
29
+
let accountsMetadata: AccountMetadata[] = [];
30
+
31
+
interface atUriObject {
32
+
repo: string;
33
+
collection: string;
34
+
rkey: string;
35
+
}
36
+
class Post {
37
+
authorDid: string;
38
+
authorAvatarCid: string | null;
39
+
postCid: string;
40
+
recordName: string;
41
+
authorHandle: string;
42
+
displayName: string;
43
+
text: string;
44
+
timestamp: number;
45
+
timenotstamp: string;
46
+
quotingUri: atUriObject | null;
47
+
replyingUri: atUriObject | null;
48
+
imagesCid: string[] | null;
49
+
videosLinkCid: string | null;
50
+
gifLink: string | null;
51
+
52
+
constructor(
53
+
record: ComAtprotoRepoListRecords.Record,
54
+
account: AccountMetadata,
55
+
) {
56
+
this.postCid = record.cid;
57
+
this.recordName = processAtUri(record.uri).rkey;
58
+
this.authorDid = account.did;
59
+
this.authorAvatarCid = account.avatarCid;
60
+
this.authorHandle = account.handle;
61
+
this.displayName = account.displayName;
62
+
const post = record.value as AppBskyFeedPost.Record;
63
+
this.timenotstamp = post.createdAt;
64
+
this.text = post.text;
65
+
this.timestamp = Date.parse(post.createdAt);
66
+
if (post.reply) {
67
+
this.replyingUri = processAtUri(post.reply.parent.uri);
68
+
} else {
69
+
this.replyingUri = null;
70
+
}
71
+
this.quotingUri = null;
72
+
this.imagesCid = null;
73
+
this.videosLinkCid = null;
74
+
this.gifLink = null;
75
+
switch (post.embed?.$type) {
76
+
case "app.bsky.embed.images":
77
+
this.imagesCid = post.embed.images.map(
78
+
(imageRecord: any) => imageRecord.image.ref.$link,
79
+
);
80
+
break;
81
+
case "app.bsky.embed.video":
82
+
this.videosLinkCid = post.embed.video.ref.$link;
83
+
break;
84
+
case "app.bsky.embed.record":
85
+
this.quotingUri = processAtUri(post.embed.record.uri);
86
+
break;
87
+
case "app.bsky.embed.recordWithMedia":
88
+
this.quotingUri = processAtUri(post.embed.record.record.uri);
89
+
switch (post.embed.media.$type) {
90
+
case "app.bsky.embed.images":
91
+
this.imagesCid = post.embed.media.images.map(
92
+
(imageRecord) => imageRecord.image.ref.$link,
93
+
);
94
+
95
+
break;
96
+
case "app.bsky.embed.video":
97
+
this.videosLinkCid = post.embed.media.video.ref.$link;
98
+
99
+
break;
100
+
}
101
+
break;
102
+
case "app.bsky.embed.external": // assuming that external embeds are gifs for now
103
+
if (post.embed.external.uri.includes(".gif")) {
104
+
this.gifLink = post.embed.external.uri;
105
+
}
106
+
break;
107
+
}
108
+
}
109
+
}
110
+
111
+
const processAtUri = (aturi: string): atUriObject => {
112
+
const parts = aturi.split("/");
113
+
return {
114
+
repo: parts[2],
115
+
collection: parts[3],
116
+
rkey: parts[4],
117
+
};
118
+
};
119
+
120
+
const rpc = new XRPC({
121
+
handler: simpleFetchHandler({
122
+
service: Config.PDS_URL,
123
+
}),
124
+
});
125
+
126
+
const getDidsFromPDS = async (): Promise<At.Did[]> => {
127
+
const { data } = await rpc.get("com.atproto.sync.listRepos", {
128
+
params: {},
129
+
});
130
+
return data.repos.map((repo: any) => repo.did) as At.Did[];
131
+
};
132
+
const getAccountMetadata = async (
133
+
did: `did:${string}:${string}`,
134
+
) => {
135
+
const account: AccountMetadata = {
136
+
did: did,
137
+
handle: "", // Guaranteed to be filled out later
138
+
displayName: "",
139
+
avatarCid: null,
140
+
};
141
+
142
+
try {
143
+
const { data } = await rpc.get("com.atproto.repo.getRecord", {
144
+
params: {
145
+
repo: did,
146
+
collection: "app.bsky.actor.profile",
147
+
rkey: "self",
148
+
},
149
+
});
150
+
const value = data.value as AppBskyActorProfile.Record;
151
+
account.displayName = value.displayName || "";
152
+
account.description = value.description || "";
153
+
if (value.avatar) {
154
+
account.avatarCid = value.avatar.ref["$link"];
155
+
}
156
+
if (value.banner) {
157
+
account.bannerCid = value.banner.ref["$link"];
158
+
}
159
+
} catch (e) {
160
+
console.warn(`Error fetching profile for ${did}:`, e);
161
+
}
162
+
163
+
try {
164
+
account.handle = await blueskyHandleFromDid(did);
165
+
} catch (e) {
166
+
console.error(`Error fetching handle for ${did}:`, e);
167
+
return null;
168
+
}
169
+
170
+
return account;
171
+
};
172
+
173
+
const getAllMetadataFromPds = async (): Promise<AccountMetadata[]> => {
174
+
const dids = await getDidsFromPDS();
175
+
const metadata = await Promise.all(
176
+
dids.map(async (repo: `did:${string}:${string}`) => {
177
+
return await getAccountMetadata(repo);
178
+
}),
179
+
);
180
+
return metadata.filter((account) => account !== null) as AccountMetadata[];
181
+
};
182
+
183
+
const identityResolve = async (did: At.Did) => {
184
+
const resolver = new CompositeDidDocumentResolver({
185
+
methods: {
186
+
plc: new PlcDidDocumentResolver(),
187
+
web: new WebDidDocumentResolver(),
188
+
},
189
+
});
190
+
191
+
if (did.startsWith("did:plc:") || did.startsWith("did:web:")) {
192
+
const doc = await resolver.resolve(
193
+
did as `did:plc:${string}` | `did:web:${string}`,
194
+
);
195
+
return doc;
196
+
} else {
197
+
throw new Error(`Unsupported DID type: ${did}`);
198
+
}
199
+
};
200
+
201
+
const blueskyHandleFromDid = async (did: At.Did) => {
202
+
const doc = await identityResolve(did);
203
+
if (doc.alsoKnownAs) {
204
+
const handleAtUri = doc.alsoKnownAs.find((url) => url.startsWith("at://"));
205
+
const handle = handleAtUri?.split("/")[2];
206
+
if (!handle) {
207
+
return "Handle not found";
208
+
} else {
209
+
return handle;
210
+
}
211
+
} else {
212
+
return "Handle not found";
213
+
}
214
+
};
215
+
216
+
interface PostsAcc {
217
+
posts: ComAtprotoRepoListRecords.Record[];
218
+
account: AccountMetadata;
219
+
}
220
+
const getCutoffDate = (postAccounts: PostsAcc[]) => {
221
+
const now = Date.now();
222
+
let cutoffDate: Date | null = null;
223
+
postAccounts.forEach((postAcc) => {
224
+
const latestPost = new Date(
225
+
(postAcc.posts[postAcc.posts.length - 1].value as AppBskyFeedPost.Record)
226
+
.createdAt,
227
+
);
228
+
if (!cutoffDate) {
229
+
cutoffDate = latestPost;
230
+
} else {
231
+
if (latestPost > cutoffDate) {
232
+
cutoffDate = latestPost;
233
+
}
234
+
}
235
+
});
236
+
if (cutoffDate) {
237
+
return cutoffDate;
238
+
} else {
239
+
return new Date(now);
240
+
}
241
+
};
242
+
243
+
const filterPostsByDate = (posts: PostsAcc[], cutoffDate: Date) => {
244
+
// filter posts for each account that are older than the cutoff date and save the cursor of the last post included
245
+
const filteredPosts: PostsAcc[] = posts.map((postAcc) => {
246
+
const filtered = postAcc.posts.filter((post) => {
247
+
const postDate = new Date(
248
+
(post.value as AppBskyFeedPost.Record).createdAt,
249
+
);
250
+
return postDate >= cutoffDate;
251
+
});
252
+
if (filtered.length > 0) {
253
+
postAcc.account.currentCursor = processAtUri(filtered[filtered.length - 1].uri).rkey;
254
+
}
255
+
return {
256
+
posts: filtered,
257
+
account: postAcc.account,
258
+
};
259
+
});
260
+
return filteredPosts;
261
+
};
262
+
263
+
const postsMutex = new Mutex();
264
+
// nightmare function. However it works so I am not touching it
265
+
const getNextPosts = async () => {
266
+
const release = await postsMutex.obtain();
267
+
if (!accountsMetadata.length) {
268
+
accountsMetadata = await getAllMetadataFromPds();
269
+
}
270
+
271
+
const postsAcc: PostsAcc[] = await Promise.all(
272
+
accountsMetadata.map(async (account) => {
273
+
const posts = await fetchPostsForUser(
274
+
account.did,
275
+
account.currentCursor || null,
276
+
);
277
+
if (posts) {
278
+
return {
279
+
posts: posts,
280
+
account: account,
281
+
};
282
+
} else {
283
+
return {
284
+
posts: [],
285
+
account: account,
286
+
};
287
+
}
288
+
}),
289
+
);
290
+
const recordsFiltered = postsAcc.filter((postAcc) =>
291
+
postAcc.posts.length > 0
292
+
);
293
+
const cutoffDate = getCutoffDate(recordsFiltered);
294
+
const recordsCutoff = filterPostsByDate(recordsFiltered, cutoffDate);
295
+
// update the accountMetadata with the new cursor
296
+
accountsMetadata = accountsMetadata.map((account) => {
297
+
const postAcc = recordsCutoff.find(
298
+
(postAcc) => postAcc.account.did == account.did,
299
+
);
300
+
if (postAcc) {
301
+
account.currentCursor = postAcc.account.currentCursor;
302
+
}
303
+
return account;
304
+
}
305
+
);
306
+
// throw the records in a big single array
307
+
let records = recordsCutoff.flatMap((postAcc) => postAcc.posts);
308
+
// sort the records by timestamp
309
+
records = records.sort((a, b) => {
310
+
const aDate = new Date(
311
+
(a.value as AppBskyFeedPost.Record).createdAt,
312
+
).getTime();
313
+
const bDate = new Date(
314
+
(b.value as AppBskyFeedPost.Record).createdAt,
315
+
).getTime();
316
+
return bDate - aDate;
317
+
});
318
+
// filter out posts that are in the future
319
+
if (!Config.SHOW_FUTURE_POSTS) {
320
+
const now = Date.now();
321
+
records = records.filter((post) => {
322
+
const postDate = new Date(
323
+
(post.value as AppBskyFeedPost.Record).createdAt,
324
+
).getTime();
325
+
return postDate <= now;
326
+
});
327
+
}
328
+
329
+
const newPosts = records.map((record) => {
330
+
const account = accountsMetadata.find(
331
+
(account) => account.did == processAtUri(record.uri).repo,
332
+
);
333
+
if (!account) {
334
+
throw new Error(
335
+
`Account with DID ${processAtUri(record.uri).repo} not found`,
336
+
);
337
+
}
338
+
return new Post(record, account);
339
+
});
340
+
// release the mutex
341
+
release();
342
+
return newPosts;
343
+
};
344
+
345
+
const fetchPostsForUser = async (did: At.Did, cursor: string | null) => {
346
+
try {
347
+
const { data } = await rpc.get("com.atproto.repo.listRecords", {
348
+
params: {
349
+
repo: did as At.Identifier,
350
+
collection: "app.bsky.feed.post",
351
+
limit: Config.MAX_POSTS,
352
+
cursor: cursor || undefined,
353
+
},
354
+
});
355
+
return data.records as ComAtprotoRepoListRecords.Record[];
356
+
} catch (e) {
357
+
console.error(`Error fetching posts for ${did}:`, e);
358
+
return null;
359
+
}
360
+
};
361
+
362
+
export { getAllMetadataFromPds, getNextPosts, Post };
363
+
export type { AccountMetadata };
+9
src/main.ts
+9
src/main.ts
+2
src/vite-env.d.ts
+2
src/vite-env.d.ts
+7
svelte.config.js
+7
svelte.config.js
+432
themes/birdrights/theme.css
+432
themes/birdrights/theme.css
···
1
+
@font-face {
2
+
font-family: "Recursive Variable";
3
+
font-weight: 100 700;
4
+
font-style: -5deg 5deg;
5
+
src: url(/fonts/Recursive_VF_1.085.woff2) format('woff2');
6
+
}
7
+
8
+
:root {
9
+
/* Color overrides, edit to whatever you want */
10
+
--primary-h: 260; /* Hue */
11
+
--background: #EFD7E4;
12
+
--foreground: #333333;
13
+
14
+
--link-color: color-mix(in srgb-linear, color-mix(in srgb-linear, var(--background), red 50%), black 25%);
15
+
--link-hover-color: color-mix(in srgb-linear, var(--link-color), black 25%);
16
+
--background-color: var(--background);
17
+
--header-background-color: var(--background);
18
+
--content-background-color: rgba(255,255,255,.5);
19
+
--text-color: var(--foreground);
20
+
--border-color: color-mix(in srgb-linear, var(--text-color), rgba(0,0,0,.15));
21
+
--indicator-inactive-color: var(--link-hover-color);
22
+
--indicator-active-color: var(--link-color);
23
+
--base-font-size: 14px;
24
+
}
25
+
26
+
* {
27
+
box-sizing: border-box;
28
+
}
29
+
30
+
body {
31
+
margin: 20px;
32
+
display: flex;
33
+
place-items: center;
34
+
min-width: 320px;
35
+
min-height: 100vh;
36
+
background-color: var(--background-color);
37
+
font-family: "Recursive Variable";
38
+
font-size: var(--base-font-size);
39
+
color: var(--text-color);
40
+
border-color: var(--border-color);
41
+
overflow-wrap: break-word;
42
+
word-wrap: normal;
43
+
word-break: break-word;
44
+
hyphens: none;
45
+
}
46
+
47
+
a {
48
+
font-weight: 500;
49
+
color: var(--link-color);
50
+
text-decoration: inherit;
51
+
}
52
+
a:hover {
53
+
color: var(--link-hover-color);
54
+
text-decoration: underline;
55
+
}
56
+
57
+
h1 {
58
+
font-size: 2em;
59
+
line-height: 1.1;
60
+
margin-bottom: 0;
61
+
text-align: center;
62
+
}
63
+
64
+
#app {
65
+
max-width: 600px;
66
+
width: 100%;
67
+
margin: 0;
68
+
padding: 0;
69
+
margin-left: auto;
70
+
margin-right: auto;
71
+
text-align: center;
72
+
}
73
+
74
+
/* Page Structure */
75
+
#content {
76
+
/* split the screen in half, left for accounts, right for posts */
77
+
width: 100%;
78
+
margin: 0 auto;
79
+
}
80
+
81
+
#account {
82
+
text-align: center;
83
+
margin-bottom: 20px;
84
+
}
85
+
86
+
#accountsList {
87
+
display: grid;
88
+
grid-template-columns: repeat(12, 1fr);
89
+
gap: 10px;
90
+
overflow-y: scroll;
91
+
height: 100%;
92
+
width: 100%;
93
+
padding: 0px;
94
+
margin: 0px;
95
+
}
96
+
97
+
#feed {
98
+
width: 100%;
99
+
margin-top: 0;
100
+
margin-bottom: 0;
101
+
}
102
+
103
+
/* Header */
104
+
#subtitle {
105
+
font-size: 1.1rem;
106
+
font-style: oblique 5deg;
107
+
margin: 0;
108
+
}
109
+
110
+
#asciiart {
111
+
font-size: .8rem;
112
+
line-height: 1;
113
+
margin: 0 auto;
114
+
display: inline-block;
115
+
}
116
+
117
+
/* Post Component */
118
+
.postContainer {
119
+
border: 1px solid var(--border-color);
120
+
background-color: var(--background-color);
121
+
margin-bottom: 15px;
122
+
overflow-wrap: break-word;
123
+
overflow: hidden;
124
+
border-radius: 5px 5px 3px 3px;
125
+
}
126
+
127
+
.postHeader {
128
+
display: flex;
129
+
flex-direction: row;
130
+
align-items: center;
131
+
justify-content: start;
132
+
background-color: var(--header-background-color);
133
+
padding: 0px 0px;
134
+
height: fit-content;
135
+
border-bottom: 1px solid var(--border-color);
136
+
font-weight: bold;
137
+
overflow-wrap: break-word;
138
+
height: 60px;
139
+
}
140
+
141
+
.headerText {
142
+
margin-left: 10px;
143
+
font-size: 0.9em;
144
+
text-align: start;
145
+
word-break: break-word;
146
+
max-width: 80%;
147
+
max-height: 95%;
148
+
overflow: hidden;
149
+
align-self: flex-start;
150
+
margin-top: auto;
151
+
margin-bottom: auto;
152
+
}
153
+
154
+
.displayName {
155
+
display: block;
156
+
color: var(--text-color);
157
+
font-size: 1.2em;
158
+
padding: 0;
159
+
margin: 0;
160
+
overflow-wrap: normal;
161
+
word-wrap: break-word;
162
+
word-break: break-word;
163
+
text-overflow: ellipsis;
164
+
overflow: hidden;
165
+
white-space: nowrap;
166
+
width: 100%;
167
+
}
168
+
169
+
.handle {
170
+
display: block;
171
+
color: var(--border-color);
172
+
font-size: 0.8em;
173
+
padding: 0;
174
+
margin: 0;
175
+
}
176
+
177
+
.avatar {
178
+
height: 60px;
179
+
width: 60px;
180
+
margin: 0px;
181
+
margin-left: 0px;
182
+
overflow: hidden;
183
+
object-fit: cover;
184
+
border-right: var(--border-color) 1px solid;
185
+
}
186
+
187
+
.postLink {
188
+
color: var(--border-color);
189
+
font-size: 0.8em;
190
+
padding: 0;
191
+
margin: 0;
192
+
}
193
+
194
+
.postContent {
195
+
display: flex;
196
+
text-align: start;
197
+
flex-direction: column;
198
+
padding: 10px;
199
+
background-color: var(--content-background-color);
200
+
color: var(--text-color);
201
+
overflow-wrap: break-word;
202
+
white-space: pre-line;
203
+
}
204
+
205
+
.replyingText {
206
+
font-size: 0.7em;
207
+
margin: 0;
208
+
padding: 0;
209
+
padding-bottom: 5px;
210
+
}
211
+
212
+
.quotingText {
213
+
font-size: 0.7em;
214
+
margin: 0;
215
+
padding: 0;
216
+
padding-bottom: 5px;
217
+
}
218
+
219
+
.postText {
220
+
margin: 0;
221
+
padding: 0;
222
+
overflow-wrap: break-word;
223
+
word-wrap: normal;
224
+
word-break: break-word;
225
+
hyphens: none;
226
+
}
227
+
228
+
.carouselContainer {
229
+
position: relative;
230
+
width: 100%;
231
+
margin-top: 10px;
232
+
display: flex;
233
+
flex-direction: column;
234
+
align-items: center;
235
+
}
236
+
237
+
.carouselControls {
238
+
display: flex;
239
+
justify-content: space-between;
240
+
align-items: center;
241
+
width: 100%;
242
+
max-width: 500px;
243
+
margin-top: 5px;
244
+
}
245
+
246
+
.carouselIndicators {
247
+
display: flex;
248
+
gap: 5px;
249
+
}
250
+
251
+
.indicator {
252
+
width: 8px;
253
+
height: 8px;
254
+
background-color: var(--indicator-inactive-color);
255
+
}
256
+
257
+
.indicator.active {
258
+
background-color: var(--indicator-active-color);
259
+
}
260
+
261
+
.prevBtn,
262
+
.nextBtn {
263
+
background-color: rgba(31, 17, 69, 0.7);
264
+
color: var(--text-color);
265
+
border: 1px solid var(--border-color);
266
+
width: 30px;
267
+
height: 30px;
268
+
cursor: pointer;
269
+
display: flex;
270
+
align-items: center;
271
+
justify-content: center;
272
+
}
273
+
274
+
.prevBtn:disabled,
275
+
.nextBtn:disabled {
276
+
opacity: 0.5;
277
+
cursor: not-allowed;
278
+
}
279
+
280
+
.embedVideo {
281
+
width: 100%;
282
+
max-width: 500px;
283
+
margin-top: 10px;
284
+
align-self: center;
285
+
}
286
+
287
+
.embedImages {
288
+
min-width: min(100%, 500px);
289
+
max-width: min(100%, 500px);
290
+
max-height: 500px;
291
+
object-fit: contain;
292
+
293
+
margin: 0;
294
+
}
295
+
296
+
/* Account Component */
297
+
.accountBtn {
298
+
appearance: none;
299
+
border: none;
300
+
background-color: transparent;
301
+
padding: 0;
302
+
position: relative;
303
+
}
304
+
305
+
.accountContainer {
306
+
padding: 0px;
307
+
border: 1px solid var(--border-color);
308
+
min-height: 30px;
309
+
overflow: hidden;
310
+
border-radius: 3px;
311
+
}
312
+
313
+
.accountContainer img.avatar {
314
+
width: 100%;
315
+
height: auto;
316
+
border-right: none;
317
+
display: block;
318
+
}
319
+
320
+
.accountName {
321
+
margin-left: 10px;
322
+
font-size: 1em;
323
+
max-width: 80%;
324
+
overflow: hidden;
325
+
text-overflow: ellipsis;
326
+
white-space: nowrap;
327
+
}
328
+
329
+
.accountTooltip {
330
+
border: 1px var(--border-color) solid;
331
+
color: var(--text-color);
332
+
background-color: var(--content-background-color);
333
+
backdrop-filter: blur(5px);
334
+
border-radius: 3px;
335
+
padding: 10px;
336
+
text-align: left;
337
+
font-size: 13px;
338
+
width: 300px;
339
+
}
340
+
341
+
.banner {
342
+
position: relative;
343
+
margin-bottom: 10px;
344
+
width: calc(100% + 20px);
345
+
margin-left: -10px;
346
+
margin-right: -10px;
347
+
margin-top: -10px;
348
+
}
349
+
350
+
.banner img.bannerImg {
351
+
width: 100%;
352
+
height: 100px;
353
+
object-fit: cover;
354
+
display: block;
355
+
border-bottom: 1px var(--border-color) solid;
356
+
}
357
+
358
+
.banner .avatarInsetImg {
359
+
position: absolute;
360
+
left: 10px;
361
+
top: 60px;
362
+
border-radius: 3px;
363
+
border: 1px var(--border-color) solid;
364
+
}
365
+
366
+
.accountTooltip .displayName {
367
+
font-weight: bold;
368
+
}
369
+
370
+
.no-avatar {
371
+
372
+
}
373
+
374
+
/* Source Link */
375
+
376
+
#footer {
377
+
position: absolute;
378
+
bottom: 5px;
379
+
right: 5px;
380
+
z-index: 99;
381
+
opacity: .8;
382
+
transition: 0.3s;
383
+
font-size: .8em;
384
+
text-transform: lowercase;
385
+
background-color: rgba(255,255,255,.5);
386
+
padding: 2px 5px;
387
+
border-radius: 10px;
388
+
}
389
+
390
+
#footer:hover {
391
+
opacity: 1;
392
+
}
393
+
394
+
/* Responsive Styling */
395
+
@media screen and (max-width: 600px) {
396
+
#accountsList {
397
+
grid-template-columns: repeat(6, 1fr);
398
+
}
399
+
}
400
+
401
+
/* Scrollbars */
402
+
403
+
::-webkit-scrollbar {
404
+
width: 0px;
405
+
background: transparent;
406
+
padding: 0;
407
+
margin: 0;
408
+
}
409
+
::-webkit-scrollbar-thumb {
410
+
background: transparent;
411
+
border-radius: 0;
412
+
}
413
+
::-webkit-scrollbar-track {
414
+
background: transparent;
415
+
border-radius: 0;
416
+
}
417
+
::-webkit-scrollbar-corner {
418
+
background: transparent;
419
+
border-radius: 0;
420
+
}
421
+
::-webkit-scrollbar-button {
422
+
background: transparent;
423
+
border-radius: 0;
424
+
}
425
+
426
+
* {
427
+
scrollbar-width: none;
428
+
scrollbar-color: transparent transparent;
429
+
-ms-overflow-style: none; /* IE and Edge */
430
+
-webkit-overflow-scrolling: touch;
431
+
-webkit-scrollbar: none; /* Safari */
432
+
}
+423
themes/default/theme.css
+423
themes/default/theme.css
···
1
+
/* Modern Theme for pds-dash */
2
+
3
+
:root {
4
+
/* Modern color palette */
5
+
--primary-h: 243;
6
+
--link-color: hsl(var(--primary-h), 73%, 59%);
7
+
--link-hover-color: #4338ca;
8
+
--time-color: #8b5cf6;
9
+
--background-color: #f8fafc;
10
+
--header-background-color: #ffffff;
11
+
--content-background-color: #ffffff;
12
+
--text-color: #111827;
13
+
--text-secondary-color: #4b5563;
14
+
--border-color: #e2e8f0;
15
+
--indicator-inactive-color: #cbd5e1;
16
+
--indicator-active-color: #6366f1;
17
+
18
+
/* Modern shadows */
19
+
--button-hover: #f3f4f6;
20
+
}
21
+
22
+
23
+
body {
24
+
margin: 0;
25
+
display: flex;
26
+
place-items: center;
27
+
min-width: 320px;
28
+
min-height: 100vh;
29
+
background-color: var(--background-color);
30
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
31
+
font-size: 18px;
32
+
line-height: 1.5;
33
+
color: var(--text-color);
34
+
border-color: var(--border-color);
35
+
overflow-wrap: break-word;
36
+
word-break: break-word;
37
+
hyphens: none;
38
+
}
39
+
40
+
a {
41
+
font-weight: 500;
42
+
color: var(--link-color);
43
+
text-decoration: none;
44
+
transition: color 0.15s ease;
45
+
}
46
+
a:hover {
47
+
color: var(--link-hover-color);
48
+
}
49
+
50
+
h1 {
51
+
font-size: 2.5em;
52
+
line-height: 1.2;
53
+
font-weight: 700;
54
+
}
55
+
56
+
#app {
57
+
max-width: 1400px;
58
+
width: 100%;
59
+
margin: 0 auto;
60
+
padding: 0;
61
+
text-align: center;
62
+
}
63
+
64
+
/* Post Component */
65
+
#postContainer {
66
+
display: flex;
67
+
flex-direction: column;
68
+
border-radius: 12px;
69
+
border: 1px solid var(--border-color);
70
+
background-color: var(--content-background-color);
71
+
margin-bottom: 20px;
72
+
overflow-wrap: break-word;
73
+
overflow: hidden;
74
+
box-shadow: var(--card-shadow);
75
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
76
+
}
77
+
78
+
#postContainer:hover {
79
+
transform: translateY(-2px);
80
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
81
+
}
82
+
83
+
#postHeader {
84
+
display: flex;
85
+
flex-direction: row;
86
+
align-items: center;
87
+
justify-content: start;
88
+
background-color: var(--header-background-color);
89
+
padding: 12px 16px;
90
+
height: 60px;
91
+
border-bottom: 1px solid var(--border-color);
92
+
font-weight: 600;
93
+
overflow-wrap: break-word;
94
+
}
95
+
96
+
#displayName {
97
+
display: block;
98
+
color: var(--text-color);
99
+
font-size: 1.1em;
100
+
padding: 0;
101
+
margin: 0 0 2px 0;
102
+
text-overflow: ellipsis;
103
+
overflow: hidden;
104
+
white-space: nowrap;
105
+
width: 100%;
106
+
letter-spacing: -0.01em;
107
+
}
108
+
109
+
#handle {
110
+
display: flex;
111
+
align-items: center;
112
+
color: #6b7280;
113
+
font-size: 0.85em;
114
+
font-weight: 400;
115
+
padding: 0;
116
+
margin: 0;
117
+
gap: 8px;
118
+
}
119
+
120
+
#postLink {
121
+
color: var(--time-color);
122
+
font-size: 0.85em;
123
+
padding: 0;
124
+
margin: 0;
125
+
opacity: 0.9;
126
+
}
127
+
128
+
#postContent {
129
+
display: flex;
130
+
text-align: start;
131
+
flex-direction: column;
132
+
padding: 16px;
133
+
background-color: var(--content-background-color);
134
+
color: var(--text-color);
135
+
overflow-wrap: break-word;
136
+
white-space: pre-line;
137
+
line-height: 1.6;
138
+
}
139
+
140
+
#replyingText, #quotingText {
141
+
font-size: 0.8em;
142
+
margin: 0;
143
+
padding: 0 0 10px 0;
144
+
color: #6b7280;
145
+
}
146
+
147
+
#postText {
148
+
margin: 0 0 8px 0;
149
+
padding: 0;
150
+
overflow-wrap: break-word;
151
+
word-break: break-word;
152
+
hyphens: none;
153
+
font-size: 1.05em;
154
+
}
155
+
156
+
#headerText {
157
+
margin-left: 12px;
158
+
font-size: 0.9em;
159
+
text-align: start;
160
+
word-break: break-word;
161
+
max-width: 80%;
162
+
max-height: 95%;
163
+
overflow: hidden;
164
+
align-self: flex-start;
165
+
margin-top: auto;
166
+
margin-bottom: auto;
167
+
}
168
+
169
+
#carouselContainer {
170
+
position: relative;
171
+
width: 100%;
172
+
margin-top: 12px;
173
+
display: flex;
174
+
flex-direction: column;
175
+
align-items: center;
176
+
border-radius: 8px;
177
+
overflow: hidden;
178
+
}
179
+
180
+
#carouselControls {
181
+
display: flex;
182
+
justify-content: space-between;
183
+
align-items: center;
184
+
width: 100%;
185
+
max-width: 500px;
186
+
margin-top: 10px;
187
+
}
188
+
189
+
#carouselIndicators {
190
+
display: flex;
191
+
gap: 6px;
192
+
}
193
+
194
+
.indicator {
195
+
width: 6px;
196
+
height: 6px;
197
+
background-color: var(--indicator-inactive-color);
198
+
border-radius: 50%;
199
+
transition: background-color 0.2s ease, transform 0.2s ease;
200
+
}
201
+
202
+
.indicator.active {
203
+
background-color: var(--indicator-active-color);
204
+
transform: scale(1.3);
205
+
}
206
+
207
+
#prevBtn,
208
+
#nextBtn {
209
+
background-color: var(--button-bg);
210
+
color: var(--text-color);
211
+
border: 1px solid var(--border-color);
212
+
width: 32px;
213
+
height: 32px;
214
+
cursor: pointer;
215
+
display: flex;
216
+
align-items: center;
217
+
justify-content: center;
218
+
border-radius: 50%;
219
+
transition: background-color 0.15s ease, transform 0.15s ease;
220
+
font-size: 16px;
221
+
}
222
+
223
+
#prevBtn:hover:not(:disabled),
224
+
#nextBtn:hover:not(:disabled) {
225
+
background-color: var(--button-hover);
226
+
transform: scale(1.05);
227
+
}
228
+
229
+
#prevBtn:disabled,
230
+
#nextBtn:disabled {
231
+
opacity: 0.4;
232
+
cursor: not-allowed;
233
+
}
234
+
235
+
#embedVideo {
236
+
width: 100%;
237
+
max-width: 500px;
238
+
margin-top: 12px;
239
+
align-self: center;
240
+
border-radius: 8px;
241
+
overflow: hidden;
242
+
}
243
+
244
+
#embedImages {
245
+
min-width: min(100%, 500px);
246
+
max-width: min(100%, 500px);
247
+
max-height: 500px;
248
+
object-fit: contain;
249
+
margin: 0;
250
+
border-radius: 8px;
251
+
}
252
+
253
+
/* Account Component */
254
+
#accountContainer {
255
+
display: flex;
256
+
text-align: start;
257
+
align-items: center;
258
+
background-color: var(--content-background-color);
259
+
padding: 12px;
260
+
margin-bottom: 15px;
261
+
border: 1px solid var(--border-color);
262
+
border-radius: 12px;
263
+
transition: background-color 0.15s ease;
264
+
}
265
+
266
+
#accountContainer:hover {
267
+
background-color: var(--hover-bg);
268
+
}
269
+
270
+
#accountName {
271
+
margin-left: 12px;
272
+
font-size: 0.95em;
273
+
max-width: 80%;
274
+
overflow: hidden;
275
+
text-overflow: ellipsis;
276
+
white-space: nowrap;
277
+
font-weight: 500;
278
+
}
279
+
280
+
#avatar {
281
+
width: 48px;
282
+
height: 48px;
283
+
margin: 0;
284
+
object-fit: cover;
285
+
border-radius: 50%;
286
+
border: 2px solid white;
287
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
288
+
}
289
+
290
+
/* App.Svelte Layout */
291
+
#Content {
292
+
display: flex;
293
+
width: 100%;
294
+
height: 100%;
295
+
flex-direction: row;
296
+
justify-content: space-between;
297
+
align-items: center;
298
+
background-color: var(--background-color);
299
+
color: var(--text-color);
300
+
gap: 24px;
301
+
}
302
+
303
+
#Feed {
304
+
overflow-y: auto;
305
+
width: 65%;
306
+
height: 100vh;
307
+
padding-right: 16px;
308
+
align-self: flex-start;
309
+
}
310
+
311
+
#spacer {
312
+
padding: 0;
313
+
margin: 0;
314
+
height: 10vh;
315
+
width: 100%;
316
+
}
317
+
318
+
#Account {
319
+
width: 35%;
320
+
display: flex;
321
+
flex-direction: column;
322
+
border: 1px solid var(--border-color);
323
+
background-color: var(--content-background-color);
324
+
max-height: 80vh;
325
+
padding: 24px;
326
+
margin-left: 16px;
327
+
border-radius: 12px;
328
+
box-shadow: var(--card-shadow);
329
+
}
330
+
331
+
#accountsList {
332
+
display: flex;
333
+
flex-direction: column;
334
+
overflow-y: auto;
335
+
height: 100%;
336
+
width: 100%;
337
+
padding: 8px 0;
338
+
margin: 0;
339
+
}
340
+
341
+
#Header {
342
+
text-align: center;
343
+
font-size: 1.8em;
344
+
margin-bottom: 16px;
345
+
font-weight: 700;
346
+
background: linear-gradient(to right, var(--link-color), #8b5cf6);
347
+
-webkit-background-clip: text;
348
+
-webkit-text-fill-color: transparent;
349
+
background-clip: text;
350
+
}
351
+
352
+
/* Mobile Styles */
353
+
@media screen and (max-width: 768px) {
354
+
#Content {
355
+
flex-direction: column;
356
+
width: auto;
357
+
padding: 12px;
358
+
margin-top: 0;
359
+
}
360
+
361
+
#Account {
362
+
width: calc(100% - 32px);
363
+
padding: 16px;
364
+
margin-bottom: 20px;
365
+
margin-left: 0;
366
+
margin-right: 0;
367
+
height: auto;
368
+
order: -1;
369
+
}
370
+
371
+
#Feed {
372
+
width: 100%;
373
+
margin: 0;
374
+
padding: 0;
375
+
overflow-y: visible;
376
+
}
377
+
378
+
#spacer {
379
+
height: 5vh;
380
+
}
381
+
382
+
body {
383
+
font-size: 16px;
384
+
}
385
+
386
+
#postHeader {
387
+
padding: 10px;
388
+
height: auto;
389
+
min-height: 50px;
390
+
}
391
+
}
392
+
393
+
/* Scrollbar Styles */
394
+
::-webkit-scrollbar {
395
+
width: 0px;
396
+
background: transparent;
397
+
padding: 0;
398
+
margin: 0;
399
+
}
400
+
::-webkit-scrollbar-thumb {
401
+
background: transparent;
402
+
border-radius: 0;
403
+
}
404
+
::-webkit-scrollbar-track {
405
+
background: transparent;
406
+
border-radius: 0;
407
+
}
408
+
::-webkit-scrollbar-corner {
409
+
background: transparent;
410
+
border-radius: 0;
411
+
}
412
+
::-webkit-scrollbar-button {
413
+
background: transparent;
414
+
border-radius: 0;
415
+
}
416
+
417
+
* {
418
+
scrollbar-width: none;
419
+
scrollbar-color: transparent transparent;
420
+
-ms-overflow-style: none; /* IE and Edge */
421
+
-webkit-overflow-scrolling: touch;
422
+
-webkit-scrollbar: none; /* Safari */
423
+
}
+375
themes/express/theme.css
+375
themes/express/theme.css
···
1
+
@import url("https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap");
2
+
3
+
:root {
4
+
/* Color overrides, edit to whatever you want */
5
+
--primary-h: 341; /* Hue */
6
+
--background-color: hsl(var(--primary-h), 62%, 30%);
7
+
--text-color: hsl(var(--primary-h), 69%, 18%);
8
+
--link-color: hsl(var(--primary-h), 100%, 20%);
9
+
--link-hover-color: hsl(var(--primary-h), 20%, 20%);
10
+
--border-color: hsl(var(--primary-h), 59%, 52%);
11
+
--content-background-color: hsl(var(--primary-h), 97%, 73%);
12
+
13
+
--header-background-color: hsl(var(--primary-h), 97%, 73%);
14
+
--indicator-inactive-color: #4a4a4a;
15
+
--indicator-active-color: var(--border-color);
16
+
}
17
+
18
+
a {
19
+
font-weight: 500;
20
+
color: var(--link-color);
21
+
text-decoration: inherit;
22
+
}
23
+
a:hover {
24
+
color: var(--link-hover-color);
25
+
text-decoration: underline;
26
+
}
27
+
28
+
body {
29
+
margin: 0;
30
+
display: flex;
31
+
place-items: center;
32
+
min-width: 320px;
33
+
min-height: 100vh;
34
+
background-color: var(--background-color);
35
+
font-family: "Share Tech Mono", monospace;
36
+
font-size: 24px;
37
+
color: var(--text-color);
38
+
border-color: var(--border-color);
39
+
overflow-wrap: break-word;
40
+
word-wrap: normal;
41
+
word-break: break-word;
42
+
hyphens: none;
43
+
}
44
+
45
+
h1 {
46
+
font-size: 3.2em;
47
+
line-height: 1.1;
48
+
}
49
+
50
+
#app {
51
+
max-width: 1400px;
52
+
width: 100%;
53
+
margin: 0;
54
+
padding: 0;
55
+
margin-left: auto;
56
+
margin-right: auto;
57
+
text-align: center;
58
+
}
59
+
60
+
/* Post Component */
61
+
a:hover {
62
+
text-decoration: underline;
63
+
}
64
+
#postContainer {
65
+
display: flex;
66
+
flex-direction: column;
67
+
border: 4px solid var(--border-color);
68
+
background-color: var(--background-color);
69
+
margin-bottom: 15px;
70
+
overflow-wrap: break-word;
71
+
box-shadow: var(--border-color) 10px 10px;
72
+
}
73
+
#postHeader {
74
+
display: flex;
75
+
flex-direction: row;
76
+
align-items: center;
77
+
justify-content: start;
78
+
background-color: var(--header-background-color);
79
+
padding: 0px 0px;
80
+
height: fit-content;
81
+
82
+
font-weight: bold;
83
+
overflow-wrap: break-word;
84
+
height: 64px;
85
+
}
86
+
#displayName {
87
+
display: block;
88
+
color: var(--text-color);
89
+
font-size: 1.2em;
90
+
padding: 0;
91
+
margin: 0;
92
+
overflow-wrap: normal;
93
+
word-wrap: break-word;
94
+
word-break: break-word;
95
+
text-overflow: ellipsis;
96
+
overflow: hidden;
97
+
white-space: nowrap;
98
+
width: 100%;
99
+
}
100
+
#handle {
101
+
display: block;
102
+
color: var(--border-color);
103
+
font-size: 0.8em;
104
+
padding: 0;
105
+
margin: 0;
106
+
}
107
+
108
+
#postLink {
109
+
color: var(--link-hover-color);
110
+
font-size: 0.8em;
111
+
padding: 0;
112
+
margin: 0;
113
+
}
114
+
#postContent {
115
+
display: flex;
116
+
text-align: start;
117
+
flex-direction: column;
118
+
padding: 10px;
119
+
background-color: var(--content-background-color);
120
+
color: var(--text-color);
121
+
overflow-wrap: break-word;
122
+
white-space: pre-line;
123
+
}
124
+
#replyingText {
125
+
font-size: 0.7em;
126
+
margin: 0;
127
+
padding: 0;
128
+
padding-bottom: 5px;
129
+
}
130
+
#quotingText {
131
+
font-size: 0.7em;
132
+
margin: 0;
133
+
padding: 0;
134
+
padding-bottom: 5px;
135
+
}
136
+
#postText {
137
+
margin: 0;
138
+
padding: 0;
139
+
overflow-wrap: break-word;
140
+
word-wrap: normal;
141
+
word-break: break-word;
142
+
hyphens: none;
143
+
}
144
+
#headerText {
145
+
margin-left: 10px;
146
+
font-size: 0.9em;
147
+
text-align: start;
148
+
word-break: break-word;
149
+
max-width: 80%;
150
+
max-height: 95%;
151
+
overflow: hidden;
152
+
align-self: flex-start;
153
+
margin-top: auto;
154
+
margin-bottom: auto;
155
+
}
156
+
#avatar {
157
+
height: 30px;
158
+
width: 30px;
159
+
overflow: hidden;
160
+
object-fit: cover;
161
+
}
162
+
#postContainer #avatar {
163
+
height: 60px;
164
+
width: 60px;
165
+
border-right: var(--border-color) 4px solid;
166
+
border-bottom: var(--border-color) 4px solid;
167
+
}
168
+
#carouselContainer {
169
+
position: relative;
170
+
width: 100%;
171
+
margin-top: 10px;
172
+
display: flex;
173
+
flex-direction: column;
174
+
align-items: center;
175
+
}
176
+
#carouselControls {
177
+
display: flex;
178
+
justify-content: space-between;
179
+
align-items: center;
180
+
width: 100%;
181
+
max-width: 500px;
182
+
margin-top: 5px;
183
+
}
184
+
#carouselIndicators {
185
+
display: flex;
186
+
gap: 5px;
187
+
}
188
+
.indicator {
189
+
width: 8px;
190
+
height: 8px;
191
+
background-color: var(--indicator-inactive-color);
192
+
}
193
+
.indicator.active {
194
+
background-color: var(--indicator-active-color);
195
+
}
196
+
#prevBtn,
197
+
#nextBtn {
198
+
background-color: rgba(31, 17, 69, 0.7);
199
+
color: var(--text-color);
200
+
border: 4px solid var(--border-color);
201
+
width: 30px;
202
+
height: 30px;
203
+
cursor: pointer;
204
+
display: flex;
205
+
align-items: center;
206
+
justify-content: center;
207
+
}
208
+
#prevBtn:disabled,
209
+
#nextBtn:disabled {
210
+
opacity: 0.5;
211
+
cursor: not-allowed;
212
+
}
213
+
#embedVideo {
214
+
width: 100%;
215
+
max-width: 500px;
216
+
margin-top: 10px;
217
+
align-self: center;
218
+
}
219
+
220
+
#embedImages {
221
+
min-width: min(100%, 500px);
222
+
max-width: min(100%, 500px);
223
+
max-height: 500px;
224
+
object-fit: contain;
225
+
226
+
margin: 0;
227
+
}
228
+
229
+
/* Account Component */
230
+
#accountContainer {
231
+
display: flex;
232
+
text-align: start;
233
+
align-items: center;
234
+
background-color: var(--header-background-color);
235
+
padding: 0px;
236
+
margin-bottom: 15px;
237
+
margin-right: 4px;
238
+
border: 4px solid var(--border-color);
239
+
box-shadow: var(--border-color) 10px 10px;
240
+
min-height: 30px;
241
+
}
242
+
#accountName {
243
+
margin-left: 10px;
244
+
font-size: 1em;
245
+
max-width: 80%;
246
+
247
+
/* replace overflow with ellipsis */
248
+
overflow: hidden;
249
+
text-overflow: ellipsis;
250
+
white-space: nowrap;
251
+
}
252
+
253
+
.no-avatar {
254
+
margin-left: 40px !important;
255
+
}
256
+
257
+
/* App.Svelte */
258
+
/* desktop style */
259
+
260
+
#Content {
261
+
display: flex;
262
+
/* split the screen in half, left for accounts, right for posts */
263
+
width: 100%;
264
+
height: 100%;
265
+
flex-direction: row;
266
+
justify-content: space-between;
267
+
align-items: center;
268
+
background-color: var(--background-color);
269
+
color: var(--text-color);
270
+
}
271
+
#Feed {
272
+
overflow-y: scroll;
273
+
width: 65%;
274
+
height: 100vh;
275
+
padding: 20px;
276
+
padding-bottom: 0;
277
+
padding-top: 0;
278
+
margin-top: 0;
279
+
margin-bottom: 0;
280
+
}
281
+
#spacer {
282
+
padding: 0;
283
+
margin: 0;
284
+
height: 10vh;
285
+
width: 100%;
286
+
}
287
+
#Account {
288
+
width: 35%;
289
+
display: flex;
290
+
flex-direction: column;
291
+
border: 4px solid var(--border-color);
292
+
background-color: var(--content-background-color);
293
+
box-shadow: var(--border-color) 10px 10px;
294
+
height: 80vh;
295
+
padding: 20px;
296
+
margin-left: 20px;
297
+
}
298
+
#accountsList {
299
+
display: flex;
300
+
flex-direction: column;
301
+
overflow-y: scroll;
302
+
height: 100%;
303
+
width: 100%;
304
+
padding: 0px;
305
+
margin: 0px;
306
+
}
307
+
308
+
#Header {
309
+
text-align: center;
310
+
font-size: 2em;
311
+
margin-bottom: 20px;
312
+
}
313
+
314
+
/* mobile style */
315
+
@media screen and (max-width: 600px) {
316
+
#Content {
317
+
flex-direction: column;
318
+
width: auto;
319
+
padding-left: 0px;
320
+
padding-right: 0px;
321
+
margin-top: 5%;
322
+
}
323
+
#Account {
324
+
width: 85%;
325
+
padding-left: 5%;
326
+
padding-right: 5%;
327
+
margin-bottom: 20px;
328
+
margin-left: 5%;
329
+
margin-right: 5%;
330
+
height: auto;
331
+
}
332
+
#Feed {
333
+
width: 95%;
334
+
margin: 0px;
335
+
margin-left: 10%;
336
+
margin-right: 10%;
337
+
padding: 0px;
338
+
overflow-y: visible;
339
+
}
340
+
341
+
#spacer {
342
+
height: 0;
343
+
}
344
+
}
345
+
346
+
::-webkit-scrollbar {
347
+
width: 0px;
348
+
background: transparent;
349
+
padding: 0;
350
+
margin: 0;
351
+
}
352
+
::-webkit-scrollbar-thumb {
353
+
background: transparent;
354
+
border-radius: 0;
355
+
}
356
+
::-webkit-scrollbar-track {
357
+
background: transparent;
358
+
border-radius: 0;
359
+
}
360
+
::-webkit-scrollbar-corner {
361
+
background: transparent;
362
+
border-radius: 0;
363
+
}
364
+
::-webkit-scrollbar-button {
365
+
background: transparent;
366
+
border-radius: 0;
367
+
}
368
+
369
+
* {
370
+
scrollbar-width: none;
371
+
scrollbar-color: transparent transparent;
372
+
-ms-overflow-style: none; /* IE and Edge */
373
+
-webkit-overflow-scrolling: touch;
374
+
-webkit-scrollbar: none; /* Safari */
375
+
}
+32
theming.ts
+32
theming.ts
···
1
+
import { Plugin } from 'vite';
2
+
import { Config } from './config';
3
+
4
+
5
+
// Replaces app.css with the contents of the file specified in the
6
+
// config file.
7
+
export const themePlugin = (): Plugin => {
8
+
const themeFolder = Config.THEME;
9
+
console.log(`Using theme folder: ${themeFolder}`);
10
+
return {
11
+
name: 'theme-generator',
12
+
enforce: 'pre', // Ensure this plugin runs first
13
+
transform(code, id) {
14
+
if (id.endsWith('app.css')) {
15
+
// Read the theme file and replace the contents of app.css with it
16
+
// Needs full path to the file
17
+
const themeCode = Deno.readTextFileSync(Deno.cwd() + '/themes/' + themeFolder + '/theme.css');
18
+
// Replace the contents of app.css with the theme code
19
+
20
+
// and add a comment at the top
21
+
const themeComment = `/* Generated from ${themeFolder} */\n`;
22
+
const themeCodeWithComment = themeComment + themeCode;
23
+
// Return the theme code as the new contents of app.css
24
+
return {
25
+
code: themeCodeWithComment,
26
+
map: null,
27
+
};
28
+
}
29
+
return null;
30
+
}
31
+
};
32
+
};
+20
tsconfig.app.json
+20
tsconfig.app.json
···
1
+
{
2
+
"extends": "@tsconfig/svelte/tsconfig.json",
3
+
"compilerOptions": {
4
+
"target": "ESNext",
5
+
"useDefineForClassFields": true,
6
+
"module": "ESNext",
7
+
"resolveJsonModule": true,
8
+
/**
9
+
* Typecheck JS in `.svelte` and `.js` files by default.
10
+
* Disable checkJs if you'd like to use dynamic types in JS.
11
+
* Note that setting allowJs false does not prevent the use
12
+
* of JS in `.svelte` files.
13
+
*/
14
+
"allowJs": true,
15
+
"checkJs": true,
16
+
"isolatedModules": true,
17
+
"moduleDetection": "force"
18
+
},
19
+
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
20
+
}
+7
tsconfig.json
+7
tsconfig.json
+24
tsconfig.node.json
+24
tsconfig.node.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+
"target": "ES2022",
5
+
"lib": ["ES2023"],
6
+
"module": "ESNext",
7
+
"skipLibCheck": true,
8
+
9
+
/* Bundler mode */
10
+
"moduleResolution": "bundler",
11
+
"allowImportingTsExtensions": true,
12
+
"isolatedModules": true,
13
+
"moduleDetection": "force",
14
+
"noEmit": true,
15
+
16
+
/* Linting */
17
+
"strict": true,
18
+
"noUnusedLocals": true,
19
+
"noUnusedParameters": true,
20
+
"noFallthroughCasesInSwitch": true,
21
+
"noUncheckedSideEffectImports": true
22
+
},
23
+
"include": ["vite.config.ts"]
24
+
}
+11
vite.config.ts
+11
vite.config.ts