Compare commits

...

454 Commits

Author SHA1 Message Date
a45fd152df 0.46.16 2026-02-12 21:07:57 +01:00
60f01d859e Restored compilation global filter position. 2026-02-12 21:07:55 +01:00
6d4e033fb7 0.46.15 2026-02-12 21:07:16 +01:00
bf635df863 Added extreme insertion global filter. 2026-02-12 21:07:14 +01:00
1732b4cf4d 0.46.14 2026-02-12 01:28:36 +01:00
a2e268913a Added Matrix link to footer. 2026-02-12 01:28:34 +01:00
dfb04e5e01 0.46.13 2026-02-08 21:41:22 +01:00
c0ce844169 Fixed scene tile banner styling disrupting other banner positions. 2026-02-08 21:41:20 +01:00
56acc42f17 0.46.12 2026-02-08 05:19:01 +01:00
7cee6639e7 Showing chapter posters before scene photos in scene album. 2026-02-08 05:18:58 +01:00
62753a4af0 0.46.11 2026-02-08 02:02:16 +01:00
2d4d2b1105 Fixed scene chapter photos not curated. 2026-02-08 02:02:14 +01:00
09ed130327 0.46.10 2026-02-07 17:47:11 +01:00
1414a846ec Added edit tooltips. 2026-02-07 17:47:10 +01:00
19dc029e28 Improved chapter date display. 2026-02-07 05:54:17 +01:00
67176db933 0.46.9 2026-02-07 05:10:06 +01:00
95e8982696 Showing heading and dates on chapters. 2026-02-07 05:10:02 +01:00
b8a03cd6fb 0.46.8 2026-02-07 02:03:17 +01:00
aad4ff8079 Centered banner blur background. 2026-02-07 02:03:15 +01:00
8821b3a00d 0.46.7 2026-02-07 01:11:55 +01:00
db43102487 Added blurred backdrop to scene tile banners to compensate for lack of upscaling. 2026-02-07 01:11:53 +01:00
a3072a4967 0.46.6 2026-02-07 00:56:05 +01:00
983e24835f Removed auto width from tile banners to prevent blurry upscaling. Fixed tag photo paths. 2026-02-07 00:56:02 +01:00
1c982124b0 0.46.5 2026-02-06 23:55:08 +01:00
6aaa3ad30c Allowing campaigns to be marked as non-global. 2026-02-06 23:55:06 +01:00
57dfa621df 0.46.4 2026-02-06 23:05:54 +01:00
e98254d444 Fixed tag photo paths. Fixed affiliate prefix slash logic. 2026-02-06 23:05:52 +01:00
56defe2c6f 0.46.3 2026-02-06 21:49:00 +01:00
a2f08c540c Switched scene tile URL back to watch URL. 2026-02-06 21:48:58 +01:00
217decee06 0.46.2 2026-02-06 21:43:29 +01:00
d93baf80ab Improved affiliate URL calculation. 2026-02-06 21:43:26 +01:00
e409f3c214 0.46.1 2026-02-04 06:37:44 +01:00
a1e080c20d Fixed search breaking due missing restrictions, added restrictions to API calls. 2026-02-04 06:37:41 +01:00
6c8fce49d6 0.46.0 2026-02-04 05:39:16 +01:00
1a84f899e7 Added georestriction with SFW mode. 2026-02-04 05:39:14 +01:00
ce107e6b65 0.45.11 2026-02-03 00:04:30 +01:00
515d3885c7 Fixed banner URL not resolving to affiliate definition. 2026-02-03 00:04:29 +01:00
5194c5e156 0.45.10 2026-02-02 23:48:16 +01:00
5b53f53fd3 Improved banner URL calculation. 2026-02-02 23:48:13 +01:00
750b30d896 0.45.9 2026-01-31 00:39:49 +01:00
b9afa61e01 Fixed query-based affiliate URL getting skipped. 2026-01-31 00:39:33 +01:00
490be8800a 0.45.8 2026-01-30 23:20:23 +01:00
49ee6b4eee Fixed unresolved affiliate scene URL breaking. 2026-01-30 23:20:21 +01:00
ada81340ef 0.45.7 2026-01-30 22:39:35 +01:00
5ae3b5d91c Ensuring affiliate URL is valid. 2026-01-30 22:39:34 +01:00
bff3bc6a0b 0.45.6 2026-01-30 22:19:23 +01:00
5496bced59 Optionally chaining user abilities until everyone's logged out and back in. 2026-01-30 22:19:21 +01:00
030d6dc835 0.45.5 2026-01-30 06:03:06 +01:00
fc46ae00f8 Added plain URL for privileged users. 2026-01-30 06:03:03 +01:00
e22978cbe4 0.45.4 2026-01-29 22:23:34 +01:00
70049ed495 Fixed entity affiliate URL generator breaking if no entity URL exists, falling back on parent URL. 2026-01-29 22:23:32 +01:00
9e20af925f 0.45.3 2026-01-29 21:51:26 +01:00
457afa5043 Allowing parent affiliate URL if channel uses the same URL as network. 2026-01-29 21:51:21 +01:00
c536a75f3d Added support for entryId in affiliate links. 2026-01-29 21:29:26 +01:00
a2d5828fda 0.45.2 2026-01-29 04:45:40 +01:00
52d041c591 Retired visitor database connection. Fixed empty traxxx breaking on missing batch IDs. 2026-01-29 04:45:38 +01:00
3bee1ac97d 0.45.1 2026-01-28 01:17:58 +01:00
428d86b1ee Fixed scene pages breaking if user is not logged in. 2026-01-28 01:17:56 +01:00
5facacd066 0.45.0 2026-01-28 00:58:14 +01:00
0bf0b716b2 Added dynamic affiliate URLs and video player restrictions. 2026-01-28 00:57:47 +01:00
31c62e01f9 0.44.6 2026-01-27 03:07:18 +01:00
a57b66cd95 Removed stray comment. 2026-01-27 03:07:16 +01:00
e4675e6e97 Removed showcase missing date filter, try disabling showcase on specific problematic channels. 2026-01-27 03:06:50 +01:00
bac0b768e2 0.44.5 2026-01-27 01:04:30 +01:00
74c69c698e PM2 ecosystem file calls src/app directly, should fix cluster problem. 2026-01-27 01:04:28 +01:00
87604ed848 0.44.4 2026-01-26 16:56:57 +01:00
b6ca08727f Darkening instead of lightening blurred scene banner background in dark theme. 2026-01-26 16:56:55 +01:00
af99491533 0.44.3 2026-01-26 16:52:37 +01:00
461f6cf8fd Fixed fingerprint row highlight color. 2026-01-26 16:52:34 +01:00
2ad17c2279 0.44.2 2026-01-26 16:50:08 +01:00
9f3bc6e8de Fixed fingerprint heading dark theme color. 2026-01-26 16:50:06 +01:00
6dedf10846 0.44.1 2026-01-26 02:44:34 +01:00
6b6e31a1bb Fixed auth page stuck on submitted on error. 2026-01-26 02:44:31 +01:00
f7016609a0 0.44.0 2026-01-26 02:02:40 +01:00
fde2d607b8 Displaying fingerprints on scene page. 2026-01-26 02:02:35 +01:00
54e9fd9f6a 0.43.4 2026-01-24 18:21:52 +01:00
886f02c5fc Further bio spacing improvements. 2026-01-24 18:21:50 +01:00
8d57dfd2d2 0.43.3 2026-01-24 18:15:15 +01:00
f9ba519dea Removed min-width from actor bio columns. 2026-01-24 18:15:13 +01:00
2eb4678afc 0.43.2 2026-01-24 18:08:43 +01:00
9558ce80b4 Improved actor bio scaling. 2026-01-24 18:08:41 +01:00
4569930a81 0.43.1 2026-01-24 17:59:23 +01:00
52b012402e Uncommented client-side CAPTCHA completion check. 2026-01-24 17:59:21 +01:00
82ff813225 0.43.0 2026-01-24 17:53:03 +01:00
b7bd0fac03 Integrated hCaptcha. 2026-01-24 17:53:01 +01:00
9933b4fbf0 0.42.28 2026-01-24 01:38:54 +01:00
c272e6c8b3 Fixed expand button showing behind bio bar. 2026-01-24 01:38:52 +01:00
302f6a0621 0.42.27 2026-01-24 01:36:17 +01:00
23155520d2 Hiding serie poster container if no poster is present. 2026-01-24 01:36:15 +01:00
b788d78aab 0.42.26 2026-01-23 05:25:39 +01:00
cd9e4a5e8d Fixed date display on serie page. 2026-01-23 05:25:36 +01:00
8ec48ec43e 0.42.25 2026-01-23 03:33:05 +01:00
a27bc2c815 Separated scene and entity affiliate replace. 2026-01-23 03:33:03 +01:00
16f43066a4 0.42.24 2026-01-23 03:26:34 +01:00
6191e17c4e Improved affiliate logic. 2026-01-23 03:26:29 +01:00
5ac7cfbc9a 0.42.23 2026-01-23 03:03:47 +01:00
bf802771de Improved affiliate entity logic. 2026-01-23 03:03:45 +01:00
559dc21189 0.42.22 2026-01-23 02:59:22 +01:00
7fdb915921 Not using parent affiliate URL for networks. 2026-01-23 02:59:20 +01:00
19f0752b0f 0.42.21 2026-01-23 02:13:54 +01:00
471ee42c0e Showing networks first in child entity list. 2026-01-23 02:13:53 +01:00
1e089f731a 0.42.20 2026-01-22 19:42:15 +01:00
c026988a7b Filter Woodman photos from actor page. 2026-01-22 19:42:12 +01:00
6281842a14 0.42.19 2026-01-22 05:58:07 +01:00
2380342328 Added improved affiliate URL logic for entities. 2026-01-22 05:58:05 +01:00
e28904b791 0.42.18 2026-01-22 03:48:54 +01:00
ad7f1ce1fa Using new object affiliate parameters. 2026-01-22 03:48:53 +01:00
327c7ab1db 0.42.17 2026-01-22 01:15:38 +01:00
30303a80d3 Not using NATS redirect URL for independent channels. Showing independent channels first in list. 2026-01-22 01:15:35 +01:00
66c1cbab6a 0.42.16 2026-01-20 03:34:47 +01:00
34348890ec Replaced double left join with lateral join in scene affiliate SQL. 2026-01-20 03:34:44 +01:00
edb4be379f 0.42.15 2026-01-20 02:43:30 +01:00
363a6b4084 Changed affiliate query to ensure channel priority. 2026-01-20 02:43:28 +01:00
fc5a0d209c 0.42.14 2026-01-19 06:05:09 +01:00
63bee8f5e0 Stripping /trial from affiliate URL. 2026-01-19 06:05:07 +01:00
166f4ee7ce Fixed scenes breaking if no affiliate is available. 2026-01-19 04:52:11 +01:00
7702839b7a 0.42.13 2026-01-19 04:46:42 +01:00
cb1c884503 Fixed NATS URL composition. 2026-01-19 04:46:40 +01:00
7bc9a90b81 0.42.12 2026-01-11 02:44:46 +01:00
b2ca5a6713 Fixed scene tile actor alignment so it's less likely to cut-off letters. 2026-01-11 02:44:44 +01:00
a4325f6ff6 0.42.11 2026-01-11 00:08:02 +01:00
03f57c1ef4 Fixed movie header filterbar clearance. 2026-01-11 00:07:59 +01:00
378e0edc75 0.42.10 2026-01-10 04:36:18 +01:00
a2622aa536 Fixed slugify behavior. 2026-01-10 04:36:15 +01:00
602765bfbb 0.42.9 2026-01-10 03:00:34 +01:00
e32dd55220 Fixed slugify not handling slugs as input. 2026-01-10 03:00:31 +01:00
4e181d5ff7 0.42.8 2026-01-09 06:35:32 +01:00
ba971035a0 Improved actor bio collapse behavior. 2026-01-09 06:35:30 +01:00
c214ccf201 0.42.7 2026-01-09 03:50:02 +01:00
414499636e Ignoring actor photos with unknown entropy. 2026-01-09 03:50:00 +01:00
d9bbd95fa1 0.42.6 2026-01-09 03:08:09 +01:00
b63037ef74 Hiding low-entropy photos from actor page. Fixed actors filter tab spacing. Fixed entity logo stretching on compact scene page. 2026-01-09 03:08:08 +01:00
37ccc3c3dd 0.42.5 2026-01-09 02:09:44 +01:00
a8bacaf083 Using local slugify. 2026-01-09 02:09:41 +01:00
0e23115cfe 0.42.4 2026-01-09 01:54:12 +01:00
e67ec5eca4 Using common slugify. 2026-01-09 01:54:10 +01:00
7a452db2f8 0.42.3 2026-01-06 01:54:51 +01:00
8b3e9d32d6 Added 5K and 8K to scene page quality map. 2026-01-06 01:54:49 +01:00
d8d2ee6785 0.42.2 2026-01-05 21:40:05 +01:00
77b9acea32 Improved search, linking results to scenes page, updating on input clear. 2026-01-05 21:40:03 +01:00
68f15d4f74 0.42.1 2025-12-30 04:55:08 +01:00
42a83b2126 Fixed header responsiveness. 2025-12-30 04:55:05 +01:00
a19c39d8eb 0.42.0 2025-12-30 04:49:34 +01:00
f06df01e70 Added easily accessible global scenes page with filters. 2025-12-30 04:49:29 +01:00
0527674333 0.41.25 2025-12-28 06:00:49 +01:00
8d6da08519 Using common for socials definitions. 2025-12-28 06:00:46 +01:00
2b338e32eb 0.41.24 2025-12-20 23:29:03 +01:00
aa8412863b Fixed tag tile breaking page if no poster is available. 2025-12-20 23:29:01 +01:00
d4a486d2ae 0.41.23 2025-12-19 23:23:24 +01:00
ceac4ecc56 Styled API key HTTP header instructions to clarify it is your literal API user ID 2025-12-19 23:23:21 +01:00
233829223e 0.41.22 2025-12-15 01:54:43 +01:00
456b69f1ca Fixed fallback affiliate query causing duplicate results in channel aggregate. 2025-12-15 01:54:41 +01:00
83efdf59d4 0.41.21 2025-12-13 04:24:50 +01:00
02f2629f6b Fixed width and height attributes on tag photos, fixes lazy loading. 2025-12-13 04:24:47 +01:00
6796f7f258 0.41.20 2025-12-13 04:00:17 +01:00
84b9bbd1b6 Showing tag poster and photos on tag page. Improved campaign fallback logic, fixes wrong ratio selected. 2025-12-13 04:00:14 +01:00
bc26e07812 0.41.19 2025-12-13 01:46:06 +01:00
e05ca80f7c Skipping seemingly redundant tags page scrolling logic that causes jittering in Firefox. Added hash exclude property to umami script attributes. 2025-12-13 01:46:00 +01:00
79eacee3f0 0.41.18 2025-11-23 03:10:20 +01:00
1fc77587ed Added 'compilation' to global tag filter. 2025-11-23 03:10:18 +01:00
77d37ce6b5 0.41.17 2025-11-23 03:07:53 +01:00
5dc829674a Showing boobs enhancement in bio if bust field is missing. 2025-11-23 03:07:47 +01:00
5ffc865c00 0.41.16 2025-11-21 06:17:49 +01:00
ae085ad5ec Limited ratio on scenes nav ad. 2025-11-21 06:17:46 +01:00
05a93293fe 0.41.15 2025-11-21 04:40:12 +01:00
117923ff1d Linked stash tile header to largest stash. 2025-11-21 04:40:10 +01:00
47a748c623 0.41.14 2025-11-18 02:17:57 +01:00
e9e0cf3600 Filtering missing items in summary actor details. 2025-11-18 02:17:54 +01:00
c530751a70 0.41.13 2025-11-18 00:41:43 +01:00
f4acee53c4 Added details prop to summary actors. 2025-11-18 00:41:41 +01:00
14bca958fd 0.41.12 2025-11-17 23:42:25 +01:00
0b5ce620d6 Added format option to actors, exposing age and gender. 2025-11-17 23:42:24 +01:00
0435472489 0.41.11 2025-11-17 17:36:04 +01:00
253052f75a Added markdown to built-in scene summary list. 2025-11-17 17:36:02 +01:00
27acb09ced 0.41.10 2025-11-14 22:52:18 +01:00
8f145e926e Added origin config to use in summaries. Added link property to summaries for local traxxx URL. 2025-11-14 22:52:16 +01:00
0dd4bcc7fe 0.41.9 2025-11-14 03:00:13 +01:00
b9c1c9914d Added custom text property to summary templates. Added URL to summary mock-up. 2025-11-14 03:00:10 +01:00
90da4b592a 0.41.8 2025-11-02 18:27:00 +01:00
ac1e44f427 Checking value before initializing date in revision curation. 2025-11-02 18:26:54 +01:00
880d6369e4 0.41.7 2025-11-02 18:19:23 +01:00
aad922ac30 Fixed scene duration getting lost during edit. 2025-11-02 18:19:21 +01:00
9c99e464aa Added Kelly Madison as popular network. 2025-10-06 05:43:18 +02:00
ebc2895d7e 0.41.6 2025-10-06 05:40:12 +02:00
45ed3be747 Fixed affiliate banner ratio on tags page. Added affiliate banners to actor page. 2025-10-06 05:40:09 +02:00
2afcdd6050 0.41.5 2025-10-06 05:20:19 +02:00
b355ef4bf5 Improved affiliate selection. 2025-10-06 05:20:17 +02:00
50280692e8 0.41.4 2025-10-05 17:53:38 +02:00
35699becf5 Re-added enhanced wand icon in bio, hiding enhancements bio section if no details are known. 2025-10-05 17:53:14 +02:00
6237aa0c03 0.41.3 2025-09-15 05:30:05 +02:00
b6398197ea Linking entities in entity health overview. 2025-09-15 05:30:03 +02:00
ea398a51aa 0.41.2 2025-09-15 05:23:33 +02:00
87a800edc9 Changed dead site alert threshold to weeks. 2025-09-15 05:23:28 +02:00
50d280a3c9 0.41.1 2025-09-15 05:19:44 +02:00
1de174a8c4 Added network to dead site overview. 2025-09-15 05:19:42 +02:00
c90d0c3f3c 0.41.0 2025-09-15 05:07:07 +02:00
32202d8ab5 Added rudimentary entity health overview. 2025-09-15 05:07:02 +02:00
37b40f1744 0.40.11 2025-08-27 05:13:09 +02:00
bd9a794e34 Added search icon to filter search inputs. 2025-08-27 05:13:07 +02:00
63ee4cae31 0.40.10 2025-08-27 04:51:30 +02:00
ddaf5c3b42 Added box cover filter to movies. 2025-08-27 04:51:27 +02:00
7b78724bb4 0.40.9 2025-08-27 04:42:38 +02:00
e4f410f293 Fixed actor countries filter disappearing if no countries are found. 2025-08-27 04:42:35 +02:00
55680b5150 0.40.8 2025-08-21 05:56:10 +02:00
1f7ad45393 Removed line clamping from scene and movie titles. 2025-08-21 05:56:07 +02:00
243d28a030 0.40.7 2025-05-26 21:47:56 +02:00
fecebee0c5 Fixed actor edit page breaking if avatar sharpness info is missing. 2025-05-26 21:47:33 +02:00
cb47e3fd9e Added actorIds and stashIds aliases to new alert API for consistency. 2025-04-04 06:09:56 +02:00
6492439f56 0.40.6 2025-04-04 06:03:05 +02:00
5ca478772c Using entityIds and alertIds fields in alert dialog API call for consistency. Improved new alert data validation. 2025-04-04 06:03:02 +02:00
dc876e7aa2 0.40.5 2025-04-04 05:56:34 +02:00
99cd551fc0 Resolving tag and entity slugs in alert API. 2025-04-04 05:56:32 +02:00
c37a2ce1a1 0.40.4 2025-04-04 05:36:49 +02:00
0dabbc7233 Exposed comment and meta fields in GraphQL API. 2025-04-04 05:36:47 +02:00
538e6ede5b 0.40.3 2025-04-01 02:14:41 +02:00
fe1a9ed26b Added GraphQL queries for alerts and notifications. 2025-04-01 02:14:36 +02:00
2121c51ae6 0.40.2 2025-04-01 00:55:43 +02:00
f5d8c30ff3 Exposing stashes on scenes GraphQL object. 2025-04-01 00:55:41 +02:00
acef14b02c 0.40.1 2025-03-31 23:01:31 +02:00
7dc1f78c80 Added optional API key auth to REST API. Returning HTTP status codes from GraphQL API. 2025-03-31 23:01:29 +02:00
73569704a5 0.40.0 2025-03-31 06:15:01 +02:00
1025285796 Added stash GraphQL mutations. Added movies to GraphQL queries. Moved key management to profile page, only for approved users. 2025-03-31 06:14:56 +02:00
09bba4fe1e Fixed template editor not resizable. 2025-03-24 00:14:19 +01:00
c93a7189ce 0.39.10 2025-03-24 00:04:40 +01:00
95008f9815 Fixed summary editor mockup not having a release date, resulting in no date showing unless fallback is explicitly enabled. 2025-03-24 00:04:38 +01:00
3bb96c3433 0.39.9 2025-03-20 03:10:52 +01:00
10a4201354 Always show summary section on scene page. 2025-03-20 03:10:50 +01:00
2a30aa5508 0.39.8 2025-03-20 02:39:28 +01:00
8f9baf6bc5 Filtering falsey values from summary template to prevent empty wraps. 2025-03-20 02:39:26 +01:00
e8f36ad9e4 0.39.7 2025-03-18 00:42:41 +01:00
8e9af7d6c4 Added date fallback configuration to summary templates, disabled by default. 2025-03-18 00:42:40 +01:00
a8ceb41f02 0.39.6 2025-03-13 17:56:19 +01:00
9b90189b07 Show uncombined alert count in remove alert bell. 2025-03-13 17:56:16 +01:00
9ba5d45153 0.39.5 2025-03-13 00:51:12 +01:00
2330d8ebb7 Fixed actor compact layout. 2025-03-13 00:51:10 +01:00
9b7876753f 0.39.4 2025-03-13 00:49:11 +01:00
7cebb1bcce Improved alert filter UI. 2025-03-13 00:49:09 +01:00
4e15c4ca03 0.39.3 2025-03-13 00:12:31 +01:00
04d98a61f6 Added alert dialog context trigger to quick alert bell. 2025-03-13 00:12:29 +01:00
45ba0b8ebc Added divider in alerts overview filters. 2025-03-09 06:50:16 +01:00
43efc440ba Added ID to alert overview search scope. 2025-03-09 06:45:51 +01:00
298c54cdbb 0.39.2 2025-03-09 06:44:51 +01:00
5c03b59082 Added basic alerts overview filters. 2025-03-09 06:44:48 +01:00
57d3615f71 0.39.1 2025-03-09 06:13:10 +01:00
2bae1de247 Improved visuals and tooltips for quick alerts. 2025-03-09 06:13:07 +01:00
a63009509a 0.39.0 2025-03-09 06:00:23 +01:00
c6c4dcad7c Added quick alert buttons to entity and tag headers. 2025-03-09 06:00:18 +01:00
61ed171c9d Added quick alert button to actor bio. 2025-03-09 04:12:02 +01:00
45a0631c9b 0.38.24 2025-03-06 01:44:14 +01:00
7ad424c389 Fixed and improved location resolve in actor editing. 2025-03-06 01:44:12 +01:00
8ada96c8bc Added date precision to release date edit. 2025-03-06 01:27:56 +01:00
65edafad54 Showing in album when teaser is shown as animated image. 2025-03-05 05:04:24 +01:00
20eb79bafa 0.38.23 2025-03-05 04:50:20 +01:00
d6f5e1912c Supporting animated WebP in scene media banner. 2025-03-05 04:50:16 +01:00
fd6dd9e9be 0.38.22 2025-02-26 06:22:46 +01:00
5f9359144e Improved entity tile text alignment. 2025-02-26 06:22:44 +01:00
1d60ea0b0b 0.38.21 2025-02-26 04:11:38 +01:00
bead604fd6 Fixed network icon overflowing on entity tile. 2025-02-26 04:11:36 +01:00
19b2a56925 Hiding parent network for subnetworks on release pages. 2025-02-25 02:49:24 +01:00
8da96090ee 0.38.20 2025-02-25 02:47:07 +01:00
072c8da922 Fixed child network logo on release pages. 2025-02-25 02:47:05 +01:00
4c7bf2231c 0.38.19 2025-02-23 17:16:59 +01:00
e248de53a2 Adjusted age at death styling in actor tile. 2025-02-23 17:16:54 +01:00
8fa5f5b405 0.38.18 2025-02-23 17:11:49 +01:00
49f1bc9ce2 Showing age at death instead of age from birth on actor tiles. 2025-02-23 17:11:47 +01:00
48331c1c5a 0.38.17 2025-02-18 00:00:18 +01:00
6265caeb62 Fixed edit page labels and dividers in dark theme. 2025-02-18 00:00:17 +01:00
c0dec0b5f5 Added vite as an override until vite-plugin-yaml gets updated. 2025-02-10 00:35:00 +01:00
9b0003b1ff 0.38.16 2025-02-10 00:25:18 +01:00
c2624f180d Removed stray console log. 2025-02-10 00:25:16 +01:00
7b96f8fc83 0.38.15 2025-02-09 23:58:31 +01:00
42167b062d Escaping question mark in manticore query to prevent conflict with manticore and knex syntax. 2025-02-09 23:58:29 +01:00
304a9c41bb 0.38.14 2024-12-29 23:56:43 +01:00
0565ad062b Added entity results to global search. 2024-12-29 23:56:41 +01:00
684c269f87 0.38.13 2024-12-29 02:17:15 +01:00
1cf789d8f7 Fixed age showing on actor tile when birth year is unknown. 2024-12-29 02:17:03 +01:00
db8aeb97a7 0.38.12 2024-12-29 00:43:01 +01:00
651f3bba84 Fixed scene page not showing current avatar. 2024-12-29 00:42:59 +01:00
1bb39573e0 0.38.11 2024-12-23 22:29:49 +01:00
341b9267e6 Embedding entity information in scene and movie watch buttons. 2024-12-23 22:29:47 +01:00
ed8c9a71fa 0.38.10 2024-12-19 22:33:07 +01:00
1cfc3baf95 Removed noreferrer from watch video link so third parties can track traffic. 2024-12-19 22:33:05 +01:00
750b55aa47 0.38.9 2024-12-16 02:36:03 +01:00
b225d5af99 Fixed year filter text color. 2024-12-16 02:36:01 +01:00
22b5561d69 0.38.8 2024-12-16 01:28:18 +01:00
428cdabac1 Allowing year 1 as unknown year of birth for compatability with date input limits. 2024-12-16 01:28:16 +01:00
19af103bc4 0.38.7 2024-11-24 06:11:45 +01:00
30408673f5 Bio allows for date of birth without (zero) year. Added entity ID to logo title. 2024-11-24 06:11:42 +01:00
293072c659 0.38.6 2024-11-09 00:20:42 +01:00
34c413da77 Added touchstart to menu for iOS, removed auto close on login link. 2024-11-09 00:20:39 +01:00
e767b80076 0.38.5 2024-11-05 04:07:45 +01:00
442a564fa1 Selecting deduped results on scene and movie updates pages. 2024-11-05 04:07:42 +01:00
3505282aa3 0.38.4 2024-11-05 02:57:43 +01:00
aa672fd641 Fixed MV link structure and bio socials sizing. 2024-11-05 02:57:41 +01:00
5d50f0b032 0.38.3 2024-11-04 03:04:53 +01:00
3c598bdb6f Fixed socials order. 2024-11-04 03:04:51 +01:00
b41ee2e5cf 0.38.2 2024-11-04 02:46:33 +01:00
cb759e7a09 Fixed socials order and prefix spacing. 2024-11-04 02:46:31 +01:00
4fba6ff71e 0.38.1 2024-11-04 02:41:36 +01:00
5f9893e0c6 Fixed edit social platform resetting to OF. 2024-11-04 02:41:34 +01:00
8f3701fd59 0.38.0 2024-11-04 02:36:32 +01:00
de60b67cb9 Added socials. 2024-11-04 02:36:30 +01:00
208f1dfde4 0.37.17 2024-11-03 06:30:28 +01:00
89c55cea93 Separated some actor edit types into dedicated components. 2024-11-03 06:30:09 +01:00
94ebc92789 0.37.16 2024-11-03 01:56:55 +01:00
8f5b7e9c45 Fixed bio and profile nav overflow, scene page channel name when no logo. 2024-11-03 01:56:51 +01:00
cd85e46594 0.37.15 2024-10-31 03:55:28 +01:00
2c49e0f9fe Resolving place in edit form. 2024-10-31 03:55:26 +01:00
f8aa903817 0.37.14 2024-10-30 02:40:21 +01:00
bcde0d8cd5 Improved chapter timeline rendering. 2024-10-30 02:40:19 +01:00
d655ed9faa 0.37.13 2024-10-30 02:25:22 +01:00
855a698eae Improved actor editing units. 2024-10-30 02:25:19 +01:00
2293e10af7 0.37.12 2024-10-27 22:51:06 +01:00
5ff522311e Fixed movie duration returned as string, therefore failing formatting. 2024-10-27 22:51:04 +01:00
fa560c179b 0.37.11 2024-10-26 01:03:32 +02:00
a6af10ee20 Using actors avatars table for secondary profile photos. 2024-10-26 01:03:30 +02:00
6b5aa9505e 0.37.10 2024-10-25 02:52:43 +02:00
d1caf5d7ac Additional movie page fix due missing chapters property. 2024-10-25 02:52:41 +02:00
57227c4bed 0.37.9 2024-10-25 02:43:05 +02:00
831f92683e Fixed movie page broken due missing chapters property. 2024-10-25 02:43:04 +02:00
3e6ad72416 0.37.8 2024-10-25 01:07:56 +02:00
a1069225c0 Fixed missing penis breaking edit page. 2024-10-25 01:07:54 +02:00
3e118f5faa 0.37.7 2024-10-25 00:08:13 +02:00
77ec461ff6 Improved bio styling and fixed penis sizes. 2024-10-25 00:08:11 +02:00
aed9b5bc21 0.37.6 2024-10-24 22:17:36 +02:00
23838889a3 No longer using meta table for avatar so edits are reflected immediately. 2024-10-24 22:17:33 +02:00
9e64dd788c 0.37.5 2024-10-23 22:23:50 +02:00
eb4234e978 Fixed false not accepted as actor edit value. 2024-10-23 22:14:12 +02:00
fabd57bbef Fixed actor revision links hiding on mobile. 2024-10-23 03:38:45 +02:00
f56ed29e21 0.37.4 2024-10-23 03:28:26 +02:00
d975d2b6c7 Fixed conversion comment showing up when removing value. Removed Dr. Dre from surgeon suggestions (lol). 2024-10-23 03:28:23 +02:00
2bdb3d24a0 Fixed post-edit user revisions link. 2024-10-23 03:10:20 +02:00
b82d5efb2a 0.37.3 2024-10-23 03:08:45 +02:00
760cef3551 Fixed actor edit page breaking if no avatar is available. 2024-10-23 03:08:44 +02:00
254050ec80 0.37.2 2024-10-23 02:58:08 +02:00
39a7cf2ec0 Added figure size conversions to actor revisions. 2024-10-23 02:58:05 +02:00
192aeeaafd 0.37.1 2024-10-23 01:28:59 +02:00
05bd7b703d Added actor revision overviews to actor and user pages. 2024-10-23 01:28:54 +02:00
d0cf9bf5d0 0.37.0 2024-10-22 03:12:44 +02:00
3967745fb3 Added actor profile revisions. 2024-10-22 03:12:42 +02:00
b5bef49f73 Removed stray console log. 2024-10-18 01:08:09 +02:00
feb77ab0c4 0.36.10 2024-10-15 02:58:48 +02:00
3aedfbf2d1 Showing chapter information. 2024-10-15 02:58:46 +02:00
63b1198fca 0.36.9 2024-10-12 23:02:15 +02:00
7bef4b232a Added scene title title fallback. 2024-10-12 23:02:07 +02:00
26772b0122 0.36.8 2024-10-12 22:40:13 +02:00
03d56ac06a Increased vertical gap between actors, tags and movies on scene edit page. 2024-10-12 22:40:11 +02:00
78108296de 0.36.7 2024-10-12 22:36:03 +02:00
fdce7b0232 Sorting scene edit page tags alphanumerically, preventing double tags. 2024-10-12 22:35:58 +02:00
211f600242 0.36.6 2024-10-12 22:13:48 +02:00
0c9e8a4a06 Fixed scene revision transaction error not caught. Deduping revision arrays. 2024-10-12 22:13:46 +02:00
06eb24d93a 0.36.5 2024-10-09 00:09:46 +02:00
19828d5ed1 Fixed edit search input not clearing, added more post-edit navigation links. 2024-10-09 00:09:45 +02:00
47d3e50a6d 0.36.4 2024-10-08 21:30:57 +02:00
067d414cd9 Added compact revision overview, formatting duration. Preventing rejected revisions from being reviewed again. 2024-10-08 21:30:55 +02:00
9e87c47b24 0.36.3 2024-10-06 23:54:56 +02:00
50b2fa02ca Fixed scene edit duration input not properly synchronizing with edit deltas. 2024-10-06 23:54:54 +02:00
bd2a1e4514 0.36.2 2024-10-06 23:42:45 +02:00
11fddcdac9 Fixed timestamp fields not defaulting to 0. 2024-10-06 23:42:43 +02:00
a9e610a82c 0.36.1 2024-10-06 02:57:34 +02:00
145ee198ff Allowing editors revision access. 2024-10-06 02:57:31 +02:00
99c10cf394 0.36.0 2024-10-06 02:46:15 +02:00
8f843f321d Expanded edit fields. Added revision history to scene and user pages. 2024-10-06 02:45:56 +02:00
8bf9e22b39 Added experimental edit page and revision history. 2024-09-10 02:47:03 +02:00
4b8dfba289 0.35.3 2024-09-05 16:58:39 +02:00
2c386e4880 Fixed shootId defined as Int rather than String 2024-09-05 16:58:36 +02:00
6751ff39a2 0.35.2 2024-09-05 01:54:54 +02:00
e12fc47f47 Removed padding from scope and pagination banners. 2024-09-05 01:54:52 +02:00
2287d7be4a 0.35.1 2024-09-05 01:49:20 +02:00
476f9bff2e Moved scene tile 'new'-star to meta component to consistent position between grid and list view. 2024-09-05 01:49:18 +02:00
415c6f3e18 0.35.0 2024-09-05 01:44:51 +02:00
77c6136a05 Added scene view toggle. 2024-09-05 01:44:48 +02:00
6458264abf Added dedicated compact scene tile. 2024-09-05 01:00:02 +02:00
08795b2ebe 0.34.3 2024-09-04 22:50:53 +02:00
3cf8fd1bb8 Fixed scene filter breaking without page entity. Improved scroll restore after applying filter. 2024-09-04 22:50:51 +02:00
858a1f7087 0.34.2 2024-09-04 02:05:58 +02:00
9bf906a096 Swapped details and description on scene page. 2024-09-04 02:05:56 +02:00
772fc813d6 0.34.1 2024-09-03 06:02:56 +02:00
437d0b5b57 Changed scene tile shoot ID color. Removed stray console logs. 2024-09-03 06:02:52 +02:00
ced3ecb08a 0.34.0 2024-09-03 05:56:16 +02:00
60c7e2a876 Added studios to filters and scene page. 2024-09-03 05:56:14 +02:00
c3362e614e 0.33.2 2024-09-02 01:22:11 +02:00
f0cf35f634 Fixed 'Add to favorites' in alert dialog, improved alerts list layout. 2024-09-02 01:22:09 +02:00
8bbb2107f5 0.33.1 2024-09-02 00:18:42 +02:00
cc7ca7544b Improved profile margins and summary editor. 2024-09-02 00:18:39 +02:00
235d097f08 0.33.0 2024-09-01 02:54:05 +02:00
05fff7608e Moved summary template editor to profile page. 2024-09-01 02:54:03 +02:00
2cf7f2a692 Split profile sections into different pages. 2024-09-01 01:30:45 +02:00
8fba01dbdd Improved alert list mobile layout. 2024-09-01 00:43:21 +02:00
4f4ff0b4bc Fixed notifications overflow on small screens. 2024-09-01 00:08:05 +02:00
1407d656dd 0.32.4 2024-08-31 22:15:41 +02:00
6e130dd96e Improved key success message layout for mobile, using global 'now' to prevent hydration mismatches. 2024-08-31 22:15:39 +02:00
2882bd59e9 0.32.3 2024-08-31 05:28:41 +02:00
29a474f471 Added user agent to access log. 2024-08-31 05:28:38 +02:00
59ebb94af8 0.32.2 2024-08-31 05:13:07 +02:00
ec4f9768a8 Removed creation date from API key removal confirmation dialog. 2024-08-31 05:13:00 +02:00
a0d38a179a Capitalized HTTP headers on keys page. 2024-08-31 05:08:50 +02:00
19dfc506e3 0.32.1 2024-08-31 05:04:16 +02:00
7c59479f29 Centered key management on page. 2024-08-31 05:04:14 +02:00
a16d17399b 0.32.0 2024-08-31 04:59:08 +02:00
e8864ce35b Added API key authentication. 2024-08-31 04:59:05 +02:00
da893c1a76 0.31.1 2024-08-30 02:45:12 +02:00
128f930c7e Added description and duration to graphql Release type. 2024-08-30 02:45:10 +02:00
3d4d7cc307 0.31.0 2024-08-30 02:28:46 +02:00
edb10c6d1a Expanded GraphQL API with scenes entities and actors. 2024-08-30 02:28:44 +02:00
706ccf1ab3 0.30.8 2024-08-29 17:53:56 +02:00
0909e8d74c Fixed add stash in notifications panel. Fixed iframe ads breaking page width. Improved actor and movie tile size in search results. 2024-08-29 17:53:54 +02:00
33e8fbea0b 0.30.7 2024-08-29 02:20:00 +02:00
aa3eb3df33 Supporting value wrap in summary template. 2024-08-29 02:19:57 +02:00
e3ed54fe3c 0.30.6 2024-08-29 01:57:24 +02:00
52d9558698 Fixed page breaking when logged out due missing primary stash reference. 2024-08-29 01:57:22 +02:00
c5c51a9560 Improved gitignore media exclusion. 2024-08-29 01:55:31 +02:00
9e9fd087ba Added media components directory to repo. 2024-08-29 01:54:39 +02:00
6a0d5b8030 0.30.5 2024-08-29 01:53:28 +02:00
ea3ef0fc1a Merged scene and movie page banner into single component. Improved movie cover display. 2024-08-29 01:53:26 +02:00
1a389ef843 0.30.4 2024-08-28 02:06:03 +02:00
24f5597521 Fixed summary copy button value. Expanded experimental GraphQL API. 2024-08-28 02:06:01 +02:00
e727745e7d 0.30.3 2024-08-27 21:19:21 +02:00
5321b08f76 Improved mobile scene album layout. Removed info channels from network children. 2024-08-27 21:19:20 +02:00
ba47d25ab5 0.30.2 2024-08-26 17:05:24 +02:00
9e8a20494b Fixed actor and movie tiles stash breaking page. 2024-08-26 17:05:21 +02:00
3fdeddb272 0.30.1 2024-08-26 06:17:25 +02:00
b2ea29d72c Fixed pages breaking when logged out. 2024-08-26 06:17:23 +02:00
3bf9a6f09f 0.30.0 2024-08-26 06:15:25 +02:00
80d8a8109a Added elaborate template switching. 2024-08-26 06:15:22 +02:00
fa991c0294 0.29.0 2024-08-25 02:51:19 +02:00
e2cffbdde2 Added summary template editor, improved summary template format and options. 2024-08-25 02:51:16 +02:00
d2c9b447ee 0.28.4 2024-08-23 17:46:56 +02:00
978dcbaa25 Fixed date fallback in scene tile. 2024-08-23 17:46:53 +02:00
c134416b46 0.28.3 2024-08-23 02:18:01 +02:00
8e473d5e3e Fixed movie scene photo query, added caps. 2024-08-23 02:17:59 +02:00
b3a3562bb8 0.28.2 2024-08-23 01:35:41 +02:00
0d383c35b5 Fixed heart menu stash creation. 2024-08-23 01:35:39 +02:00
b683653af4 0.28.1 2024-08-22 06:28:49 +02:00
222456dd55 Fixed channel expand tab overlapping header. 2024-08-22 06:28:46 +02:00
a4d8d5859f 0.28.0 2024-08-22 06:23:44 +02:00
c38206bbbf Added movies page to actors, entities and tags. 2024-08-22 06:23:41 +02:00
a903c7f31f 0.27.7 2024-08-22 02:16:17 +02:00
8226742aab Added BTS and VR to tag filter settings. 2024-08-22 02:16:02 +02:00
333df9c141 0.27.6 2024-08-20 02:51:48 +02:00
be957a9212 Added robots.txt preventing image crawling. 2024-08-20 02:51:46 +02:00
b09b18ecd8 0.27.5 2024-08-20 02:43:25 +02:00
80d6216790 Fixed date failing for minute and hour date precision. 2024-08-20 02:43:23 +02:00
0ca847a878 0.27.4 2024-08-20 02:31:54 +02:00
527484b617 Fixed actors overview page grow. 2024-08-20 02:31:51 +02:00
24a4bd5204 0.27.3 2024-08-19 23:31:30 +02:00
1ea8e52ab1 Accounting for date precision in scene and movie pages and tiles. 2024-08-19 23:31:28 +02:00
0cc9ee51e2 0.27.2 2024-08-19 22:07:03 +02:00
becd196b01 Improved movie page styling. Fixed parentless channel icon in channel filter. 2024-08-19 22:07:01 +02:00
0ad62a7305 Fixed year query inside stash. 2024-08-18 23:34:38 +02:00
fb574e62d4 0.27.1 2024-08-18 02:07:48 +02:00
b3b529faac Fixed dark styling on years filter. 2024-08-18 02:07:46 +02:00
6e56f8c9fe 0.27.0 2024-08-18 01:39:50 +02:00
b9b4a8e773 Added years filter. Changed default DMCA to generic content e-mail. 2024-08-18 01:36:37 +02:00
236 changed files with 19258 additions and 4036 deletions

View File

@@ -21,7 +21,7 @@
"no-console": 0,
"no-param-reassign": ["error", {
"props": true,
"ignorePropertyModificationsFor": ["state", "acc"]
"ignorePropertyModificationsFor": ["state", "acc", "req"]
}],
"vue/multi-word-component-names": 0,
"vue/no-reserved-component-names": 0,
@@ -32,7 +32,8 @@
"vue/html-indent": ["error", "tab"],
"vue/multiline-html-element-content-newline": 0,
"vue/no-v-html": 0,
"vue/singleline-html-element-content-newline": 0
"vue/singleline-html-element-content-newline": 0,
"vue/comment-directive": 0,
},
"settings": {
"import/resolver": {

5
.gitignore vendored
View File

@@ -3,4 +3,7 @@ dist/
config/*
!config/default.*js
log/
media/
/media
data/
assets/*.mmdb
assets/.geoipupdate.lock

3
.gitmodules vendored
View File

@@ -2,3 +2,6 @@
path = static
ignore = all
url = git@unknown.name:DebaucheryLibrarian/traxxx-assets.git
[submodule "common"]
path = common
url = git@unknown.name:DebaucheryLibrarian/traxxx-common.git

View File

@@ -57,3 +57,11 @@
text-overflow: ellipsis;
overflow: hidden;
}
.noshrink {
flex-shrink: 0;
}
.capitalize {
text-transform: capitalize;
}

View File

@@ -29,5 +29,37 @@ body {
.heading {
margin: 0 0 1rem 0;
color: var(--primary-light-20);
color: var(--primary);
}
.icon.icon-social.icon-twitter {
fill: #008ad8;
}
.icon.icon-social.icon-onlyfans {
fill: #00adef;
}
.icon.icon-social.icon-fansly {
fill: #2699f6;
}
.icon.icon-social.icon-linktree {
fill: #43e660;
}
.icon.icon-social.icon-pornhub {
fill: #ff9000;
}
.icon.icon-social.icon-cashapp {
fill: #00c853;
}
.icon.icon-social.icon-loyalfans {
fill: #d90a16;
}
.icon.icon-social {
fill: var(--highlight-strong-10);
}

View File

@@ -29,6 +29,7 @@
--background-level-20: #eee;
--background-level-30: #eee;
--background-dim: var(--shadow-weak-10);
--background-error: rgba(255, 0, 0, .1);
--shadow-weak-50: rgba(0, 0, 0, .02);
--shadow-weak-40: rgba(0, 0, 0, .05);
@@ -62,7 +63,8 @@
--text: #222;
--text-light: #fff;
--link: #48f;
/* --link: #48f; */
--link: var(--primary);
--male: #0af;
--female: #f0a;
@@ -79,6 +81,9 @@
--success: #5c2;
--notice: #25c;
--approve: #3a1;
--reject: #a22;
--gold: #d5b522;
}

View File

@@ -5,6 +5,11 @@
top: 0;
left: 0;
outline: none;
max-width: 100%;
}
.v-popper__wrapper {
margin: 0 .25rem .25rem .25rem; /* arrow provides top clearance */
}
.v-popper__popper.v-popper__popper--hidden {

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15.25 3h-9c-0.412 0-0.989 0.239-1.28 0.53l-4.439 4.439c-0.292 0.292-0.292 0.769 0 1.061l4.439 4.439c0.292 0.292 0.868 0.53 1.28 0.53h9c0.412 0 0.75-0.338 0.75-0.75v-9.5c0-0.413-0.338-0.75-0.75-0.75zM14 10.5l-1.5 1.5-2-2-2 2-1.5-1.5 2-2-2-2 1.5-1.5 2 2 2-2 1.5 1.5-2 2 2 2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 430 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M8 3.188c0.561 0 1.084 0.165 1.523 0.449l-3.887 3.887c-0.284-0.439-0.449-0.962-0.449-1.523 0-1.551 1.262-2.813 2.813-2.813zM6.477 8.363l3.887-3.887c0.284 0.439 0.449 0.962 0.449 1.523 0 1.551-1.262 2.812-2.813 2.812-0.561 0-1.084-0.165-1.523-0.449zM14.5 0h-13c-0.825 0-1.5 0.675-1.5 1.5v9c0 0.825 0.675 1.5 1.5 1.5h2.5v4l4.8-4h5.7c0.825 0 1.5-0.675 1.5-1.5v-9c0-0.825-0.675-1.5-1.5-1.5zM8 10c-2.209 0-4-1.791-4-4s1.791-4 4-4 4 1.791 4 4-1.791 4-4 4z"></path>
</svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.5 0h-13c-0.825 0-1.5 0.675-1.5 1.5v9c0 0.825 0.675 1.5 1.5 1.5h2.5v4l4.8-4h5.7c0.825 0 1.5-0.675 1.5-1.5v-9c0-0.825-0.675-1.5-1.5-1.5zM11 4.282l-1.718 1.718 1.718 1.718v1.282h-1.282l-1.718-1.718-1.718 1.718h-1.282v-1.282l1.718-1.718-1.718-1.718v-1.282h1.282l1.718 1.718 1.718-1.718h1.282v1.282z"></path>
</svg>

After

Width:  |  Height:  |  Size: 455 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.5 0h-13c-0.825 0-1.5 0.675-1.5 1.5v9c0 0.825 0.675 1.5 1.5 1.5h2.5v4l4.8-4h5.7c0.825 0 1.5-0.675 1.5-1.5v-9c0-0.825-0.675-1.5-1.5-1.5zM7 9.414l-3.207-3.707 0.914-0.914 2.293 1.793 4.293-3.793 0.914 0.914-5.207 5.707z"></path>
</svg>

After

Width:  |  Height:  |  Size: 377 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.5 1h-13c-0.825 0-1.5 0.675-1.5 1.5v8c0 0.825 0.675 1.5 1.5 1.5h2.5v4l4.8-4h5.7c0.825 0 1.5-0.675 1.5-1.5v-8c0-0.825-0.675-1.5-1.5-1.5zM7 9h-4v-1h4v1zM11 7h-8v-1h8v1zM13 5h-10v-1h10v1z"></path>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.5 0h-13c-0.825 0-1.5 0.675-1.5 1.5v9c0 0.825 0.675 1.5 1.5 1.5h2.5v4l4.8-4h5.7c0.825 0 1.5-0.675 1.5-1.5v-9c0-0.825-0.675-1.5-1.5-1.5zM7 9h-3c-1.1 0-2-0.9-2-2v-1c0-1.1 0.9-2 2-2h3v1h-3c-0.265 0-0.515 0.105-0.705 0.295s-0.295 0.441-0.295 0.705v1c0 0.265 0.105 0.515 0.295 0.705s0.441 0.295 0.705 0.295h3v1zM6 7v-1h4v1h-4zM14 7c0 1.1-0.9 2-2 2h-3v-1h3c0.265 0 0.515-0.105 0.705-0.295s0.295-0.441 0.295-0.705v-1c0-0.265-0.105-0.515-0.295-0.705s-0.44-0.295-0.705-0.295h-3v-1h3c1.1 0 2 0.9 2 2v1z"></path>
</svg>

After

Width:  |  Height:  |  Size: 652 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.5 0h-13c-0.825 0-1.5 0.675-1.5 1.5v9c0 0.825 0.675 1.5 1.5 1.5h2.5v4l4.8-4h5.7c0.825 0 1.5-0.675 1.5-1.5v-9c0-0.825-0.675-1.5-1.5-1.5zM9 10h-2v-2h2v2zM9 6h-2v-4h2v4z"></path>
</svg>

After

Width:  |  Height:  |  Size: 326 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.5 0h-13c-0.825 0-1.5 0.675-1.5 1.5v9c0 0.825 0.675 1.5 1.5 1.5h2.5v4l4.8-4h5.7c0.825 0 1.5-0.675 1.5-1.5v-9c0-0.825-0.675-1.5-1.5-1.5zM12 7h-3v3h-2v-3h-3v-2h3v-3h2v3h3v2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 331 B

37
assets/img/icons/cashapp.svg Executable file
View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
class="app-icon"
viewBox="0 0 64 64"
version="1.1"
id="svg2"
sodipodi:docname="cashapp.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="4.2922526"
inkscape:cx="-6.4068923"
inkscape:cy="41.004111"
inkscape:window-width="1920"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<path
id="path1"
style="fill-rule:nonzero"
d="M 22.300781 0 C 15.900781 0 12.699219 -0.000390625 9.1992188 1.0996094 A 13.6 13.6 0 0 0 1.0996094 9.1992188 C -0.000390625 12.659219 0 15.880781 0 22.300781 L 0 41.689453 C 0 48.099453 -0.000390625 51.300781 1.0996094 54.800781 A 13.6 13.6 0 0 0 9.1992188 62.900391 C 12.659219 64.000391 15.880781 64 22.300781 64 L 41.699219 64 C 48.099219 64 51.300781 64.000859 54.800781 62.880859 A 13.6 13.6 0 0 0 62.900391 54.779297 C 64.000391 51.319297 64 48.099688 64 41.679688 L 64 22.310547 C 64 15.900547 64.000391 12.699219 62.900391 9.1992188 A 13.6 13.6 0 0 0 54.800781 1.0996094 C 51.300781 -0.000390625 48.099219 0 41.699219 0 L 22.300781 0 z M 34.660156 10.009766 L 39.5 10.009766 C 40.33 10.009766 40.949297 10.789141 40.779297 11.619141 L 39.990234 15.419922 A 19.73 19.73 0 0 1 46.710938 19.259766 C 47.270938 19.799766 47.299531 20.699219 46.769531 21.199219 L 44.269531 23.800781 C 43.799531 24.300781 42.970703 24.300781 42.470703 23.800781 L 42.490234 23.820312 C 40.380234 21.920312 37.149062 20.529297 33.789062 20.529297 C 31.149062 20.529297 28.519531 21.490156 28.519531 23.910156 C 28.519531 26.400156 31.339609 27.229453 34.599609 28.439453 C 40.299609 30.409453 45 32.830312 45 38.570312 C 45 44.800312 40.27925 49.069844 32.53125 49.589844 L 31.832031 52.980469 A 1.32 1.32 0 0 1 30.53125 54.039062 L 25.681641 54 C 24.851641 53.99 24.242109 53.220625 24.412109 52.390625 L 25.152344 48.820312 C 22.120344 47.990313 19.459375 46.489922 17.359375 44.419922 A 1.36 1.36 0 0 1 17.359375 42.5 L 20.060547 39.800781 A 1.3 1.3 0 0 1 21.900391 39.800781 C 24.500391 42.410781 27.860547 43.480469 31.060547 43.480469 C 34.580547 43.480469 36.960938 42.019297 36.960938 39.529297 C 36.960937 37.109297 34.570547 36.47 30.060547 34.75 C 25.290547 33.04 20.779297 30.55 20.779297 24.75 C 20.779297 18.05 26.239687 14.779219 32.679688 14.449219 L 33.380859 11.070312 A 1.32 1.32 0 0 1 34.660156 10.009766 z " />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M8 0c-4.418 0-8 3.582-8 8s3.582 8 8 8 8-3.582 8-8-3.582-8-8-8zM6.707 10.293c0.391 0.391 0.391 1.024 0 1.414-0.195 0.195-0.451 0.293-0.707 0.293s-0.512-0.098-0.707-0.293l-3-3c-0.391-0.391-0.391-1.024 0-1.414l3-3c0.391-0.391 1.024-0.391 1.414 0s0.391 1.024 0 1.414l-2.293 2.293 2.293 2.293zM10.707 11.707c-0.391 0.391-1.024 0.391-1.414 0s-0.391-1.024 0-1.414l2.293-2.293-2.293-2.293c-0.391-0.391-0.391-1.024 0-1.414 0.195-0.195 0.451-0.293 0.707-0.293s0.512 0.098 0.707 0.293l3 3c0.391 0.391 0.391 1.024 0 1.414l-3 3z"></path>
</svg>

After

Width:  |  Height:  |  Size: 672 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M8 0c-4.418 0-8 3.582-8 8s3.582 8 8 8 8-3.582 8-8-3.582-8-8-8zM7 4.111c-0.552 0-1 0.497-1 1.111v1.111c0 0.92-0.672 1.667-1.5 1.667 0.828 0 1.5 0.746 1.5 1.667v1.111c0 0.614 0.448 1.111 1 1.111v1.111h-1c-1.103 0-2-0.997-2-2.222v-1.111c0-0.614-0.448-1.111-1-1.111v-1.111c0.552 0 1-0.497 1-1.111v-1.111c0-1.225 0.897-2.222 2-2.222h1v1.111zM13 8.556c-0.552 0-1 0.498-1 1.111v1.111c0 1.225-0.897 2.222-2 2.222h-1v-1.111c0.552 0 1-0.497 1-1.111v-1.111c0-0.92 0.672-1.667 1.5-1.667-0.828 0-1.5-0.746-1.5-1.667v-1.111c0-0.614-0.448-1.111-1-1.111v-1.111h1c1.103 0 2 0.997 2 2.222v1.111c0 0.614 0.448 1.111 1 1.111v1.111z"></path>
</svg>

After

Width:  |  Height:  |  Size: 768 B

View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M16 8.414l-1.414-1.414-2.086 2.086-2.086-2.086-1.414 1.414 2.086 2.086-2.086 2.086 1.414 1.414 2.086-2.086 2.086 2.086 1.414-1.414-2.086-2.086z"></path>
<path d="M8 13.421c-0.752 0.173-1.611 0.266-2.5 0.266-1.242 0-2.429-0.181-3.342-0.51-0.763-0.275-1.074-0.562-1.158-0.677v-2.594c0.995 0.643 2.64 1.062 4.5 1.062 0.9 0 1.75-0.098 2.5-0.273v-1.306c-0.752 0.173-1.611 0.266-2.5 0.266-1.242 0-2.429-0.181-3.342-0.51-0.762-0.275-1.074-0.562-1.158-0.677v-2.531c0.995 0.643 2.64 1.062 4.5 1.062 3.038 0 5.5-1.119 5.5-2.5s-2.462-2.5-5.5-2.5-5.5 1.119-5.5 2.5v8c0 1.381 2.462 2.5 5.5 2.5 0.9 0 1.75-0.098 2.5-0.273v-1.306zM2.402 3.823c0.846-0.329 1.946-0.51 3.098-0.51s2.252 0.181 3.098 0.51c0.707 0.275 0.995 0.562 1.074 0.677-0.078 0.115-0.367 0.402-1.074 0.677-0.846 0.329-1.946 0.51-3.098 0.51s-2.252-0.181-3.098-0.51c-0.707-0.275-0.996-0.562-1.074-0.677 0.078-0.115 0.367-0.402 1.074-0.677z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 725.59998 198.3"
version="1.1"
id="svg6"
width="725.59998"
height="198.3">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<path
d="m 105.9,83.2 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0,-6.1 -4.6,-11 -10.2,-11 z m -36.5,0 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0.1,-6.1 -4.5,-11 -10.2,-11 z"
id="path2" />
<path
d="M 154.5,0 H 20.5 C 9.2,0 0,9.2 0,20.5 v 134 C 0,165.8 9.2,175 20.5,175 h 113.4 l -5.3,-18.3 12.8,11.8 12.1,11.1 21.6,18.7 V 20.5 C 175,9.2 165.8,0 154.5,0 Z m -38.6,129.5 c 0,0 -3.6,-4.3 -6.6,-8 13.1,-3.7 18.1,-11.8 18.1,-11.8 -4.1,2.7 -8,4.6 -11.5,5.9 -5,2.1 -9.8,3.4 -14.5,4.3 -9.6,1.8 -18.4,1.3 -25.9,-0.1 -5.7,-1.1 -10.6,-2.6 -14.7,-4.3 -2.3,-0.9 -4.8,-2 -7.3,-3.4 -0.3,-0.2 -0.6,-0.3 -0.9,-0.5 -0.2,-0.1 -0.3,-0.2 -0.4,-0.2 -1.8,-1 -2.8,-1.7 -2.8,-1.7 0,0 4.8,7.9 17.5,11.7 -3,3.8 -6.7,8.2 -6.7,8.2 C 38.1,128.9 29.7,114.5 29.7,114.5 29.7,82.6 44.1,56.7 44.1,56.7 58.5,46 72.1,46.3 72.1,46.3 l 1,1.2 c -18,5.1 -26.2,13 -26.2,13 0,0 2.2,-1.2 5.9,-2.8 10.7,-4.7 19.2,-5.9 22.7,-6.3 0.6,-0.1 1.1,-0.2 1.7,-0.2 6.1,-0.8 13,-1 20.2,-0.2 9.5,1.1 19.7,3.9 30.1,9.5 0,0 -7.9,-7.5 -24.9,-12.6 l 1.4,-1.6 c 0,0 13.7,-0.3 28,10.4 0,0 14.4,25.9 14.4,57.8 0,-0.1 -8.4,14.3 -30.5,15 z m 151,-86.7 H 233.7 V 80.1 L 255.8,100 V 63.8 h 11.8 c 7.5,0 11.2,3.6 11.2,9.4 v 27.7 c 0,5.8 -3.5,9.7 -11.2,9.7 h -34 v 21.1 h 33.2 c 17.8,0.1 34.5,-8.8 34.5,-29.2 V 72.7 C 301.4,51.9 284.7,42.8 266.9,42.8 Z m 174,59.7 V 71.9 c 0,-11 19.8,-13.5 25.8,-2.5 L 485,62 C 477.8,46.2 464.7,41.6 453.8,41.6 436,41.6 418.4,51.9 418.4,71.9 v 30.6 c 0,20.2 17.6,30.3 35,30.3 11.2,0 24.6,-5.5 32,-19.9 l -19.6,-9 c -4.8,12.3 -24.9,9.3 -24.9,-1.4 z M 380.4,76.1 c -6.9,-1.5 -11.5,-4 -11.8,-8.3 0.4,-10.3 16.3,-10.7 25.6,-0.8 l 14.7,-11.3 c -9.2,-11.2 -19.6,-14.2 -30.3,-14.2 -16.3,0 -32.1,9.2 -32.1,26.6 0,16.9 13,26 27.3,28.2 7.3,1 15.4,3.9 15.2,8.9 -0.6,9.5 -20.2,9 -29.1,-1.8 l -14.2,13.3 c 8.3,10.7 19.6,16.1 30.2,16.1 16.3,0 34.4,-9.4 35.1,-26.6 C 412,84.5 396.2,79 380.4,76.1 Z m -67,55.5 h 22.4 V 42.8 H 313.4 Z M 691.1,42.8 H 657.9 V 80.1 L 680,100 V 63.8 h 11.8 c 7.5,0 11.2,3.6 11.2,9.4 v 27.7 c 0,5.8 -3.5,9.7 -11.2,9.7 h -34 v 21.1 h 33.3 c 17.8,0.1 34.5,-8.8 34.5,-29.2 V 72.7 c 0,-20.8 -16.7,-29.9 -34.5,-29.9 z M 528.2,41.6 c -18.4,0 -36.7,10 -36.7,30.5 v 30.3 c 0,20.3 18.4,30.5 36.9,30.5 18.4,0 36.7,-10.2 36.7,-30.5 V 72.1 c 0,-20.4 -18.5,-30.5 -36.9,-30.5 z m 14.4,60.8 c 0,6.4 -7.2,9.7 -14.3,9.7 -7.2,0 -14.4,-3.1 -14.4,-9.7 V 72.1 c 0,-6.5 7,-10 14,-10 7.3,0 14.7,3.1 14.7,10 z M 645.5,72.1 C 645,51.3 630.8,42.9 612.5,42.9 H 577 v 88.8 h 22.7 v -28.2 h 4 l 20.6,28.2 h 28 l -24.2,-30.5 c 10.7,-3.4 17.4,-12.7 17.4,-29.1 z m -32.6,12 H 599.7 V 63.8 h 13.2 c 14.1,0 14.1,20.3 0,20.3 z"
id="path4" />
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

21
assets/img/icons/discord.svg Executable file → Normal file
View File

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 725.59998 198.3"
viewBox="0 0 200 200"
version="1.1"
id="svg6"
width="725.59998"
height="198.3">
width="200"
height="200"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata12">
<rdf:RDF>
@@ -18,16 +18,15 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<path
d="m 105.9,83.2 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0,-6.1 -4.6,-11 -10.2,-11 z m -36.5,0 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0.1,-6.1 -4.5,-11 -10.2,-11 z"
d="m 118.35,84.05 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0,-6.1 -4.6,-11 -10.2,-11 z m -36.5,0 c -5.7,0 -10.2,4.9 -10.2,11 0,6.1 4.6,11 10.2,11 5.7,0 10.2,-4.9 10.2,-11 0.1,-6.1 -4.5,-11 -10.2,-11 z"
id="path2" />
<path
d="M 154.5,0 H 20.5 C 9.2,0 0,9.2 0,20.5 v 134 C 0,165.8 9.2,175 20.5,175 h 113.4 l -5.3,-18.3 12.8,11.8 12.1,11.1 21.6,18.7 V 20.5 C 175,9.2 165.8,0 154.5,0 Z m -38.6,129.5 c 0,0 -3.6,-4.3 -6.6,-8 13.1,-3.7 18.1,-11.8 18.1,-11.8 -4.1,2.7 -8,4.6 -11.5,5.9 -5,2.1 -9.8,3.4 -14.5,4.3 -9.6,1.8 -18.4,1.3 -25.9,-0.1 -5.7,-1.1 -10.6,-2.6 -14.7,-4.3 -2.3,-0.9 -4.8,-2 -7.3,-3.4 -0.3,-0.2 -0.6,-0.3 -0.9,-0.5 -0.2,-0.1 -0.3,-0.2 -0.4,-0.2 -1.8,-1 -2.8,-1.7 -2.8,-1.7 0,0 4.8,7.9 17.5,11.7 -3,3.8 -6.7,8.2 -6.7,8.2 C 38.1,128.9 29.7,114.5 29.7,114.5 29.7,82.6 44.1,56.7 44.1,56.7 58.5,46 72.1,46.3 72.1,46.3 l 1,1.2 c -18,5.1 -26.2,13 -26.2,13 0,0 2.2,-1.2 5.9,-2.8 10.7,-4.7 19.2,-5.9 22.7,-6.3 0.6,-0.1 1.1,-0.2 1.7,-0.2 6.1,-0.8 13,-1 20.2,-0.2 9.5,1.1 19.7,3.9 30.1,9.5 0,0 -7.9,-7.5 -24.9,-12.6 l 1.4,-1.6 c 0,0 13.7,-0.3 28,10.4 0,0 14.4,25.9 14.4,57.8 0,-0.1 -8.4,14.3 -30.5,15 z m 151,-86.7 H 233.7 V 80.1 L 255.8,100 V 63.8 h 11.8 c 7.5,0 11.2,3.6 11.2,9.4 v 27.7 c 0,5.8 -3.5,9.7 -11.2,9.7 h -34 v 21.1 h 33.2 c 17.8,0.1 34.5,-8.8 34.5,-29.2 V 72.7 C 301.4,51.9 284.7,42.8 266.9,42.8 Z m 174,59.7 V 71.9 c 0,-11 19.8,-13.5 25.8,-2.5 L 485,62 C 477.8,46.2 464.7,41.6 453.8,41.6 436,41.6 418.4,51.9 418.4,71.9 v 30.6 c 0,20.2 17.6,30.3 35,30.3 11.2,0 24.6,-5.5 32,-19.9 l -19.6,-9 c -4.8,12.3 -24.9,9.3 -24.9,-1.4 z M 380.4,76.1 c -6.9,-1.5 -11.5,-4 -11.8,-8.3 0.4,-10.3 16.3,-10.7 25.6,-0.8 l 14.7,-11.3 c -9.2,-11.2 -19.6,-14.2 -30.3,-14.2 -16.3,0 -32.1,9.2 -32.1,26.6 0,16.9 13,26 27.3,28.2 7.3,1 15.4,3.9 15.2,8.9 -0.6,9.5 -20.2,9 -29.1,-1.8 l -14.2,13.3 c 8.3,10.7 19.6,16.1 30.2,16.1 16.3,0 34.4,-9.4 35.1,-26.6 C 412,84.5 396.2,79 380.4,76.1 Z m -67,55.5 h 22.4 V 42.8 H 313.4 Z M 691.1,42.8 H 657.9 V 80.1 L 680,100 V 63.8 h 11.8 c 7.5,0 11.2,3.6 11.2,9.4 v 27.7 c 0,5.8 -3.5,9.7 -11.2,9.7 h -34 v 21.1 h 33.3 c 17.8,0.1 34.5,-8.8 34.5,-29.2 V 72.7 c 0,-20.8 -16.7,-29.9 -34.5,-29.9 z M 528.2,41.6 c -18.4,0 -36.7,10 -36.7,30.5 v 30.3 c 0,20.3 18.4,30.5 36.9,30.5 18.4,0 36.7,-10.2 36.7,-30.5 V 72.1 c 0,-20.4 -18.5,-30.5 -36.9,-30.5 z m 14.4,60.8 c 0,6.4 -7.2,9.7 -14.3,9.7 -7.2,0 -14.4,-3.1 -14.4,-9.7 V 72.1 c 0,-6.5 7,-10 14,-10 7.3,0 14.7,3.1 14.7,10 z M 645.5,72.1 C 645,51.3 630.8,42.9 612.5,42.9 H 577 v 88.8 h 22.7 v -28.2 h 4 l 20.6,28.2 h 28 l -24.2,-30.5 c 10.7,-3.4 17.4,-12.7 17.4,-29.1 z m -32.6,12 H 599.7 V 63.8 h 13.2 c 14.1,0 14.1,20.3 0,20.3 z"
d="m 166.95,0.85 h -134 c -11.3,0 -20.5,9.2 -20.5,20.5 v 134 c 0,11.3 9.2,20.5 20.5,20.5 h 113.4 l -5.3,-18.3 12.8,11.8 12.1,11.1 21.6,18.7 V 21.35 c -0.1,-11.3 -9.3,-20.5 -20.6,-20.5 z m -38.6,129.5 c 0,0 -3.6,-4.3 -6.6,-8 13.1,-3.7 18.1,-11.8 18.1,-11.8 -4.1,2.7 -8,4.6 -11.5,5.9 -5,2.1 -9.8,3.4 -14.5,4.3 -9.6,1.8 -18.4,1.3 -25.9,-0.1 -5.7,-1.1 -10.6,-2.6 -14.7,-4.3 -2.3,-0.9 -4.8,-2 -7.3,-3.4 -0.3,-0.2 -0.6,-0.3 -0.9,-0.5 -0.2,-0.1 -0.3,-0.2 -0.4,-0.2 -1.8,-1 -2.8,-1.7 -2.8,-1.7 0,0 4.8,7.9 17.5,11.7 -3,3.8 -6.7,8.2 -6.7,8.2 -22.1,-0.7 -30.5,-15.1 -30.5,-15.1 0,-31.9 14.4,-57.8 14.4,-57.8 14.4,-10.7 28,-10.4 28,-10.4 l 1,1.2 c -18,5.1 -26.2,13 -26.2,13 0,0 2.2,-1.2 5.9,-2.8 10.7,-4.7 19.2,-5.9 22.7,-6.3 0.6,-0.1 1.1,-0.2 1.7,-0.2 6.1,-0.8 13,-1 20.2,-0.2 9.5,1.1 19.7,3.9 30.1,9.5 0,0 -7.9,-7.5 -24.9,-12.6 l 1.4,-1.6 c 0,0 13.7,-0.3 28,10.4 0,0 14.4,25.9 14.4,57.8 0,-0.1 -8.4,14.3 -30.5,15 z"
id="path4" />
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

5
assets/img/icons/embed.svg Executable file
View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M9 11.5l1.5 1.5 5-5-5-5-1.5 1.5 3.5 3.5z"></path>
<path d="M7 4.5l-1.5-1.5-5 5 5 5 1.5-1.5-3.5-3.5z"></path>
</svg>

After

Width:  |  Height:  |  Size: 256 B

6
assets/img/icons/embed2.svg Executable file
View File

@@ -0,0 +1,6 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 20 16">
<path d="M13 11.5l1.5 1.5 5-5-5-5-1.5 1.5 3.5 3.5z"></path>
<path d="M7 4.5l-1.5-1.5-5 5 5 5 1.5-1.5-3.5-3.5z"></path>
<path d="M10.958 2.352l1.085 0.296-3 11-1.085-0.296 3-11z"></path>
</svg>

After

Width:  |  Height:  |  Size: 324 B

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="250mm"
height="250mm"
viewBox="0 0 250 250"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><g
id="layer1"
transform="translate(24.386144,-19.654193)"><path
d="m 97.694468,246.49306 c -3.29163,-0.77463 -0.47054,1.91978 -54.511619,-52.06397 -50.6807564,-50.62701 -53.322727,-53.34287 -56.39996,-57.97743 -27.178147,-40.932394 2.576397,-95.055377 51.592389,-93.845613 18.803333,0.464085 29.67869,5.96299 47.82344,24.180963 l 8.13594,8.168766 v 1.17671 1.17671 l -9.5952,9.5952 -9.59519,9.59519 h -1.17012 -1.17011 l -8.747713,-8.68541 C 53.42233,77.255906 51.16241,75.641266 44.558703,73.883746 22.440946,67.99732 2.0677986,88.370466 7.9542296,110.48822 c 1.854298,6.96737 3.0461774,8.54843 17.0298114,22.59053 L 36.775364,144.91938 80.437818,101.307 C 104.45218,77.320186 125.11232,56.891924 126.34924,55.910863 c 41.54647,-32.952327 102.5948,-0.905053 98.18473,51.541997 -1.50182,17.86043 -6.90484,27.66745 -25.34204,45.99828 l -6.85255,6.81302 h -1.30245 -1.30245 l -9.23084,-9.19427 c -5.23119,-5.21045 -9.3927,-9.56782 -9.60439,-10.05639 -0.68655,-1.5845 -0.44769,-1.89305 8.34481,-10.77954 10.07564,-10.18335 11.76153,-12.45408 13.61891,-18.34331 7.36307,-23.346184 -14.7662,-45.211633 -37.93595,-37.483684 -6.11717,2.04029 -7.63165,3.23438 -21.10859,16.64299 l -11.71311,11.653724 31.35842,31.36255 c 34.29087,34.29538 32.03326,31.83093 30.97951,33.81784 -0.76337,1.4394 -76.38158,76.6301 -77.82527,77.3853 -2.61462,1.36771 -6.21408,1.86131 -8.923512,1.22369 z m 6.844022,-50.26472 c 24.80444,-3.44998 34.77631,-34.05871 16.6922,-51.23684 -18.69981,-17.76299 -49.500062,-5.18911 -50.373192,20.56433 -0.6271,18.49668 15.51714,33.19887 33.680992,30.67251 z m -8.748622,-11.08611 c -19.05073,-5.06896 -18.95399,-32.29299 0.13229,-37.22612 3.38201,-0.87413 10.621562,-0.39034 10.164072,0.67923 -1.05639,2.46975 -1.14575,3.85862 -0.37986,5.90421 1.62525,4.34083 6.42005,6.18157 10.40675,3.9952 l 1.53784,-0.84338 0.53717,1.24025 c 6.33111,14.61762 -7.07381,30.32809 -22.398262,26.25061 z"
id="path1" /></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

5
assets/img/icons/file-css2.svg Executable file
View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M11 4h3.627c-0.078-0.126-0.172-0.266-0.286-0.421-0.347-0.473-0.831-1.027-1.362-1.558s-1.085-1.015-1.558-1.362c-0.155-0.114-0.295-0.208-0.421-0.286v3.627z"></path>
<path d="M10.5 5c-0.276 0-0.5-0.224-0.5-0.5v-4.5h-7.75c-0.689 0-1.25 0.561-1.25 1.25v13.5c0 0.689 0.561 1.25 1.25 1.25h11.5c0.689 0 1.25-0.561 1.25-1.25v-9.75h-4.5zM6 11.25v1.25c0 0.276 0.224 0.5 0.5 0.5s0.5 0.224 0.5 0.5-0.224 0.5-0.5 0.5c-0.827 0-1.5-0.673-1.5-1.5v-1.25c0-0.138-0.112-0.25-0.25-0.25h-0.25c-0.276 0-0.5-0.224-0.5-0.5s0.224-0.5 0.5-0.5h0.25c0.138 0 0.25-0.112 0.25-0.25v-1.25c0-0.827 0.673-1.5 1.5-1.5 0.276 0 0.5 0.224 0.5 0.5s-0.224 0.5-0.5 0.5c-0.276 0-0.5 0.224-0.5 0.5v1.25c0 0.281-0.093 0.541-0.251 0.75 0.157 0.209 0.251 0.469 0.251 0.75zM11.5 11h-0.25c-0.138 0-0.25 0.112-0.25 0.25v1.25c0 0.827-0.673 1.5-1.5 1.5-0.276 0-0.5-0.224-0.5-0.5s0.224-0.5 0.5-0.5c0.276 0 0.5-0.224 0.5-0.5v-1.25c0-0.281 0.093-0.541 0.251-0.75-0.157-0.209-0.251-0.469-0.251-0.75v-1.25c0-0.276-0.224-0.5-0.5-0.5s-0.5-0.224-0.5-0.5 0.224-0.5 0.5-0.5c0.827 0 1.5 0.673 1.5 1.5v1.25c0 0.138 0.112 0.25 0.25 0.25h0.25c0.276 0 0.5 0.224 0.5 0.5s-0.224 0.5-0.5 0.5z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

6
assets/img/icons/file-eye2.svg Executable file
View File

@@ -0,0 +1,6 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M11 4h3.627c-0.078-0.126-0.172-0.266-0.286-0.421-0.347-0.473-0.831-1.027-1.362-1.558s-1.085-1.015-1.558-1.362c-0.155-0.114-0.295-0.208-0.421-0.286v3.627z"></path>
<path d="M5.755 15.881c-1.241-0.806-2.19-1.932-2.743-3.254-0.167-0.399-0.167-0.856 0-1.254 0.553-1.322 1.502-2.448 2.743-3.254 1.253-0.814 2.721-1.244 4.245-1.244s2.992 0.43 4.245 1.244c0.266 0.172 0.518 0.36 0.755 0.56v-3.679h-4.5c-0.276 0-0.5-0.224-0.5-0.5v-4.5h-7.75c-0.689 0-1.25 0.561-1.25 1.25v13.5c0 0.689 0.561 1.25 1.25 1.25h3.695c-0.064-0.039-0.127-0.078-0.19-0.119z"></path>
<path d="M15.95 11.807c-0.466-1.113-1.267-2.062-2.318-2.745-1.070-0.695-2.326-1.062-3.632-1.062s-2.562 0.367-3.632 1.062c-1.051 0.683-1.853 1.632-2.318 2.745-0.052 0.123-0.052 0.262 0 0.386 0.466 1.113 1.267 2.062 2.318 2.745 1.070 0.695 2.326 1.062 3.632 1.062s2.562-0.367 3.632-1.062c1.051-0.683 1.853-1.632 2.318-2.745 0.052-0.123 0.052-0.262-0-0.386zM10 11c0 0.552-0.448 1-1 1s-1-0.448-1-1 0.448-1 1-1 1 0.448 1 1zM13.087 14.099c-0.908 0.589-1.975 0.901-3.087 0.901s-2.18-0.312-3.087-0.901c-0.82-0.533-1.458-1.255-1.855-2.099 0.397-0.844 1.035-1.566 1.855-2.099 0.102-0.066 0.206-0.128 0.311-0.188-0.199 0.405-0.311 0.86-0.311 1.342 0 1.681 1.363 3.044 3.044 3.044s3.044-1.363 3.044-3.044c0-0.508-0.125-0.986-0.344-1.407 0.147 0.078 0.292 0.162 0.432 0.253 0.82 0.533 1.457 1.255 1.855 2.099-0.397 0.844-1.035 1.566-1.855 2.099v0 0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

5
assets/img/icons/file-plus.svg Executable file
View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.341 3.579c-0.347-0.473-0.831-1.027-1.362-1.558s-1.085-1.015-1.558-1.362c-0.806-0.591-1.197-0.659-1.421-0.659h-7.75c-0.689 0-1.25 0.561-1.25 1.25v13.5c0 0.689 0.561 1.25 1.25 1.25h6.25c0.276 0 0.5-0.224 0.5-0.5s-0.224-0.5-0.5-0.5h-6.25c-0.135 0-0.25-0.114-0.25-0.25v-13.5c0-0.135 0.115-0.25 0.25-0.25 0 0 7.749-0 7.75 0v3.5c0 0.276 0.224 0.5 0.5 0.5h3.5v3.5c0 0.276 0.224 0.5 0.5 0.5s0.5-0.224 0.5-0.5v-3.5c0-0.224-0.068-0.615-0.659-1.421zM11 4v-2.405c0.359 0.278 0.792 0.654 1.271 1.134s0.856 0.912 1.134 1.271h-2.406z"></path>
<path d="M16 12h-2v-2h-2v2h-2v2h2v2h2v-2h2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 732 B

View File

@@ -0,0 +1,6 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M11 4h3.627c-0.078-0.126-0.172-0.266-0.286-0.421-0.347-0.473-0.831-1.027-1.362-1.558s-1.085-1.015-1.558-1.362c-0.155-0.114-0.295-0.208-0.421-0.286v3.627z"></path>
<path d="M7.875 12.5c0-2.55 2.075-4.625 4.625-4.625 0.92 0 1.779 0.27 2.5 0.736v-3.611h-4.5c-0.276 0-0.5-0.224-0.5-0.5v-4.5h-7.75c-0.689 0-1.25 0.561-1.25 1.25v13.5c0 0.689 0.561 1.25 1.25 1.25h7.23c-0.982-0.849-1.605-2.103-1.605-3.5z"></path>
<path d="M12.5 9c-1.933 0-3.5 1.567-3.5 3.5s1.567 3.5 3.5 3.5c1.933 0 3.5-1.567 3.5-3.5s-1.567-3.5-3.5-3.5zM13 13v2h-1v-2h-2v-1h2v-2h1v2h2v1h-2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 708 B

View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M11 4h3.627c-0.078-0.126-0.172-0.266-0.286-0.421-0.347-0.473-0.831-1.027-1.362-1.558s-1.085-1.015-1.558-1.362c-0.155-0.114-0.295-0.208-0.421-0.286v3.627z"></path>
<path d="M10.5 5c-0.276 0-0.5-0.224-0.5-0.5v-4.5h-7.75c-0.689 0-1.25 0.561-1.25 1.25v13.5c0 0.689 0.561 1.25 1.25 1.25h11.5c0.689 0 1.25-0.561 1.25-1.25v-9.75h-4.5zM11.5 13h-7c-0.276 0-0.5-0.224-0.5-0.5s0.224-0.5 0.5-0.5h7c0.276 0 0.5 0.224 0.5 0.5s-0.224 0.5-0.5 0.5zM11.5 11h-7c-0.276 0-0.5-0.224-0.5-0.5s0.224-0.5 0.5-0.5h7c0.276 0 0.5 0.224 0.5 0.5s-0.224 0.5-0.5 0.5zM11.5 9h-7c-0.276 0-0.5-0.224-0.5-0.5s0.224-0.5 0.5-0.5h7c0.276 0 0.5 0.224 0.5 0.5s-0.224 0.5-0.5 0.5z"></path>
</svg>

After

Width:  |  Height:  |  Size: 795 B

5
assets/img/icons/file-xml2.svg Executable file
View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M11 4h3.627c-0.078-0.126-0.172-0.266-0.286-0.421-0.347-0.473-0.831-1.027-1.362-1.558s-1.085-1.015-1.558-1.362c-0.155-0.114-0.295-0.208-0.421-0.286v3.627z"></path>
<path d="M10.5 5c-0.276 0-0.5-0.224-0.5-0.5v-4.5h-7.75c-0.689 0-1.25 0.561-1.25 1.25v13.5c0 0.689 0.561 1.25 1.25 1.25h11.5c0.689 0 1.25-0.561 1.25-1.25v-9.75h-4.5zM7.5 13l-1 1-3-3 3-3 1 1-2 2 2 2zM9.5 14l-1-1 2-2-2-2 1-1 3 3-3 3z"></path>
</svg>

After

Width:  |  Height:  |  Size: 550 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15 4h-14l1-2h5.5l0.5 1h6.5zM0 5l1 10h14l1-10h-16zM6.719 13.352l-3.207-3.707 0.914-0.914 2.293 1.793 4.293-3.793 0.914 0.914-5.207 5.707z"></path>
</svg>

After

Width:  |  Height:  |  Size: 294 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15 4h-14l1-2h5.5l0.5 1h6.5zM7.813 12.5c0 0.918 0.266 1.776 0.724 2.5h-7.537l-1-10h16l-0.399 3.988c-0.827-0.731-1.913-1.176-3.101-1.176-2.585 0-4.688 2.103-4.688 4.687zM12.5 9c-1.933 0-3.5 1.567-3.5 3.5s1.567 3.5 3.5 3.5 3.5-1.567 3.5-3.5-1.567-3.5-3.5-3.5zM10 13v-1h5v1h-5z"></path>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15 4h-14l1-2h5.5l0.5 1h6.5zM0 5l1 10h14l1-10h-16zM11 11h-6v-2h6v2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 224 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15 4h-14l1-2h5.5l0.5 1h6.5zM7.813 12.5c0 0.918 0.266 1.776 0.724 2.5h-7.537l-1-10h16l-0.399 3.988c-0.827-0.731-1.913-1.176-3.101-1.176-2.585 0-4.688 2.103-4.688 4.687zM12.5 9c-1.933 0-3.5 1.567-3.5 3.5s1.567 3.5 3.5 3.5 3.5-1.567 3.5-3.5-1.567-3.5-3.5-3.5zM13 13v2h-1v-2h-2v-1h2v-2h1v2h2v1h-2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15 4h-14l1-2h5.5l0.5 1h6.5zM0 5l1 10h14l1-10h-16zM11 11h-2v2h-2v-2h-2v-2h2v-2h2v2h2v2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 244 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15 4h-14l1-2h5.5l0.5 1h6.5l0.5 1zM0 5l1 10h14l1-10h-16zM11.25 7.811l-2.189 2.189 2.189 2.189-1.061 1.061-2.189-2.189-2.189 2.189-1.061-1.061 2.189-2.189-2.189-2.189 1.061-1.061 2.189 2.189 2.189-2.189 1.061 1.061z"></path>
</svg>

After

Width:  |  Height:  |  Size: 371 B

View File

@@ -0,0 +1,6 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.5 2h-6.5l-0.5-1h-5.5l-1 2h14z"></path>
<path d="M14.13 11h1.17l0.7-7h-16l1 10h7.564c-1.639-0.59-2.814-2.16-2.814-4 0-2.343 1.907-4.25 4.25-4.25s4.25 1.907 4.25 4.25c0 0.339-0.041 0.674-0.12 1z"></path>
<path d="M15.672 14.277l-3.101-2.73c0.273-0.452 0.43-0.981 0.43-1.548 0-1.657-1.343-3-3-3s-3 1.343-3 3 1.343 3 3 3c0.566 0 1.096-0.157 1.548-0.43l2.73 3.101c0.359 0.417 0.971 0.44 1.359 0.051l0.086-0.086c0.389-0.389 0.366-1.001-0.051-1.359zM10 11.938c-1.070 0-1.938-0.867-1.938-1.938s0.867-1.938 1.938-1.938 1.938 0.867 1.938 1.938-0.867 1.938-1.938 1.938z"></path>
</svg>

After

Width:  |  Height:  |  Size: 719 B

4
assets/img/icons/hammer2.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15.784 14.309l-8.572-7.804 0.399-0.4c0.326-0.327 0.503-0.75 0.53-1.181 0.016-0.007 0.031-0.014 0.046-0.023l1.609-1.006c0.218-0.256 0.202-0.66-0.036-0.898l-2.799-2.806c-0.237-0.238-0.641-0.254-0.896-0.036l-1.004 1.614c-0.008 0.015-0.015 0.031-0.022 0.046-0.43 0.027-0.852 0.204-1.178 0.531l-1.522 1.527c-0.327 0.327-0.503 0.75-0.53 1.181-0.016 0.007-0.031 0.014-0.046 0.023l-1.609 1.006c-0.218 0.256-0.202 0.66 0.036 0.898l2.799 2.806c0.237 0.238 0.641 0.254 0.896 0.036l1.004-1.614c0.008-0.015 0.015-0.031 0.023-0.046 0.43-0.027 0.852-0.204 1.178-0.531l0.442-0.443 7.783 8.596c0.226 0.249 0.573 0.289 0.773 0.089l0.787-0.789c0.199-0.2 0.159-0.549-0.089-0.775z"></path>
</svg>

After

Width:  |  Height:  |  Size: 817 B

4
assets/img/icons/hand.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M12 16h-5c-0.133 0-0.26-0.053-0.354-0.146l-3-3c-0.027-0.027-0.050-0.056-0.070-0.089l-2.5-4c-0.143-0.229-0.078-0.531 0.147-0.681l1.5-1c0.213-0.142 0.498-0.1 0.661 0.096l1.616 1.939v-7.619c0-0.276 0.224-0.5 0.5-0.5h1.5v-0.5c0-0.276 0.224-0.5 0.5-0.5h2c0.276 0 0.5 0.224 0.5 0.5v0.5h1.5c0.276 0 0.5 0.224 0.5 0.5v1.5h1.5c0.276 0 0.5 0.224 0.5 0.5v9c0 0.078-0.018 0.154-0.053 0.224l-1.5 3c-0.085 0.169-0.258 0.276-0.447 0.276zM7.207 15h4.484l1.309-2.618v-8.382h-1v4.5c0 0.276-0.224 0.5-0.5 0.5s-0.5-0.224-0.5-0.5v-6.5h-1v6.5c0 0.276-0.224 0.5-0.5 0.5s-0.5-0.224-0.5-0.5v-7.5h-1v7.5c0 0.276-0.224 0.5-0.5 0.5s-0.5-0.224-0.5-0.5v-6.5h-1v8.5c0 0.21-0.132 0.398-0.33 0.47s-0.42 0.012-0.554-0.15l-2.212-2.655-0.722 0.481 2.213 3.54 2.813 2.813z"></path>
</svg>

After

Width:  |  Height:  |  Size: 892 B

4
assets/img/icons/history.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="17" height="16" viewBox="0 0 17 16">
<path d="M10 1c3.866 0 7 3.134 7 7s-3.134 7-7 7v-1.5c1.469 0 2.85-0.572 3.889-1.611s1.611-2.42 1.611-3.889c0-1.469-0.572-2.85-1.611-3.889s-2.42-1.611-3.889-1.611c-1.469 0-2.85 0.572-3.889 1.611-0.799 0.799-1.322 1.801-1.52 2.889h2.909l-3.5 4-3.5-4h2.571c0.485-3.392 3.402-6 6.929-6zM13 7v2h-4v-5h2v3z"></path>
</svg>

After

Width:  |  Height:  |  Size: 448 B

View File

@@ -0,0 +1,16 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 494.1 100" style="enable-background:new 0 0 494.1 100;" xml:space="preserve">
<path d="M0,10.7h14.2v74.5h39.3v13.1H0V10.7z M67.5,10.7c4.8,0,8.9,3.7,8.9,8.6c0,4.9-4,8.8-8.9,8.8c-4.9,0-8.9-3.9-8.9-8.8
C58.6,14.5,62.5,10.7,67.5,10.7z M60.5,35.2h13.6v63.2H60.5V35.2z M82.2,35.2h13.6v8.7c4-6.7,10.9-10.4,20.1-10.4
c14.8,0,24,11.5,24,29.7v35.1h-13.6V64.5c0-11.8-5.2-18.5-14.5-18.5c-10.3,0-15.9,7-15.9,19.6v32.7H82.2L82.2,35.2L82.2,35.2z
M147.1,10.7h13.6v55.4l25.4-30.9h17.1l-27.1,31.6l27.1,31.5h-17.1l-25.4-30.8v30.8h-13.6V10.7z M208.6,19.1h13.9v16.1h16.2v11.3
h-16.2v32.5c0,4.1,2.5,6.7,6.5,6.7h9.1v12.7h-10.9c-11.8,0-18.5-7-18.5-19.4L208.6,19.1L208.6,19.1z M245.6,35.2h12.6V43
c3.4-6,9-9.5,15.9-9.5c2.1,0,3.2,0.1,4.8,0.6v12.6c-0.9-0.2-2.3-0.5-5.1-0.5c-10,0-15.5,8.4-15.5,22.8v29.2h-13.6V35.2H245.6z
M310.8,33.5c15,0,31.3,9,31.3,34.7V70h-48.8c1.1,11.3,7.6,17.5,18.6,17.5c7.9,0,14.5-4.2,16-10.1h13.9
C340.3,90,327.1,100,311.8,100c-19.6,0-32-12.7-32-33.3C279.8,48.4,291.7,33.5,310.8,33.5z M327.5,58.8c-1.9-7.8-8.1-12.7-16.7-12.7
c-8.3,0-14.2,5-16.5,12.7H327.5z M379.1,33.5c15,0,31.3,9,31.3,34.7V70h-48.8c1.1,11.3,7.6,17.5,18.6,17.5c7.9,0,14.5-4.2,16-10.1
H410C408.6,90,395.4,100,380.1,100c-19.6,0-32-12.7-32-33.3C348.1,48.4,360,33.5,379.1,33.5z M395.8,58.8
c-1.9-7.8-8.1-12.7-16.8-12.7c-8.3,0-14.2,5-16.5,12.7H395.8z M413.7,33.3H438l-17.3-16.4l9.5-9.7L446.7,24V0h14.3v24l16.5-16.8
l9.5,9.7l-17.3,16.4h24.3v13.6h-24.5L487,63.7l-9.5,9.5l-23.7-23.7l-23.7,23.7l-9.5-9.5L438,46.8h-24.5V33.3H413.7z M446.8,66.2
h14.3v32.2h-14.3V66.2z">
</path>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

39
assets/img/icons/linktree.svg Executable file
View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 98 98.000003"
xml:space="preserve"
sodipodi:docname="linktree.svg"
width="98"
height="98"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="3.7971541"
inkscape:cx="-1.1850981"
inkscape:cy="63.205231"
inkscape:window-width="1920"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<path
d="M 9.2,33.2 H 33.4 L 16.1,16.8 25.6,7.2 42,23.9 V 0.1 H 56.2 V 23.9 L 72.6,7.2 82.1,16.8 64.8,33.1 H 89 V 46.6 H 64.7 L 82,63.3 72.5,72.7 49,49.2 25.5,72.8 16,63.3 33.3,46.6 H 9 V 33.2 Z m 32.9,32.7 h 14.2 v 32 H 42.1 Z"
id="path1">
</path>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

40
assets/img/icons/loyalfans.svg Executable file
View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 213.99999 214"
xml:space="preserve"
sodipodi:docname="loyalfans.svg"
width="214"
height="214"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.5023135"
inkscape:cx="-47.593262"
inkscape:cy="179.72281"
inkscape:window-width="1920"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<path
d="m 71.2,29.9 c 8.5,-8.5 22.5,-8.5 31,0 8.5,8.5 8.5,22.5 0,31 -8.5,8.5 -22.5,8.5 -31,0 -8.5,-8.5 -8.7,-22.5 0,-31 z M 212,0.4 133.9,97.3 c -1.2,1.4 -2.4,3 -3.6,4.4 -5.9,7.7 -11.1,16 -15.2,24.9 -8.3,17.6 -13.1,37.4 -13.1,58.3 v 28.7 H 71 v -30.1 c 0,-2.6 0,-5.1 -0.2,-7.7 -1,-18 -5.5,-34.8 -12.7,-50.2 -1.8,-3.8 -3.8,-7.5 -5.9,-11.3 L 51.4,112.9 2,30.5 l 84.8,72 v 0 0 z"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,8 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M5 2h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1z"></path>
<path d="M11 6h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1zM11 3h2v2h-2v-2z"></path>
<path d="M5 10h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM5 13h-2v-2h2v2z"></path>
<path d="M13 10h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1z"></path>
<path d="M14 8h-1c-1.336 0-2.591-0.52-3.536-1.464s-1.464-2.2-1.464-3.536v-1c0-1.1-0.9-2-2-2h-4c-1.1 0-2 0.9-2 2v4c0 1.1 0.9 2 2 2h1c1.336 0 2.591 0.52 3.536 1.464s1.464 2.2 1.464 3.536v1c0 1.1 0.9 2 2 2h4c1.1 0 2-0.9 2-2v-4c0-1.1-0.9-2-2-2zM15 14c0 0.265-0.105 0.515-0.295 0.705s-0.44 0.295-0.705 0.295h-4c-0.265 0-0.515-0.105-0.705-0.295s-0.295-0.44-0.295-0.705v-1c0-3.314-2.686-6-6-6h-1c-0.265 0-0.515-0.105-0.705-0.295s-0.295-0.441-0.295-0.705v-4c0-0.265 0.105-0.515 0.295-0.705s0.44-0.295 0.705-0.295h4c0.265 0 0.515 0.105 0.705 0.295s0.295 0.44 0.295 0.705v1c0 3.314 2.686 6 6 6h1c0.265 0 0.515 0.105 0.705 0.295s0.295 0.44 0.295 0.705v4z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" data-name="logo-default" data-testid="logo-default-icon" viewBox="0 -0.04 154.28 24.03">
<path d="m110.215 23.07 7.873-17.456h-3.862l-4.534 9.913-4.557-9.913h-3.858l7.89 17.465zm13.452-17.506h-3.537V23.07h3.537zm3.33.05v17.434h1.417v.031h3.51a8.76 8.76 0 0 0 6.154-2.567 7.963 7.963 0 0 0 1.868-2.761 8.624 8.624 0 0 0 0-6.8 8.348 8.348 0 0 0-1.868-2.793 8.586 8.586 0 0 0-6.154-2.54zm3.483 13.978V9.101h1.444a5.245 5.245 0 0 1 0 10.49zm17.339-3.885.077.027c1.818.623 2.892 1.493 2.892 2.441a1.919 1.919 0 0 1-1.918 1.769 6.034 6.034 0 0 1-4.68-1.965l-2.54 2.391a9.681 9.681 0 0 0 4.155 2.639 10.322 10.322 0 0 0 3.068.424 5.418 5.418 0 0 0 5.405-5.247 5.053 5.053 0 0 0-1.367-3.415 9.316 9.316 0 0 0-3.858-2.319c-.149-.05-.3-.1-.5-.149-1.6-.4-2.22-.9-2.242-1.791a1.225 1.225 0 0 1 .3-1 2.664 2.664 0 0 1 1.543-.65 5.379 5.379 0 0 1 3.366 1.2l.618.4 1.818-2.964-.573-.374a8.663 8.663 0 0 0-5.234-1.742 6.071 6.071 0 0 0-4.061 1.715 4.67 4.67 0 0 0-1.272 3.56 4.815 4.815 0 0 0 2.238 3.939 8.459 8.459 0 0 0 2.639 1.1zM95.543 5.61l-3.56 6.574-3.587-6.574h-3.858l5.7 11.059v6.4h3.483v-6.4L99.405 5.61zm-17.484 0v9.764L68.422 5.61h-1.1v17.461h3.488V12.882l9.809 10.188h.92V5.609zm-21.991 0-8.893 17.451h3.912l1.642-3.24h7.224l1.521 3.244h3.858l-8.244-17.46zm.42 6.849 1.818 3.889h-3.785zM43.21 5.605l-7.273 6.619L28.645 5.6h-1.2v17.461h3.488V12.377l5 4.557 5.008-4.557v10.684h3.488V5.6H43.21zM10.63 19.118a1.876 1.876 0 0 0-.041-.217c-.239-.911-.465-1.823-.726-2.725a.569.569 0 0 1 .176-.677 1.3 1.3 0 0 0 .221-1.543 1.418 1.418 0 0 0-1.313-.772 1.376 1.376 0 0 0-1.232.938 1.3 1.3 0 0 0 .415 1.507.358.358 0 0 1 .117.438c-.257.916-.492 1.836-.735 2.757a2.781 2.781 0 0 0-.041.289h3.158m-1.6-7.9a5.417 5.417 0 0 1 1.642-2.134 4.357 4.357 0 0 1 5.577.352 5.785 5.785 0 0 1 1.187 6.736 13.445 13.445 0 0 1-2.725 3.61 28.84 28.84 0 0 1-5.491 4.16.424.424 0 0 1-.352.027 27.5 27.5 0 0 1-6.4-5.094 9.672 9.672 0 0 1-2.279-3.84 5.677 5.677 0 0 1 1.8-5.766 4.324 4.324 0 0 1 6.6 1.169c.149.239.28.492.438.781M5.503 2.722c.4.379.79.781 1.214 1.132.352.293.537.262.772-.126.329-.546.618-1.11.9-1.683.036-.077-.036-.244-.108-.329A1.015 1.015 0 0 1 8.387.222a1.006 1.006 0 0 1 1.363 1.48.31.31 0 0 0-.072.438c.3.532.573 1.087.893 1.611.221.361.37.37.722.126a3.189 3.189 0 0 0 .447-.37c.266-.266.519-.541.776-.808-.492-.672-.4-1.358.2-1.678a1.006 1.006 0 0 1 1.354.406.985.985 0 0 1-.424 1.367.619.619 0 0 0-.388.492c-.23.947-.483 1.886-.717 2.833a.486.486 0 0 1-.532.415q-3-.007-5.992 0a.48.48 0 0 1-.532-.411c-.244-.966-.505-1.931-.74-2.9a.513.513 0 0 0-.334-.411 1 1 0 0 1-.564-1.132 1.026 1.026 0 0 1 .862-.767.982.982 0 0 1 1.074.659c.14.361.063.672-.284 1.142"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
data-name="logo-default"
data-testid="logo-default-icon"
viewBox="0 -0.04 25 24.999999"
version="1.1"
id="svg1"
width="25"
height="25"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<path
d="m 14.114527,19.614142 c -0.0094,-0.07308 -0.02308,-0.145535 -0.041,-0.217 -0.239,-0.911 -0.465,-1.823 -0.726,-2.725 -0.103352,-0.239339 -0.03083,-0.5183 0.176,-0.677 0.408042,-0.408238 0.498047,-1.036638 0.221,-1.543 -0.250584,-0.490502 -0.762542,-0.791516 -1.313,-0.772 -0.56432,0.03001 -1.052913,0.40201 -1.232,0.938 -0.212834,0.538692 -0.04361,1.15321 0.415,1.507 0.138253,0.09956 0.18719,0.28276 0.117,0.438 -0.257,0.916 -0.492,1.836 -0.735,2.757 -0.01872,0.09555 -0.0324,0.192015 -0.041,0.289 h 3.158 m -1.6,-7.9 c 0.359002,-0.838194 0.923783,-1.572203 1.642,-2.1340003 1.699386,-1.2472131 4.047914,-1.0989825 5.577,0.352 1.822799,1.7459463 2.303203,4.4721513 1.187,6.7360003 -0.697958,1.348581 -1.619326,2.569183 -2.725,3.61 -1.657551,1.601073 -3.501032,2.997701 -5.491,4.16 -0.107829,0.05974 -0.236321,0.0696 -0.352,0.027 -2.3753486,-1.369455 -4.5325397,-3.086444 -6.3999999,-5.094 -1.048787,-1.084717 -1.8292518,-2.39976 -2.2790001,-3.84 -0.5571739,-2.108189 0.1424143,-4.349203 1.8000001,-5.7660003 2.0738766,-1.7880035 5.2664089,-1.2225383 6.5999999,1.1690003 0.149,0.239 0.28,0.492 0.438,0.781 M 8.9875271,3.2181417 c 0.4,0.379 0.79,0.781 1.2139999,1.132 0.352,0.293 0.537,0.262 0.772,-0.126 0.329,-0.546 0.618,-1.11 0.9,-1.683 0.036,-0.077 -0.036,-0.244 -0.108,-0.329 -0.410242,-0.4325033 -0.361198,-1.1237515 0.106,-1.49399998 0.986666,-0.90866627 2.349666,0.57133368 1.363,1.47999998 -0.142839,0.099814 -0.17537,0.2977135 -0.072,0.438 0.3,0.532 0.573,1.087 0.893,1.611 0.221,0.361 0.37,0.37 0.722,0.126 0.159934,-0.109493 0.309554,-0.2333389 0.447,-0.37 0.266,-0.266 0.519,-0.541 0.776,-0.808 -0.492,-0.672 -0.4,-1.358 0.2,-1.678 0.486391,-0.2579836 1.089809,-0.077047 1.354,0.406 0.277315,0.4928925 0.08357,1.1175522 -0.424,1.367 -0.208917,0.081596 -0.357352,0.2698181 -0.388,0.492 -0.23,0.947 -0.483,1.886 -0.717,2.833 -0.03678,0.2589486 -0.271887,0.4423538 -0.532,0.415 -2,-0.00467 -3.997333,-0.00467 -5.9919999,0 -0.2596827,0.030788 -0.4962279,-0.1519569 -0.532,-0.411 -0.244,-0.966 -0.505,-1.931 -0.74,-2.9 -0.026543,-0.1883196 -0.15509,-0.3465019 -0.334,-0.411 -0.4330367,-0.1954461 -0.6687764,-0.6685975 -0.564,-1.132 0.1027353,-0.4083194 0.4445066,-0.7124244 0.862,-0.767 0.4703187,-0.069255 0.9227059,0.2083271 1.074,0.659 0.14,0.361 0.063,0.672 -0.284,1.142"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

6
assets/img/icons/markup.svg Executable file
View File

@@ -0,0 +1,6 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="16" viewBox="0 0 24 16">
<path d="M17 7v-2h-2v-2h-2v2h-2v-2h-2v2h-2v2h2v2h-2v2h2v2h2v-2h2v2h2v-2h2v-2h-2v-2h2zM13 9h-2v-2h2v2z"></path>
<path d="M8.707 1.707l-1.414-1.414-7.293 7.293v0.828l7.293 7.293 1.414-1.414-6.293-6.293z"></path>
<path d="M15.293 1.707l1.414-1.414 7.293 7.293v0.828l-7.293 7.293-1.414-1.414 6.293-6.293z"></path>
</svg>

After

Width:  |  Height:  |  Size: 448 B

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
viewBox="0 0 75 32"
id="svg1"
sodipodi:docname="matrix-full.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="12.000629"
inkscape:cx="27.79021"
inkscape:cy="22.165505"
inkscape:window-width="1920"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<title
id="title1">Matrix (protocol) logo</title>
<g
id="g1">
<path
d="m0.936 0.732v30.52h2.194v0.732h-3.035v-31.98h3.034v0.732zm8.45 9.675v1.544h0.044a4.461 4.461 0 0 1 1.487-1.368c0.58-0.323 1.245-0.485 1.993-0.485 0.72 0 1.377 0.14 1.972 0.42 0.595 0.279 1.047 0.771 1.355 1.477 0.338-0.5 0.796-0.941 1.377-1.323 0.58-0.383 1.266-0.574 2.06-0.574 0.602 0 1.16 0.074 1.674 0.22 0.514 0.148 0.954 0.383 1.322 0.707 0.366 0.323 0.653 0.746 0.859 1.268 0.205 0.522 0.308 1.15 0.308 1.887v7.633h-3.127v-6.464c0-0.383-0.015-0.743-0.044-1.082a2.305 2.305 0 0 0-0.242-0.882 1.473 1.473 0 0 0-0.584-0.596c-0.257-0.146-0.606-0.22-1.047-0.22-0.44 0-0.796 0.085-1.068 0.253-0.272 0.17-0.485 0.39-0.639 0.662a2.654 2.654 0 0 0-0.308 0.927 7.074 7.074 0 0 0-0.078 1.048v6.354h-3.128v-6.398c0-0.338-7e-3 -0.673-0.021-1.004a2.825 2.825 0 0 0-0.188-0.916 1.411 1.411 0 0 0-0.55-0.673c-0.258-0.168-0.636-0.253-1.135-0.253a2.33 2.33 0 0 0-0.584 0.1 1.94 1.94 0 0 0-0.705 0.374c-0.228 0.184-0.422 0.449-0.584 0.794-0.161 0.346-0.242 0.798-0.242 1.357v6.619h-3.129v-11.41zm16.46 1.677a3.751 3.751 0 0 1 1.233-1.17 5.37 5.37 0 0 1 1.685-0.629 9.579 9.579 0 0 1 1.884-0.187c0.573 0 1.153 0.04 1.74 0.121 0.588 0.081 1.124 0.24 1.609 0.475 0.484 0.235 0.88 0.562 1.19 0.981 0.308 0.42 0.462 0.975 0.462 1.666v5.934c0 0.516 0.03 1.008 0.088 1.478 0.058 0.471 0.161 0.824 0.308 1.06h-3.171a4.435 4.435 0 0 1-0.22-1.104c-0.5 0.515-1.087 0.876-1.762 1.081a7.084 7.084 0 0 1-2.071 0.31c-0.544 0-1.05-0.067-1.52-0.2a3.472 3.472 0 0 1-1.234-0.617 2.87 2.87 0 0 1-0.826-1.059c-0.199-0.426-0.298-0.934-0.298-1.522 0-0.647 0.114-1.18 0.342-1.6 0.227-0.419 0.52-0.753 0.881-1.004 0.36-0.25 0.771-0.437 1.234-0.562 0.462-0.125 0.929-0.224 1.399-0.298 0.47-0.073 0.932-0.132 1.387-0.176 0.456-0.044 0.86-0.11 1.212-0.199 0.353-0.088 0.631-0.217 0.837-0.386s0.301-0.415 0.287-0.74c0-0.337-0.055-0.606-0.166-0.804a1.217 1.217 0 0 0-0.44-0.464 1.737 1.737 0 0 0-0.639-0.22 5.292 5.292 0 0 0-0.782-0.055c-0.617 0-1.101 0.132-1.454 0.397-0.352 0.264-0.558 0.706-0.617 1.323h-3.128c0.044-0.735 0.227-1.345 0.55-1.83zm6.179 4.423a5.095 5.095 0 0 1-0.639 0.165 9.68 9.68 0 0 1-0.716 0.11c-0.25 0.03-0.5 0.067-0.749 0.11a5.616 5.616 0 0 0-0.694 0.177 2.057 2.057 0 0 0-0.594 0.298c-0.17 0.125-0.305 0.284-0.408 0.474-0.103 0.192-0.154 0.434-0.154 0.728 0 0.28 0.051 0.515 0.154 0.706 0.103 0.192 0.242 0.342 0.419 0.453 0.176 0.11 0.381 0.187 0.617 0.231 0.234 0.044 0.477 0.066 0.726 0.066 0.617 0 1.094-0.102 1.432-0.309 0.338-0.205 0.587-0.452 0.75-0.739 0.16-0.286 0.26-0.576 0.297-0.87 0.036-0.295 0.055-0.53 0.055-0.707v-1.17a1.4 1.4 0 0 1-0.496 0.277zm11.86-6.1v2.096h-2.291v5.647c0 0.53 0.088 0.883 0.264 1.059 0.176 0.177 0.529 0.265 1.057 0.265 0.177 0 0.345-7e-3 0.507-0.022 0.161-0.015 0.316-0.037 0.463-0.066v2.426a7.49 7.49 0 0 1-0.882 0.089 21.67 21.67 0 0 1-0.947 0.022c-0.484 0-0.944-0.034-1.377-0.1a3.233 3.233 0 0 1-1.145-0.386 2.04 2.04 0 0 1-0.782-0.816c-0.191-0.353-0.287-0.816-0.287-1.39v-6.728h-1.894v-2.096h1.894v-3.42h3.129v3.42h2.29zm4.471 0v2.118h0.044a3.907 3.907 0 0 1 1.454-1.754 4.213 4.213 0 0 1 1.036-0.497 3.734 3.734 0 0 1 1.145-0.176c0.206 0 0.433 0.037 0.683 0.11v2.912a5.862 5.862 0 0 0-0.528-0.077 5.566 5.566 0 0 0-0.595-0.033c-0.573 0-1.058 0.096-1.454 0.287a2.52 2.52 0 0 0-0.958 0.783 3.143 3.143 0 0 0-0.518 1.158 6.32 6.32 0 0 0-0.154 1.434v5.14h-3.128v-11.4zm5.684-1.765v-2.582h3.128v2.582h-3.127zm3.128 1.765v11.4h-3.127v-11.4h3.128zm1.63 0h3.569l2.005 2.978 1.982-2.978h3.459l-3.745 5.339 4.208 6.067h-3.57l-2.378-3.596-2.38 3.596h-3.502l4.097-6.001zm15.3 20.84v-30.52h-2.194v-0.732h3.035v31.98h-3.035v-0.732z"
id="path1" />
</g>
<metadata
id="metadata1">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>Matrix (protocol) logo</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg
fill="#000000"
width="800px"
height="800px"
viewBox="0 0 32 32"
version="1.1"
id="svg1"
sodipodi:docname="matrix.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.23142407"
inkscape:cx="412.66234"
inkscape:cy="557.41825"
inkscape:window-width="1920"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="M0.844 0.735v30.531h2.197v0.735h-3.041v-32h3.041v0.735zM10.235 10.412v1.547h0.041c0.412-0.595 0.912-1.047 1.489-1.371 0.579-0.323 1.251-0.484 2-0.484 0.719 0 1.38 0.141 1.975 0.417 0.599 0.281 1.047 0.776 1.359 1.479 0.339-0.5 0.803-0.943 1.38-1.323 0.579-0.38 1.267-0.573 2.063-0.573 0.604 0 1.161 0.073 1.677 0.224 0.521 0.145 0.959 0.38 1.328 0.703 0.365 0.329 0.651 0.751 0.86 1.272 0.203 0.52 0.307 1.151 0.307 1.891v7.635h-3.129v-6.468c0-0.381-0.016-0.745-0.048-1.084-0.020-0.307-0.099-0.604-0.239-0.88-0.131-0.251-0.333-0.459-0.584-0.593-0.255-0.152-0.609-0.224-1.047-0.224-0.443 0-0.797 0.083-1.068 0.249-0.265 0.167-0.489 0.396-0.64 0.667-0.161 0.287-0.265 0.604-0.308 0.927-0.052 0.349-0.077 0.699-0.083 1.048v6.359h-3.131v-6.401c0-0.339-0.005-0.672-0.025-1-0.011-0.317-0.073-0.624-0.193-0.916-0.104-0.281-0.301-0.516-0.552-0.672-0.255-0.167-0.636-0.255-1.136-0.255-0.151 0-0.348 0.031-0.588 0.099-0.24 0.067-0.479 0.192-0.703 0.375-0.229 0.188-0.428 0.453-0.589 0.797-0.161 0.343-0.239 0.796-0.239 1.359v6.62h-3.131v-11.421zM31.156 31.265v-30.531h-2.197v-0.735h3.041v32h-3.041v-0.735z"
id="path1"
style="fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

69
assets/img/icons/onlyfans.svg Executable file → Normal file
View File

@@ -1,66 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="icon-logo"
viewBox="0 0 92.400002 92.399879"
viewBox="0 0 400 400"
version="1.1"
sodipodi:docname="onlyfans.svg"
width="92.400002"
height="92.399879"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<metadata
id="metadata21">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
id="svg2"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs19" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1026"
id="namedview17"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="6.1429902"
inkscape:cx="26.40336"
inkscape:cy="24.398909"
inkscape:window-x="1047"
inkscape:window-y="930"
inkscape:window-maximized="1"
inkscape:current-layer="icon-logo" />
id="defs2" />
<path
class="svg-logo-color-1"
d="M 46.07999,3.9024059e-5 A 46.2,46.2 0 1 0 92.39999,46.170039 46.26,46.26 0 0 0 46.07999,3.9024059e-5 Z m 0,87.569999975941 a 41.38,41.38 0 1 1 41.48,-41.4 41.44,41.44 0 0 1 -41.48,41.38 z"
id="path2"
inkscape:connector-curvature="0"
style="fill-rule:evenodd" />
d="M137.5 75a125 125 0 1 0 125 125 125 125 0 0 0-125-125zm0 162.5A37.5 37.5 0 1 1 175 200a37.45 37.45 0 0 1-37.5 37.5z"
opacity=".7"
id="path1" />
<path
d="m 65.28999,43.190039 v -3.77 a 16.75,16.75 0 0 0 -5.08,-12 17.31,17.31 0 0 0 -12.19,-5 h -3.1 a 17.31,17.31 0 0 0 -12.18,5 16.7,16.7 0 0 0 -5.07,12 v 3.77 l -2.14,3.86 v 5.56 a 18.64,18.64 0 0 0 5.64,13.33 19.37,19.37 0 0 0 13.59,5.54 h 3.45 a 19.33,19.33 0 0 0 13.55,-5.54 18.61,18.61 0 0 0 5.65,-13.33 v -5.56 z m -16.8,17.06 v 4.45 a 1.93,1.93 0 0 1 -0.89,1.64 h -0.84 a 1,1 0 0 1 -0.3,0 h -0.2 a 1.18,1.18 0 0 1 -0.25,0 h -0.38 a 2,2 0 0 1 -0.92,-1.67 v -4.42 a 5.3,5.3 0 0 1 2,-10.24 h 0.11 a 5.3,5.3 0 0 1 2,10.24 z m 9.09,-16.94 h -22.21 v -3.89 a 9.27,9.27 0 0 1 2.81,-6.63 9.62,9.62 0 0 1 6.74,-2.79 h 3.1 a 9.61,9.61 0 0 1 6.74,2.79 9.31,9.31 0 0 1 2.81,6.63 z"
class="svg-logo-color-2"
id="path14"
inkscape:connector-curvature="0" />
d="M278 168.75c31.76 9.14 69.25 0 69.25 0-10.88 47.5-45.38 77.25-95.13 80.87A124.73 124.73 0 0 1 137.5 325L175 205.81C213.55 83.3 233.31 75 324.73 75h62.77c-10.5 46.25-46.69 81.58-109.5 93.75z"
id="path2" />
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 626 B

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="icon-logo"
viewBox="0 0 92.400002 92.399879"
version="1.1"
sodipodi:docname="onlyfans.svg"
width="92.400002"
height="92.399879"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<metadata
id="metadata21">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs19" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1026"
id="namedview17"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="6.1429902"
inkscape:cx="26.40336"
inkscape:cy="24.398909"
inkscape:window-x="1047"
inkscape:window-y="930"
inkscape:window-maximized="1"
inkscape:current-layer="icon-logo" />
<path
class="svg-logo-color-1"
d="M 46.07999,3.9024059e-5 A 46.2,46.2 0 1 0 92.39999,46.170039 46.26,46.26 0 0 0 46.07999,3.9024059e-5 Z m 0,87.569999975941 a 41.38,41.38 0 1 1 41.48,-41.4 41.44,41.44 0 0 1 -41.48,41.38 z"
id="path2"
inkscape:connector-curvature="0"
style="fill-rule:evenodd" />
<path
d="m 65.28999,43.190039 v -3.77 a 16.75,16.75 0 0 0 -5.08,-12 17.31,17.31 0 0 0 -12.19,-5 h -3.1 a 17.31,17.31 0 0 0 -12.18,5 16.7,16.7 0 0 0 -5.07,12 v 3.77 l -2.14,3.86 v 5.56 a 18.64,18.64 0 0 0 5.64,13.33 19.37,19.37 0 0 0 13.59,5.54 h 3.45 a 19.33,19.33 0 0 0 13.55,-5.54 18.61,18.61 0 0 0 5.65,-13.33 v -5.56 z m -16.8,17.06 v 4.45 a 1.93,1.93 0 0 1 -0.89,1.64 h -0.84 a 1,1 0 0 1 -0.3,0 h -0.2 a 1.18,1.18 0 0 1 -0.25,0 h -0.38 a 2,2 0 0 1 -0.92,-1.67 v -4.42 a 5.3,5.3 0 0 1 2,-10.24 h 0.11 a 5.3,5.3 0 0 1 2,10.24 z m 9.09,-16.94 h -22.21 v -3.89 a 9.27,9.27 0 0 1 2.81,-6.63 9.62,9.62 0 0 1 6.74,-2.79 h 3.1 a 9.61,9.61 0 0 1 6.74,2.79 9.31,9.31 0 0 1 2.81,6.63 z"
class="svg-logo-color-2"
id="path14"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

45
assets/img/icons/pornhub.svg Executable file
View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.2"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 79.5 75"
overflow="visible"
xml:space="preserve"
sodipodi:docname="pornhub.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs3" /><sodipodi:namedview
id="namedview3"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="10.906667"
inkscape:cx="39.746333"
inkscape:cy="37.5"
inkscape:window-width="1920"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<path
id="path1"
d="M 64.099609 46 C 62.799609 46 61.599219 46.499609 60.699219 47.599609 C 59.799219 48.699609 59.300781 50.3 59.300781 52.5 C 59.300781 54.8 59.700391 56.4 60.400391 57.5 C 61.400391 59 62.700391 59.800781 64.400391 59.800781 C 65.600391 59.800781 66.699609 59.299219 67.599609 58.199219 C 68.499609 57.199219 68.9 55.4 69 53 C 69 50.5 68.499609 48.699609 67.599609 47.599609 C 66.699609 46.499609 65.499609 46 64.099609 46 z " /><path
id="path4"
d="M 3.6992188 25.300781 C 1.4992187 25.600781 0.2 26.900391 0 29.400391 L 0 70.800781 C 0.2 73.300781 1.3992187 74.700391 3.6992188 74.900391 L 75.900391 74.900391 C 78.100391 74.600391 79.399609 73.300781 79.599609 70.800781 L 79.599609 29.400391 C 79.299609 26.900391 78.100781 25.500781 75.800781 25.300781 L 3.6992188 25.300781 z M 7.0996094 33.900391 L 12.800781 33.900391 L 12.800781 43.699219 C 14.300781 42.299219 17.400781 41.599219 19.800781 41.699219 L 19.900391 41.699219 C 21.100391 41.699219 22.099609 42.000391 23.099609 42.400391 C 24.199609 42.900391 25 43.500781 25.5 44.300781 C 26 45.100781 26.400781 45.900391 26.800781 46.900391 C 27.000781 47.800391 27.099609 49.199219 27.099609 51.199219 L 27.099609 63.800781 L 21.300781 63.800781 L 21.300781 52.300781 C 21.300781 50.100781 21.2 48.6 21 48 C 20.7 47.4 20.400781 46.9 19.800781 46.5 C 19.200781 46.2 18.499219 46 17.699219 46 C 16.699219 46 15.899609 46.199219 15.099609 46.699219 C 14.299609 47.199219 13.700391 47.900781 13.400391 48.800781 C 13.100391 49.800781 12.900391 51.2 12.900391 53 L 12.900391 63.900391 L 7.0996094 63.900391 L 7.0996094 33.900391 z M 53.400391 33.900391 L 59.199219 33.900391 L 58.900391 43.599609 C 60.300391 42.399609 63.299219 41.499219 65.699219 41.699219 C 66.399219 41.799219 67 41.9 67.5 42 C 69.3 42.4 70.999219 43.199609 72.199219 44.599609 C 73.899219 46.499609 74.799609 49.199219 74.599609 52.699219 C 74.599609 56.399219 73.700391 59.300781 71.900391 61.300781 C 70.200391 63.300781 68 64.300781 65.5 64.300781 L 65.300781 64.300781 L 65 64.300781 C 62 64.300781 60.000781 63.199219 58.800781 62.199219 L 58.800781 63.900391 L 53.400391 63.900391 L 53.400391 33.900391 z M 30.300781 42.199219 L 36.099609 42.199219 L 36.099609 52.199219 C 36.099609 55.199219 36.200391 57.100781 36.400391 57.800781 C 36.700391 58.400781 37.099609 59.000391 37.599609 59.400391 C 38.099609 59.800391 38.799219 60 39.699219 60 C 40.599219 60 41.500781 59.699219 42.300781 59.199219 C 43.100781 58.699219 43.600391 57.999219 43.900391 57.199219 C 44.200391 56.399219 44.300781 54.400781 44.300781 51.300781 L 44.199219 42.199219 L 50 42.199219 L 50 64 L 44.699219 64 L 44.699219 61.599609 C 43.799219 62.699609 41.899219 64.4 38.199219 64.5 L 37.5 64.5 C 36.1 64.5 34.799609 64.1 33.599609 63.5 C 32.499609 62.9 31.599609 62.000781 31.099609 60.800781 C 30.599609 59.700781 30.300781 58 30.300781 56 L 30.300781 42.199219 z " />
<path
d="M74.6,9.9c-0.2-0.7-0.5-1.3-0.9-1.9c-0.4-0.6-1-1-1.9-1.4c-0.8-0.4-1.7-0.5-2.7-0.5c-1.8-0.1-4.7,0.6-5.6,2V6.5h-4.2v17h4.5 v-7.7c0-1.9,0.1-3.2,0.3-3.9c0.2-0.7,0.7-1.3,1.3-1.7c0.6-0.4,1.3-0.6,2.1-0.6c0.6,0,1.1,0.2,1.6,0.4c0.4,0.3,0.7,0.7,0.9,1.3 c0.2,0.5,0.3,1.7,0.3,3.6v8.7h4.5V12.9C74.8,11.6,74.7,10.6,74.6,9.9 M53.1,6.2c-1.3,0.2-2.6,1.2-3.2,1.9V6.5h-4.2v17h4.5v-5.3 c0-2.9,0.1-4.8,0.4-5.7c0.3-0.9,0.6-1.5,1-1.9c0.4-0.3,1-0.5,1.6-0.5c0.7,0,1.4,0.2,2.1,0.7l1.4-3.9c-1-0.6-1.9-0.8-3-0.8 C53.6,6.1,53.3,6.1,53.1,6.2 M34.8,6.1c-1.7,0-3.2,0.4-4.5,1.1c-1.4,0.7-2.4,1.8-3.1,3.2c-0.7,1.4-1.1,2.8-1.1,4.3 c0,2,0.4,3.6,1.1,5c0.7,1.4,1.8,2.4,3.2,3.1c1.4,0.7,2.9,1.1,4.5,1.1c2.5,0,4.6-0.8,6.3-2.5c1.7-1.7,2.5-3.8,2.5-6.4 c0-2.6-0.8-4.7-2.5-6.3C39.4,6.9,37.3,6.1,34.8,6.1 M37.7,18.9c-0.8,0.9-1.8,1.3-3,1.3c-1.2,0-2.2-0.4-3-1.3 C31,18,30.6,16.7,30.6,15s0.4-3,1.2-3.9c0.8-0.9,1.8-1.3,3-1.3c1.2,0,2.2,0.4,3,1.3c0.8,0.9,1.2,2.2,1.2,3.8 C38.9,16.7,38.5,18,37.7,18.9 M20.3,0.4C19.4,0.1,17.5,0,14.6,0H7v23.5h4.7v-8.9h3.1c2.1,0,3.8-0.1,4.9-0.3c0.8-0.2,1.7-0.6,2.5-1.1 s1.5-1.3,2-2.3c0.5-1,0.8-2.2,0.8-3.6c0-1.9-0.5-3.4-1.4-4.6C22.8,1.5,21.6,0.7,20.3,0.4 M19.6,9.1c-0.4,0.5-0.9,0.9-1.5,1.2 s-1.9,0.4-3.7,0.4h-2.6V4h2.3c1.7,0,2.8,0.1,3.4,0.2c0.8,0.1,1.4,0.5,1.9,1c0.5,0.6,0.8,1.3,0.8,2.1C20.1,8,20,8.6,19.6,9.1"
id="path3" />
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

7
assets/img/icons/regexp.svg Executable file
View File

@@ -0,0 +1,7 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="21" height="16" viewBox="0 0 21 16">
<path d="M14.964 7.134c-0.809-0.467-1.915-0.854-2.865-1.134 0.95-0.28 2.056-0.667 2.865-1.134 0.478-0.276 0.642-0.888 0.366-1.366s-0.888-0.642-1.366-0.366c-0.809 0.467-1.697 1.232-2.415 1.914 0.232-0.963 0.45-2.114 0.45-3.048 0-0.552-0.448-1-1-1s-1 0.448-1 1c0 0.934 0.218 2.086 0.45 3.048-0.717-0.683-1.606-1.447-2.415-1.914-0.478-0.276-1.090-0.112-1.366 0.366s-0.112 1.090 0.366 1.366c0.809 0.467 1.915 0.854 2.865 1.134-0.95 0.28-2.056 0.667-2.865 1.134-0.478 0.276-0.642 0.888-0.366 1.366s0.888 0.642 1.366 0.366c0.809-0.467 1.697-1.232 2.415-1.914-0.232 0.963-0.45 2.114-0.45 3.048 0 0.552 0.448 1 1 1s1-0.448 1-1c0-0.934-0.218-2.086-0.45-3.048 0.717 0.683 1.606 1.447 2.415 1.914 0.478 0.276 1.090 0.112 1.366-0.366s0.112-1.090-0.366-1.366z"></path>
<path d="M7 13.5c0 0.828-0.672 1.5-1.5 1.5s-1.5-0.672-1.5-1.5c0-0.828 0.672-1.5 1.5-1.5s1.5 0.672 1.5 1.5z"></path>
<path d="M0 16l4-16h2l-4 16z"></path>
<path d="M15 16l4-16h2l-4 16z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

4
assets/img/icons/regexp2.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15.15 0h-11.235c-0.562 0-1.106 0.442-1.21 0.983l-2.688 14.034c-0.104 0.541 0.271 0.983 0.833 0.983h11.235c0.562 0 1.106-0.442 1.21-0.983l2.688-14.034c0.104-0.541-0.271-0.983-0.833-0.983zM11.964 9.134c0.478 0.276 0.642 0.888 0.366 1.366s-0.888 0.642-1.366 0.366c-0.809-0.467-1.697-1.232-2.415-1.914 0.232 0.963 0.45 2.114 0.45 3.048 0 0.552-0.448 1-1 1s-1-0.448-1-1c0-0.934 0.218-2.085 0.45-3.048-0.717 0.683-1.606 1.447-2.415 1.914-0.478 0.276-1.090 0.112-1.366-0.366s-0.112-1.090 0.366-1.366c0.809-0.467 1.915-0.854 2.865-1.134-0.95-0.28-2.056-0.667-2.865-1.134-0.478-0.276-0.642-0.888-0.366-1.366s0.888-0.642 1.366-0.366c0.809 0.467 1.697 1.232 2.415 1.914-0.232-0.963-0.45-2.114-0.45-3.048 0-0.552 0.448-1 1-1s1 0.448 1 1c0 0.934-0.218 2.086-0.45 3.048 0.717-0.683 1.606-1.447 2.415-1.914 0.478-0.276 1.090-0.112 1.366 0.366s0.112 1.090-0.366 1.366c-0.809 0.467-1.915 0.854-2.865 1.134 0.95 0.28 2.056 0.667 2.865 1.134z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

4
assets/img/icons/scissors.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M12.218 16c-0.85-0-1.912-0.519-2.369-1.678-0.064-0.151-0.133-0.335-0.214-0.547-0.259-0.68-0.65-1.707-1.059-2.132-0.15-0.156-0.265-0.269-0.359-0.36-0.084-0.081-0.154-0.15-0.217-0.219-0.063 0.069-0.134 0.138-0.217 0.219-0.093 0.091-0.209 0.204-0.358 0.359-0.409 0.425-0.8 1.453-1.059 2.132-0.081 0.212-0.151 0.396-0.214 0.547-0.457 1.159-1.519 1.678-2.369 1.678-0.166 0-0.317-0.020-0.449-0.060-0.837-0.251-1.344-1.186-1.263-2.325l0.012-0.131c0.103-0.919 0.662-1.827 1.576-2.556 0.792-0.632 1.728-1.025 2.444-1.025 0.142 0 0.272 0.015 0.391 0.046l0.491-1.007c-0.505-1.263-1.223-3.149-1.785-4.801-0.313-0.921-0.545-1.675-0.69-2.241-0.23-0.897-0.215-1.2-0.122-1.379l0.269-0.52 3.345 6.858 3.345-6.858 0.269 0.52c0.093 0.179 0.107 0.483-0.122 1.379-0.145 0.566-0.377 1.321-0.69 2.241-0.562 1.652-1.28 3.538-1.785 4.801l0.491 1.007c0.119-0.031 0.25-0.046 0.391-0.046 0.715 0 1.652 0.393 2.444 1.025 0.781 0.624 1.304 1.378 1.503 2.158l0.037-0.018 0.048 0.548c0.081 1.139-0.426 2.073-1.263 2.325-0.132 0.040-0.283 0.060-0.449 0.060-0 0-0 0-0 0zM10.839 13.78c0.592 1.074 1.12 1.234 1.383 1.234 0.358 0 0.626-0.289 0.678-0.568 0.034-0.183 0.040-0.372 0.018-0.561-0.089-0.755-0.604-1.403-1.021-1.814-0.573-0.565-1.215-0.939-1.628-1.103-0.041-0.014-0.066-0.016-0.078-0.016-0.008 0-0.011 0.001-0.011 0.001-0.12 0.078-0.264 0.985 0.658 2.827zM5.723 10.968c-0.41 0.164-1.048 0.539-1.618 1.103-0.414 0.411-0.927 1.059-1.015 1.814-0.022 0.188-0.016 0.377 0.018 0.561 0.052 0.279 0.318 0.568 0.674 0.568 0.261 0 0.787-0.161 1.375-1.234 0.916-1.842 0.774-2.748 0.653-2.828 0 0-0 0-0 0s-0.004-0.001-0.010-0.001c-0.011-0-0.037 0.002-0.077 0.016z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

4
assets/img/icons/scissors2.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M4.655 0l-0.269 0.52c-0.093 0.179-0.107 0.483 0.122 1.379 0.145 0.566 0.377 1.321 0.69 2.241 0.395 1.163 0.868 2.441 1.29 3.536l0.955-1.959-2.789-5.718zM13.93 13.616l-0.048-0.548-0.037 0.018c-0.199-0.78-0.722-1.534-1.503-2.158-0.792-0.633-1.729-1.025-2.444-1.025-0.142 0-0.272 0.015-0.391 0.046l-0.491-1.007c0.505-1.263 1.224-3.149 1.785-4.801 0.313-0.921 0.545-1.675 0.69-2.241 0.23-0.897 0.215-1.2 0.122-1.379l-0.269-0.52-4.852 9.948c-0.119-0.031-0.25-0.046-0.391-0.046-0.715 0-1.652 0.393-2.444 1.025-0.914 0.73-1.473 1.638-1.576 2.556l-0.012 0.131c-0.081 1.139 0.426 2.073 1.263 2.325 0.132 0.040 0.283 0.060 0.449 0.060 0.85 0 1.912-0.519 2.369-1.678 0.064-0.151 0.133-0.335 0.214-0.547 0.259-0.68 0.65-1.707 1.059-2.132 0.15-0.156 0.265-0.269 0.358-0.359 0.084-0.082 0.154-0.15 0.217-0.219 0.063 0.069 0.134 0.138 0.217 0.219 0.093 0.091 0.209 0.204 0.359 0.36 0.409 0.425 0.8 1.453 1.059 2.132 0.081 0.212 0.151 0.396 0.214 0.547 0.457 1.159 1.519 1.678 2.369 1.678 0 0 0 0 0 0 0.166 0 0.317-0.020 0.449-0.060 0.837-0.252 1.344-1.186 1.263-2.325zM5.157 13.78c-0.588 1.073-1.114 1.234-1.375 1.234-0.356 0-0.622-0.289-0.674-0.568-0.034-0.184-0.040-0.373-0.018-0.561 0.088-0.755 0.6-1.403 1.015-1.814 0.569-0.565 1.208-0.939 1.618-1.103 0.041-0.014 0.066-0.016 0.077-0.016 0.006 0 0.009 0.001 0.010 0.001s0 0 0-0c0.121 0.080 0.263 0.986-0.653 2.828zM12.9 14.446c-0.052 0.279-0.32 0.568-0.678 0.568-0.263 0-0.791-0.161-1.383-1.234-0.922-1.842-0.778-2.749-0.658-2.827 0 0 0.003-0.001 0.011-0.001 0.011 0 0.037 0.002 0.078 0.016 0.412 0.164 1.055 0.539 1.628 1.103 0.417 0.411 0.933 1.059 1.021 1.814 0.022 0.189 0.016 0.378-0.018 0.561z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M13.937 8.034c-0.283 0-0.552 0.055-0.798 0.154-0.164-1.787-1.723-3.188-3.625-3.188-0.465 0-0.917 0.088-1.317 0.237-0.156 0.058-0.197 0.117-0.197 0.233v6.292c0 0.121 0.098 0.222 0.221 0.234 0.005 0.001 5.68 0.003 5.717 0.003 1.139 0 2.062-0.888 2.062-1.983s-0.924-1.983-2.063-1.983zM6.25 12h0.5l0.25-3.503-0.25-3.497h-0.5l-0.25 3.497zM4.75 12h-0.5l-0.25-2.543 0.25-2.457h0.5l0.25 2.5zM2.25 12h0.5l0.25-2-0.25-2h-0.5l-0.25 2zM0.25 11h0.5l0.25-1-0.25-1h-0.5l-0.25 1z"></path>
</svg>

After

Width:  |  Height:  |  Size: 620 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M14.5 0h-13c-0.825 0-1.5 0.675-1.5 1.5v13c0 0.825 0.675 1.5 1.5 1.5h13c0.825 0 1.5-0.675 1.5-1.5v-13c0-0.825-0.675-1.5-1.5-1.5zM2.75 11h-0.5l-0.25-1.5 0.25-1.5h0.5l0.25 1.5-0.25 1.5zM4.75 11h-0.5l-0.25-2 0.25-2h0.5l0.25 2-0.25 2zM6.75 11h-0.5l-0.25-3 0.25-3h0.5l0.25 3-0.25 3zM12.894 11c-0.031 0-4.706-0.003-4.709-0.003-0.1-0.009-0.181-0.097-0.184-0.2v-5.394c0-0.1 0.034-0.15 0.162-0.2 0.331-0.128 0.703-0.203 1.088-0.203 1.566 0 2.85 1.2 2.987 2.734 0.203-0.084 0.425-0.131 0.656-0.131 0.938 0 1.7 0.762 1.7 1.7s-0.762 1.697-1.7 1.697z"></path>
</svg>

After

Width:  |  Height:  |  Size: 693 B

4
assets/img/icons/spam.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M16 11.5l-4.5-11.5h-7l-4.5 4.5v7l4.5 4.5h7l4.5-4.5v-7l-4.5-4.5zM9 13h-2v-2h2v2zM9 9h-2v-6h2v6z"></path>
</svg>

After

Width:  |  Height:  |  Size: 251 B

View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M5 1v1.155l-2.619 0.368 0.17 1.211-2.551 0.732 3.308 11.535 10.189-2.921 0.558-0.079h1.945v-12h-11zM3.929 14.879l-2.808-9.793 1.558-0.447 1.373 9.766 2.997-0.421-3.119 0.894zM4.822 13.382l-1.418-10.088 1.595-0.224v9.93h2.543l-2.721 0.382zM15 12h-9v-10h9v10zM13 8.939v1.061h-1.061l-1.439-1.439-1.439 1.439h-1.061v-1.061l1.439-1.439-1.439-1.439v-1.061h1.061l1.439 1.439 1.439-1.439h1.061v1.061l-1.439 1.439z"></path>
</svg>

After

Width:  |  Height:  |  Size: 562 B

4
assets/img/icons/telegram.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M8 0c-4.419 0-8 3.581-8 8s3.581 8 8 8 8-3.581 8-8-3.581-8-8-8zM11.931 5.484l-1.313 6.184c-0.091 0.441-0.356 0.544-0.725 0.341l-2-1.478-0.959 0.934c-0.112 0.109-0.2 0.2-0.4 0.2-0.259 0-0.216-0.097-0.303-0.344l-0.681-2.237-1.978-0.616c-0.428-0.131-0.431-0.425 0.097-0.634l7.706-2.975c0.35-0.159 0.691 0.084 0.556 0.625z"></path>
</svg>

After

Width:  |  Height:  |  Size: 474 B

6
assets/img/icons/twitch.svg Executable file
View File

@@ -0,0 +1,6 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M1.5 0l-1.5 2.5v11.5h4v2h2l2-2h2.5l4.5-4.5v-9.5h-13.5zM13 8.5l-2.5 2.5h-2.5l-2 2v-2h-3v-9h10v6.5z"></path>
<path d="M9.5 4h1.5v4h-1.5v-4z"></path>
<path d="M6.5 4h1.5v4h-1.5v-4z"></path>
</svg>

After

Width:  |  Height:  |  Size: 334 B

3
assets/img/icons/twitter-x.svg Executable file
View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-twitter-x" viewBox="0 0 16 16">
<path d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z"/>
</svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M6 11.5c0-2.363 1.498-4.383 3.594-5.159 0.254-0.571 0.406-1.206 0.406-1.841 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h6.208c-0.135-0.477-0.208-0.98-0.208-1.5z"></path>
<path d="M11.5 7c-2.485 0-4.5 2.015-4.5 4.5s2.015 4.5 4.5 4.5c2.485 0 4.5-2.015 4.5-4.5s-2.015-4.5-4.5-4.5zM8 11.5c0-1.933 1.567-3.5 3.5-3.5 0.763 0 1.47 0.245 2.045 0.659l-4.885 4.886c-0.415-0.575-0.66-1.282-0.66-2.045zM11.5 15c-0.763 0-1.47-0.245-2.045-0.659l4.886-4.886c0.415 0.575 0.659 1.282 0.659 2.045 0 1.933-1.567 3.5-3.5 3.5z"></path>
</svg>

After

Width:  |  Height:  |  Size: 716 B

View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M6 11.5c0-2.363 1.498-4.383 3.594-5.159 0.254-0.571 0.406-1.206 0.406-1.841 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h6.208c-0.135-0.477-0.208-0.98-0.208-1.5z"></path>
<path d="M11.5 7c-2.485 0-4.5 2.015-4.5 4.5s2.015 4.5 4.5 4.5c2.485 0 4.5-2.015 4.5-4.5s-2.015-4.5-4.5-4.5zM13.898 13.102c0.22 0.22 0.22 0.576 0 0.795-0.11 0.11-0.254 0.165-0.398 0.165s-0.288-0.055-0.398-0.165l-1.602-1.602-1.602 1.602c-0.11 0.11-0.254 0.165-0.398 0.165s-0.288-0.055-0.398-0.165c-0.22-0.22-0.22-0.576 0-0.795l1.602-1.602-1.602-1.602c-0.22-0.22-0.22-0.576 0-0.795s0.576-0.22 0.795 0l1.602 1.602 1.602-1.602c0.22-0.22 0.576-0.22 0.795 0s0.22 0.576 0 0.795l-1.602 1.602 1.602 1.602z"></path>
</svg>

After

Width:  |  Height:  |  Size: 876 B

View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15 9.5l-4.5 4.5-1.5-1.5-1 1 2.5 2.5 5.5-5.5z"></path>
<path d="M7 12h5v-1.799c-1.050-0.613-2.442-1.033-4-1.16v-0.825c1.102-0.621 2-2.168 2-3.716 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h7v-1z"></path>
</svg>

After

Width:  |  Height:  |  Size: 406 B

5
assets/img/icons/user-lock.svg Executable file
View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M15.25 11h-0.25v-1c0-1.103-0.897-2-2-2s-2 0.897-2 2v1h-0.25c-0.412 0-0.75 0.338-0.75 0.75v3.5c0 0.412 0.338 0.75 0.75 0.75h4.5c0.412 0 0.75-0.338 0.75-0.75v-3.5c0-0.412-0.338-0.75-0.75-0.75zM12 10c0-0.551 0.449-1 1-1s1 0.449 1 1v1h-2v-1z"></path>
<path d="M9 9.166c-0.324-0.055-0.658-0.097-1-0.125v-0.825c1.102-0.621 2-2.168 2-3.716 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h9v-3.834z"></path>
</svg>

After

Width:  |  Height:  |  Size: 597 B

View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M6 11.5c0-2.363 1.498-4.383 3.594-5.159 0.254-0.571 0.406-1.206 0.406-1.841 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h6.208c-0.135-0.477-0.208-0.98-0.208-1.5z"></path>
<path d="M11.5 7c-2.485 0-4.5 2.015-4.5 4.5s2.015 4.5 4.5 4.5c2.485 0 4.5-2.015 4.5-4.5s-2.015-4.5-4.5-4.5zM14 12h-5v-1h5v1z"></path>
</svg>

After

Width:  |  Height:  |  Size: 505 B

5
assets/img/icons/user-plus.svg Executable file
View File

@@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M6 11.5c0-2.363 1.498-4.383 3.594-5.159 0.254-0.571 0.406-1.206 0.406-1.841 0-2.485 0-4.5-3-4.5s-3 2.015-3 4.5c0 1.548 0.898 3.095 2 3.716v0.825c-3.392 0.277-6 1.944-6 3.959h6.208c-0.135-0.477-0.208-0.98-0.208-1.5z"></path>
<path d="M11.5 7c-2.485 0-4.5 2.015-4.5 4.5s2.015 4.5 4.5 4.5c2.485 0 4.5-2.015 4.5-4.5s-2.015-4.5-4.5-4.5zM14 12h-2v2h-1v-2h-2v-1h2v-2h1v2h2v1z"></path>
</svg>

After

Width:  |  Height:  |  Size: 525 B

4
assets/img/icons/whatsapp.svg Executable file
View File

@@ -0,0 +1,4 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M13.641 2.325c-1.497-1.5-3.488-2.325-5.609-2.325-4.369 0-7.925 3.556-7.925 7.928 0 1.397 0.366 2.763 1.059 3.963l-1.125 4.109 4.203-1.103c1.159 0.631 2.463 0.966 3.787 0.966h0.003c0 0 0 0 0 0 4.369 0 7.928-3.556 7.928-7.928 0-2.119-0.825-4.109-2.322-5.609zM8.034 14.525v0c-1.184 0-2.344-0.319-3.356-0.919l-0.241-0.144-2.494 0.653 0.666-2.431-0.156-0.25c-0.663-1.047-1.009-2.259-1.009-3.506 0-3.634 2.956-6.591 6.594-6.591 1.759 0 3.416 0.688 4.659 1.931 1.244 1.247 1.928 2.9 1.928 4.662-0.003 3.637-2.959 6.594-6.591 6.594zM11.647 9.588c-0.197-0.1-1.172-0.578-1.353-0.644s-0.313-0.1-0.447 0.1c-0.131 0.197-0.512 0.644-0.628 0.778-0.116 0.131-0.231 0.15-0.428 0.050s-0.838-0.309-1.594-0.984c-0.588-0.525-0.987-1.175-1.103-1.372s-0.013-0.306 0.088-0.403c0.091-0.088 0.197-0.231 0.297-0.347s0.131-0.197 0.197-0.331c0.066-0.131 0.034-0.247-0.016-0.347s-0.447-1.075-0.609-1.472c-0.159-0.388-0.325-0.334-0.447-0.341-0.116-0.006-0.247-0.006-0.378-0.006s-0.347 0.050-0.528 0.247c-0.181 0.197-0.694 0.678-0.694 1.653s0.709 1.916 0.809 2.050c0.1 0.131 1.397 2.134 3.384 2.991 0.472 0.203 0.841 0.325 1.128 0.419 0.475 0.15 0.906 0.128 1.247 0.078 0.381-0.056 1.172-0.478 1.338-0.941s0.166-0.859 0.116-0.941c-0.047-0.088-0.178-0.137-0.378-0.238z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

14
assets/img/icons/x.svg Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="300.25"
height="300.25"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<path
d="M 178.695,127.15 290.395,0 h -26.46 L 166.905,110.38 89.465,0 H 0.125 L 117.255,166.93 0.125,300.25 h 26.46 l 102.4,-116.59 81.8,116.59 h 89.34 M 36.135,19.54 h 40.65 l 187.13,262.13 h -40.66"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 470 B

4
assets/markdown.yaml Normal file
View File

@@ -0,0 +1,4 @@
- wrap: ['[Scene details](', ')']
items:
- link
- text: 'on [traxxx](https://traxxx.me/).'

45
assets/mockup-release.ts Normal file
View File

@@ -0,0 +1,45 @@
const now = new Date();
export default {
id: 0,
shootId: 12345,
title: 'Nut For Human Consumption',
slug: 'nut-for-human-consumption',
link: 'https://traxxx.me/scene/0/nut-for-human-consumption',
url: 'https://example.com/video/12345/nut-for-human-consumption',
date: now,
effectiveDate: now,
createdAt: new Date(now.getFullYear(), 0, 1),
actors: [
{
name: 'Chanel Chakra',
gender: 'female',
ageThen: 26,
ageFromBirth: 31,
dateOfBirth: new Date(1999, 2, 2),
},
{
name: 'Mo The Fucker',
gender: 'male',
ageThen: 32,
ageFromBirth: 37,
dateOfBirth: new Date(1988, 5, 12),
},
],
tags: [
{ name: 'anal' },
{ name: 'facefucking' },
{ name: 'deepthroat' },
{ name: 'blowjob' },
{ name: 'facial' },
],
movies: [{
title: `Best Of Traxxx ${String(now.getFullYear()).slice(2)}`,
}],
channel: {
name: 'Traxxxed',
},
network: {
name: 'Traxxx',
},
};

125
assets/sfw.ejs Normal file
View File

@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16.png">
<link rel="manifest" href="/img/favicon/site.webmanifest">
<link rel="mask-icon" href="/img/favicon/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="/img/favicon/favicon.ico">
<meta name="msapplication-TileColor" content="#b91d47">
<meta name="msapplication-config" content="/img/favicon/browserconfig.xml">
<meta name="theme-color" content="#f65596">
<meta property="og:title" content="traxxx" />
<meta property="og:image" content="https://traxxx.me/img/og_logo.png" />
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0,interactive-widget=resizes-content" />
<title>traxxx - None shall pass</title>
<style>
:root {
--primary-dark-10: #e54485;
--primary: #f65596;
--primary-light-10: #f075a6;
--primary-light-20: #f2a6c4;
--primary-light-30: #f7c9dc;
}
html,
body {
height: 100%;
margin: 0;
}
.content {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-sizing: border-box;
padding: 1rem;
}
.explainer {
margin-bottom: 3rem;
font-size: 1.25rem;
text-align: justify;
line-height: 1.5;
}
.useful {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.links {
display: flex;
gap: 1rem;
list-style: none;
padding: 0;
margin: 1rem 0 0 0;
}
.links li {
padding: .25rem 0;
margin: 0;
font-size: 1.25rem;
}
.links a {
padding: .5rem 1rem;
border-radius: .5rem;
background: var(--primary);
color: white;
text-decoration: none;
white-space: nowrap;
flex-shrink: 0;
}
.links a:hover {
background: var(--primary-dark-10);
}
</style>
</head>
<body>
<div class="content">
<h2 class="heading">Not so fast, rascal.</h2>
<p class="explainer">The content offered by traxxx is restricted in your jurisdiction.</p>
<% if (!noVpn) { %>
<div class="useful">
Useful links:
<ul class="links">
<li>
<a
href="https://mullvad.net/"
target="_blank"
rel="noopener"
class="link"
>Mullvad VPN</a>
</li>
<li>
<a
href="https://protonvpn.com/"
target="_blank"
rel="noopener"
class="link"
>Proton VPN</a>
</li>
</ul>
</div>
<% } %>
</div>
</body>
</html>

View File

@@ -1,8 +1,15 @@
- ' - ':
- delimit: ' - '
items:
- channel
- - movie
- scene|Scene $
- items:
- movie
- scene
- title
- ', |(|)':
- actors
- date|yyyy-MM-dd
- delimit: ', '
wrap: ['(', ')']
items:
- key: actors
genders: fmtou
- key: date
format: yyyy-MM-dd
fallbackToAdded: false

1
common Submodule

Submodule common added at ec4b15ce33

View File

@@ -8,7 +8,7 @@
type="search"
placeholder="Search actors"
class="input search"
@keydown.enter="search"
@search="search"
>
<Icon
@@ -265,6 +265,7 @@ function updateFilter(prop, value, reload = true) {
.page {
min-height: 100%;
display: flex;
flex-grow: 1;
align-items: stretch;
background: var(--background-base-10);
}
@@ -280,7 +281,7 @@ function updateFilter(prop, value, reload = true) {
.actors-header {
display: flex;
align-items: center;
padding: .5rem 0 .25rem 2.25rem;
padding: .5rem 0 .5rem 3rem;
margin-bottom: .25rem;
}

View File

@@ -8,10 +8,15 @@
class="avatar-container"
>
<img
:src="getMediaPath(actor.avatar, 'thumbnail')"
:src="getPath(actor.avatar, 'thumbnail')"
:title="actor.avatar.credit && `© ${actor.avatar.credit}`"
class="avatar"
>
<span
v-if="actor.avatar?.credit"
class="avatar-credit"
>{{ actor.avatar.credit }}</span>
</div>
<ul class="bio nolist">
@@ -22,11 +27,11 @@
<dfn class="bio-label"><Icon icon="cake" />Date of birth</dfn>
<span class="birthdate">
<span class="birthdate-long">{{ formatDate(actor.dateOfBirth, 'MMMM d, yyyy') }}</span>
<span class="birthdate-short">{{ formatDate(actor.dateOfBirth, 'MMM d, yyyy') }}</span>
<span class="birthdate-long">{{ formatDate(actor.dateOfBirth, actor.dateOfBirth.getFullYear() > 1 ? 'MMMM d, yyyy' : 'MMMM d') }}</span>
<span class="birthdate-short">{{ formatDate(actor.dateOfBirth, actor.dateOfBirth.getFullYear() > 1 ? 'MMM d, yyyy' : 'MMM d') }}</span>
<span
v-if="!actor.dateOfDeath"
v-if="!actor.dateOfDeath && actor.dateOfBirth.getFullYear() > 1"
class="age"
>{{ actor.ageFromBirth }}</span>
</span>
@@ -97,7 +102,8 @@
<li
v-if="actor.residence"
class="bio-item residence hideable"
class="bio-item residence"
:class="{ hideable: !!actor.origin }"
>
<dfn class="bio-label"><Icon icon="location" />Lives in</dfn>
@@ -137,12 +143,59 @@
>
<dfn class="bio-label"><Icon icon="ruler" />Figure</dfn>
<span class="bio-value">
{{ actor.bust || '??' }}{{ actor.cup || '?' }}-{{ actor.waist || '??' }}-{{ actor.hip || '??' }}
<Icon
v-if="actor.naturalBoobs === false"
:title="'Enhanced boobs'"
v-if="actor.naturalBoobs === false || actor.naturalButt === false"
v-tooltip="[actor.naturalBoobs === false ? 'Enhanced boobs' : null, actor.naturalButt === false ? 'Enhanced butt' : null].filter(Boolean).join(', ')"
icon="magic-wand2"
class="enhanced"
/>{{ actor.bust || '??' }}{{ actor.cup || '?' }}-{{ actor.waist || '??' }}-{{ actor.hip || '??' }}
/>
</span>
</li>
<li
v-if="actor.naturalLips === false || actor.naturalLabia === false
|| (actor.naturalBoobs === false && !actor.bust) || actor.boobsVolume || actor.boobsPlacement || actor.boobsIncision || actor.boobsImplant
|| actor.buttVolume || actor.buttImplant"
class="bio-item augmentations hideable"
>
<dfn class="bio-label"><Icon icon="magic-wand2" />Enhancements</dfn>
<span class="bio-value">
<div
v-if="actor.naturalBoobs === false"
:title="[
actor.boobsVolume && `${actor.boobsVolume}cc`,
augmentationMap[actor.boobsPlacement] || actor.boobsPlacement,
augmentationMap[actor.boobsIncision] || actor.boobsIncision,
augmentationMap[actor.boobsImplant] || actor.boobsImplant
].filter(Boolean).join(' ')"
class="augmentations-section"
>Boobs<template v-if="actor.boobsVolume || actor.boobsImplant">:&nbsp;</template>
<template v-if="actor.boobsVolume">{{ actor.boobsVolume }}cc</template>
<template v-if="actor.boobsImplant">&nbsp;{{ augmentationMap[actor.boobsImplant] || actor.boobsImplant }}</template>
</div>
<div
v-if="actor.naturalButt === false"
class="augmentations-section"
>Butt<template v-if="actor.buttVolume || actor.buttImplant">:&nbsp;</template>
<template v-if="actor.buttVolume">{{ actor.buttVolume }}cc</template>
<template v-if="actor.buttImplant">&nbsp;{{ augmentationMap[actor.buttImplant] || actor.buttImplant }}</template>
</div>
<div
v-if="actor.naturalLips === false"
class="augmentations-section"
>Lip filler<template v-if="actor.lipsVolume || actor.lipsImplant">:&nbsp;</template>
<template v-if="actor.lipsVolume">{{ actor.lipsVolume }}cc</template>
<template v-if="actor.lipsImplant">&nbsp;{{ augmentationMap[actor.lipsImplant] || actor.lipsImplant }}</template>
</div>
<div
v-if="actor.naturalLabia === false"
class="augmentations-section"
>Labiaplasty</div>
</span>
</li>
@@ -154,20 +207,20 @@
<span>
<Icon
v-if="actor.circumcised"
v-if="actor.isCircumcised"
:title="'Circumcised'"
icon="page-break"
icon="pagebreak"
class="circumcised"
/>
<template v-if="actor.penisLengthMetric && actor.penisGirthMetric">
<span>{{ actor.penisLengthMetric }} * {{ actor.penisGirthMetric }} cm</span>
<span class="bio-segment">{{ actor.penisLengthImperial }}" * {{ actor.penisGirthImperial }}"</span>
<template v-if="actor.penisLength && actor.penisGirth">
<span>{{ actor.penisLength.imperial }}" × {{ actor.penisLength.imperial }}"</span>
<span class="bio-segment">{{ actor.penisLength.metric }} × {{ actor.penisGirth.metric }} cm</span>
</template>
<template v-else-if="actor.penisLengthMetric">
<span>{{ actor.penisLengthMetric }} cm</span>
<span class="bio-segment">{{ actor.penisLengthImperial }}"</span>
<template v-else-if="actor.penisLength">
<span>{{ actor.penisLength.imperial }}"</span>
<span class="bio-segment">{{ actor.penisLength.metric }} cm</span>
</template>
</span>
</li>
@@ -239,7 +292,79 @@
<span v-else>Yes</span>
</li>
<li class="bio-item updated hideable">Updated {{ formatDate(actor.updatedAt, 'yyyy-MM-dd hh:mm') }}, ID: {{ actor.id }}</li>
<li
v-if="actor.agency"
class="bio-item hideable"
>
<dfn class="bio-label"><Icon icon="user-tie" />Agency</dfn>
<span
:title="actor.agency"
class="bio-value"
>{{ actor.agency }}</span>
</li>
<div
v-if="socials.length > 0"
class="bio-item bio-socials hideable"
>
<ul class="socials">
<a
v-for="social in socials"
:key="`social-${social.id}`"
:href="getSocialUrl(social)"
target="_blank"
rel="noopener"
:title="social.platform || social.url"
class="social ellipsis"
>
<Icon
v-if="social.platform && env.socials.urls[social.platform]"
:icon="iconMap[social.platform] || social.platform"
:title="social.platform"
:class="`icon-social icon-${social.platform}`"
/>
<Icon
v-else-if="social.platform"
icon="bubbles10"
:title="social.platform"
:class="`icon-social icon-${social.platform} icon-generic`"
/>
<Icon
v-else-if="social.url"
icon="sphere"
:title="social.platform"
:class="`icon-social icon-${social.platform} icon-generic`"
/>
<template v-if="social.platform">{{ env.socials.prefix[social.platform] || env.socials.prefix.default }}</template>{{ social.handle }}
</a>
</ul>
</div>
<li class="bio-item updated">
<span
class="ellipsis"
:title="formatDate(actor.updatedAt, 'yyyy-MM-dd hh:mm')"
>{{ formatDate(actor.updatedAt, 'yyyy-MM-dd') }}</span>
<div class="actor-actions">
<a
v-if="user && user.role !== 'user'"
:href="`/actor/edit/${actor.id}/${actor.slug}`"
target="_blank"
class="link"
>Edit bio</a>
<a
:href="`/actor/revs/${actor.id}/${actor.slug}`"
target="_blank"
class="link"
>Revisions</a>
</div>
</li>
</ul>
<div class="descriptions-container">
@@ -258,12 +383,14 @@
v-if="description.entity.type === 'network' || !description.entity.parent || description.entity.isIndependent"
:src="`/logos/${description.entity.slug}/thumbs/network.png`"
class="description-logo"
loading="lazy"
>
<img
v-else
:src="`/logos/${description.entity.parent.slug}/thumbs/${description.entity.slug}.png`"
class="description-logo"
loading="lazy"
>
</a>
</p>
@@ -294,13 +421,17 @@
</template>
<script setup>
import { ref } from 'vue';
import { ref, inject } from 'vue';
import formatTemplate from 'template-format';
import { getMediaPath } from '#/utils/media-path.js';
import getPath from '#/src/get-path.js';
import { formatDate } from '#/utils/format.js';
const expanded = ref(false);
const pageContext = inject('pageContext');
const { user, env } = pageContext;
const props = defineProps({
actor: {
type: Object,
@@ -308,12 +439,17 @@ const props = defineProps({
},
});
const iconMap = {
twitter: 'twitter-x',
};
// if the profile is empty, the expand button overlaps the header
const showExpand = [
'age',
'bust',
'cup',
'eyes',
'ethnicity',
'hairColor',
'hasPiercings',
'hasTattoos',
@@ -326,12 +462,48 @@ const showExpand = [
'weight',
].some((attribute) => !!props.actor[attribute]);
const augmentationMap = {
bbl: 'BBL',
fat: 'fat transfer',
lift: 'direct lift',
lipo: 'lipo without BBL',
filler: 'filler',
mms: 'MMS',
over: 'over-muscle',
under: 'under-muscle',
mammary: 'under breast',
areolar: 'areolar',
crescent: 'crescent areolar',
lollipop: 'lollipop',
axillary: 'armpit',
umbilical: 'navel',
};
const descriptions = Object.values(Object.fromEntries(props.actor.profiles
.filter((profile) => !!profile.description)
.map((profile) => [profile.descriptionHash, {
text: profile.description,
entity: profile.entity,
}])));
function getSocialUrl(social) {
if (social.url) {
return social.url;
}
if (pageContext.env.socials.urls[social.platform]) {
return formatTemplate(pageContext.env.socials.urls[social.platform], { handle: social.handle });
}
return null;
}
const socials = props.actor.socials.map((social) => ({
...social,
handle: social.url
? new URL(social.url).hostname
: social.handle,
}));
</script>
<style>
@@ -344,7 +516,7 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
.icon {
width: 1.25rem;
height: 1.25rem;
height: auto;
}
}
</style>
@@ -365,17 +537,32 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
}
.avatar-container {
padding: 0 0 1rem 1rem;
position: relative;
margin: 0 .5rem 1rem 1rem;
flex-shrink: 0;
font-size: 0;
}
.avatar {
height: 100%;
flex-shrink: 0;
border: solid 3px var(--highlight-hint);
margin: 0 .5rem 0 0;
border: solid 3px var(--highlight-weak-30);
border-radius: .5rem;
}
.avatar-credit {
width: 100%;
position: absolute;
left: 0;
bottom: 0;
z-index: 1;
box-sizing: border-box;
padding: 0 .5rem;
color: var(--text-light);
font-size: .75rem;
text-shadow: 1px 1px 0 var(--shadow-strong-20);
}
&.expanded {
padding-bottom: 1.5rem;
margin-bottom: .75rem;
@@ -402,7 +589,6 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
}
.bio-item {
width: calc(50% - 4rem);
display: flex;
justify-content: space-between;
box-sizing: border-box;
@@ -417,6 +603,10 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
&:not(:last-of-type) {
border-bottom: solid 1px var(--highlight-weak-40);
}
.icon {
height: auto; /* prevents jumping */
}
}
.bio-label,
@@ -439,7 +629,9 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
}
.bio-value {
display: inline-block;
margin: 0 0 0 2rem;
max-width: 20rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
@@ -452,6 +644,7 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
.flag {
height: 1rem;
margin: .25rem .5rem 0 0;
border-radius: 3px;
}
.bio-name {
@@ -494,7 +687,7 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
.penis-length-imperial,
.bio-segment {
padding: 0 0 0 .5rem;
border-left: solid 1px var(--highlight-weak);
border-left: solid 1px var(--highlight-weak-20);
margin: 0 0 0 .5rem;
}
@@ -502,6 +695,8 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
.circumcised.icon {
fill: var(--primary);
padding: 0 .5rem;
margin-right: .25rem;
transform: translateY(2px);
}
.enhanced.icon {
@@ -519,9 +714,76 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
content: ',\00a0';
}
.augmentations .bio-value {
flex-direction: column;
align-items: flex-end;
}
.updated {
color: var(--highlight-weak-20);
color: var(--highlight-weak-10);
font-size: .8rem;
text-align: left;
.ellipsis {
width: 0;
flex-grow: 1;
}
}
.bio-socials {
display: block;
}
.socials {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
grid-gap: 0 0;
overflow: hidden;
gap: .25rem;
padding: 0;
}
.social {
display: flex;
height: 2rem;
align-items: center;
padding: .1rem .5rem;
border-radius: .25rem;
color: inherit;
text-decoration: none;
font-size: .9rem;
font-weight: normal;
background: var(--highlight-weak-40);
.icon {
margin-right: .5rem;
}
.icon-generic {
fill: var(--highlight);
}
&:hover {
color: var(--primary);
cursor: pointer;
.icon {
fill: var(--primary);
}
}
}
.actor-actions {
display: flex;
flex-shrink: 0;
justify-content: flex-start;
gap: 1rem;
margin-right: .5rem;
.link {
color: inherit;
flex-shrink: 0;
}
}
.descriptions-container {
@@ -592,8 +854,8 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
display: none;
justify-content: center;
position: absolute;
z-index: 1;
bottom: -.75rem;
z-index: 10;
bottom: -.25rem;
}
.expand {
@@ -642,6 +904,10 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
.descriptions-container {
display: none;
}
.bio {
margin-right: 1rem;
}
}
@media(--compact) {
@@ -661,7 +927,7 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
}
}
@media(--small-15) {
@media(--small) {
.profile {
height: auto;
max-height: none;
@@ -674,6 +940,11 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
&:not(.expanded) .hideable {
display: none;
}
/* only hide update/actions line if other bio lines and thus the expand button are present */
&:not(.expanded) .bio-item + .updated {
display: none;
}
}
@@ -689,6 +960,10 @@ const descriptions = Object.values(Object.fromEntries(props.actor.profiles
margin: 0;
}
.bio-value {
max-width: initial;
}
.expanded .bio-value {
white-space: normal;
}

View File

@@ -0,0 +1,101 @@
<template>
<VDropdown
:disabled="disabled"
class="trigger"
@show="focus"
>
<slot />
<template #popper>
<div>
<input
ref="queryInput"
v-model="query"
placeholder="Search actor"
class="input"
@input="search"
>
<ul class="actors nolist">
<li
v-for="actor in actors"
:key="`actor-${actor.slug}`"
v-close-popper
class="actor"
@click="emit('actor', actor); query = '';"
>{{ actor.name }} ({{ [actor.ageFromBirth, actor.origin?.country?.alpha2].filter(Boolean).join(', ') }})
<img
v-if="actor.avatar"
:src="getPath(actor.avatar, 'thumbnail')"
class="avatar"
>
</li>
</ul>
</div>
</template>
</VDropdown>
</template>
<script setup>
import { ref } from 'vue';
import { get } from '#/src/api.js';
import getPath from '#/src/get-path.js';
const actors = ref([]);
const query = ref(null);
const queryInput = ref(null);
defineProps({
disabled: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['actor']);
async function search() {
const data = await get('/actors', { q: query.value });
actors.value = data.actors;
}
function focus() {
setTimeout(() => {
queryInput.value?.focus();
}, 100);
}
</script>
<style scoped>
.trigger {
height: 100%;
overflow: hidden;
}
.actor {
display: block;
padding: .25rem .5rem;
&:hover {
background: var(--glass-weak-50);
color: var(--primary);
cursor: pointer;
.avatar {
display: block;
}
}
}
.avatar {
position: fixed;
display: none;
left: 7rem;
width: 8rem;
border-radius: .25rem;
box-shadow: 0 0 3px var(--shadow-weak-10);
pointer-events: none;
}
</style>

View File

@@ -11,7 +11,7 @@
class="avatar-link no-link"
>
<img
v-if="actor.avatar"
v-if="actor.avatar && !restriction"
:src="getPath(actor.avatar, 'thumbnail')"
:style="{ 'background-image': `url(${getPath(actor.avatar, 'lazy')})` }"
loading="lazy"
@@ -40,13 +40,20 @@
<Gender :gender="actor.gender" />
<span
v-if="actor.ageFromBirth"
v-if="actor.ageAtDeath"
:title="`Passed ${formatDate(actor.ageAtDeath, 'MMMM d, yyyy')}`"
class="age age-death"
>{{ actor.ageAtDeath }}</span>
<span
v-else-if="actor.ageFromBirth"
:title="`Born ${formatDate(actor.dateOfBirth, 'MMMM d, yyyy')}`"
class="age"
>{{ actor.ageFromBirth }}</span>
<!-- don't show 'age then' for scenes released after the performer's death, because it might show an age they sadly never reached -->
<span
v-if="actor.ageThen && actor.ageThen < actor.ageFromBirth"
v-if="actor.ageThen && actor.ageThen < actor.ageFromBirth && (!actor.ageAtDeath || actor.ageThen < actor.ageAtDeath)"
title="Age on date of release"
class="age age-then"
>{{ actor.ageThen }}</span>
@@ -66,7 +73,16 @@
</div>
</div>
<span class="name">{{ actor.name }}</span>
<span class="label">
<span class="name ellipsis">{{ actor.name }}</span>
<img
v-if="actor.entity"
v-tooltip="actor.entity.name"
:src="`/logos/${actor.entity.slug}/favicon_dark.png`"
class="favicon"
>
</span>
</div>
</template>
@@ -87,11 +103,11 @@ const props = defineProps({
});
const pageContext = inject('pageContext');
const { user } = pageContext;
const { user, restriction } = pageContext;
const pageStash = pageContext.pageProps.stash;
const currentStash = pageStash || user?.primaryStash;
const currentStash = pageStash || pageContext.assets?.primaryStash;
const favorited = ref(props.actor.stashes.some((actorStash) => actorStash.id === currentStash.id));
const favorited = ref(props.actor.stashes.some((actorStash) => actorStash.id === currentStash?.id));
</script>
<style scoped>
@@ -119,9 +135,11 @@ const favorited = ref(props.actor.stashes.some((actorStash) => actorStash.id ===
}
}
.name {
.label {
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
padding: .35rem .5rem;
font-weight: bold;
font-size: .9rem;
white-space: nowrap;
@@ -130,6 +148,15 @@ const favorited = ref(props.actor.stashes.some((actorStash) => actorStash.id ===
user-select: all;
}
.name {
padding: .35rem .25rem .35rem .5rem;
}
.favicon {
height: 1rem;
padding: .35rem .5rem .35rem .25rem;
}
.avatar-container {
position: relative;
flex-grow: 1;
@@ -189,7 +216,16 @@ const favorited = ref(props.actor.stashes.some((actorStash) => actorStash.id ===
}
.age {
display: inline-flex;
align-items: center;
margin-right: .25rem;
.icon {
width: .8rem;
height: .8rem;
fill: var(--highlight-weak-10);
margin-left: .25rem;
}
}
.age-then {
@@ -197,6 +233,10 @@ const favorited = ref(props.actor.stashes.some((actorStash) => actorStash.id ===
font-weight: normal;
}
.age-death {
color: var(--grey-light-20);
}
.country {
display: flex;
align-items: center;

View File

@@ -0,0 +1,94 @@
<template>
<div class="page">
<nav class="nav">
<ul class="nav-items nolist">
<li class="nav-item">
<a
href="/admin/revisions/scenes"
class="nav-link nolink"
:class="{ active: pageContext.routeParams.section === 'revisions' && pageContext.routeParams.domain === 'scenes' }"
>Scene Revisions</a>
</li>
<li class="nav-item">
<a
href="/admin/revisions/actors"
class="nav-link nolink"
:class="{ active: pageContext.routeParams.section === 'revisions' && pageContext.routeParams.domain === 'actors' }"
>Actor Revisions</a>
</li>
<li class="nav-item">
<a
href="/admin/entities"
class="nav-link nolink"
:class="{ active: pageContext.routeParams.section === 'entities' }"
>Entity Health</a>
</li>
</ul>
</nav>
<div class="content">
<slot />
</div>
</div>
</template>
<script setup>
import { inject } from 'vue';
const pageContext = inject('pageContext');
</script>
<style scoped>
.page {
display: flex;
flex-direction: column;
flex-grow: 1;
background: var(--background-base-10);
}
.nav {
display: flex;
padding: 1rem 1rem .75rem 1rem;
border-bottom: solid 1px var(--shadow-weak-30);
margin-bottom: .25rem;
overflow-x: auto;
}
.nav-items {
display: flex;
gap: .5rem;
}
.nav-item {
display: block;
flex-shrink: 0;
background: var(--background-dark-20);
border-radius: 1rem;
color: var(--glass-strong-20);
font-size: .9rem;
font-weight: bold;
}
.nav-link {
display: block;
padding: .5rem 1rem;
font-weight: bold;
&.active {
color: var(--primary);
}
&:hover {
color: var(--primary);
}
}
.content {
display: flex;
flex-direction: column;
flex-grow: 1;
padding: 1rem;
}
</style>

View File

@@ -0,0 +1,540 @@
<template>
<section class="profile-section">
<div class="section-header">
<h3 class="heading">Alerts</h3>
<button
class="button"
@click="showAlertDialog = true"
>
<Icon icon="alarm-add" />
<span class="button-label">New alert</span>
</button>
</div>
<div class="filters">
<input
v-model="query"
type="search"
class="input filters-search"
placeholder="Search alerts"
>
<div class="filters-section">
<Icon
icon="star"
title="Only show actor alerts"
class="noselect"
:class="{ active: filterActors }"
@click="filterActors = !filterActors"
/>
<Icon
icon="price-tags"
title="Only show tag alerts"
class="noselect"
:class="{ active: filterTags }"
@click="filterTags = !filterTags"
/>
<Icon
icon="device_hub"
title="Only show channel alerts"
class="noselect"
:class="{ active: filterEntities }"
@click="filterEntities = !filterEntities"
/>
<Icon
icon="regexp"
title="Only show expression alerts"
class="noselect"
:class="{ active: filterExpressions }"
@click="filterExpressions = !filterExpressions"
/>
<Icon
v-if="filterCombined === false"
icon="target3"
title="Only show uncombined alerts"
class="noselect active filters-uncombined"
@click="toggleFilterCombined"
/>
<Icon
v-else
icon="make-group"
title="Only show combined alerts"
class="noselect filters-uncombined"
:class="{ active: filterCombined }"
@click="toggleFilterCombined"
/>
</div>
</div>
<ul class="alerts nolist">
<li
v-for="alert in filteredAlerts"
:key="`alert-${alert.id}`"
class="alert"
>
<div
class="alert-details"
:class="{ and: alert.and.fields, or: !alert.and.fields }"
>
<span
v-if="alert.tags.length > 0"
class="alert-detail alert-tags"
:class="{ and: alert.and.tags, or: !alert.and.tags }"
>
<span class="alert-values">
<span
v-for="tag in alert.tags"
:key="`tag-${alert.id}-${tag.id}`"
class="alert-key"
>
<a
:href="`/tag/${tag.slug}`"
class="alert-value link"
>{{ tag.name }}</a>
</span>
</span>
</span>
<span
v-if="alert.actors.length > 0"
class="alert-detail alert-actors"
:class="{ and: alert.and.actors, or: !alert.and.actors }"
>
<span class="alert-values">with
<span
v-for="actor in alert.actors"
:key="`actor-${alert.id}-${actor.id}`"
class="alert-key"
>
<a
:href="`/actor/${actor.id}/${actor.slug}`"
class="alert-value link"
>{{ actor.name }}</a>
</span>
</span>
</span>
<span
v-if="alert.entities.length > 0"
class="alert-detail alert-entities or"
>
<span class="alert-values">for
<span
v-for="entity in alert.entities"
:key="`entity-${alert.id}-${entity.id}`"
class="alert-key"
>
<a
:href="`/${entity.type}/${entity.slug}`"
class="alert-value link"
>
<Icon
v-if="entity.type === 'network'"
icon="device_hub"
/>{{ entity.name }}
</a>
</span>
</span>
</span>
<span
v-if="alert.matches.length > 0"
class="alert-detail alert-matches"
:class="{ and: alert.and.matches, or: !alert.and.matches }"
>
<span class="alert-values">matching
<span
v-for="match in alert.matches"
:key="`match-${alert.id}-${match.id}`"
class="alert-key"
>
<span class="alert-value">{{ match.property }}:
<span
class="alert-regex"
title="If your original expression was not a /regular expression/, it was converted, and new characters may have been added for syntactical purposes. These characters do not alter the function of the expression; they ensure it."
>{{ match.expression }}</span>
</span>
</span>
</span>
</span>
</div>
<div class="alert-meta">
<div class="alert-triggers">
<Icon
v-if="alert.notify && alert.isFromPreset"
v-tooltip="'Notify in traxxx, added as quick alert'"
icon="bell-plus"
class="trigger"
/>
<Icon
v-else-if="alert.notify"
v-tooltip="'Notify in traxxx'"
icon="bell2"
class="trigger"
/>
<Icon
v-if="alert.stashes.some((stash) => !stash.isPrimary)"
v-tooltip="`Add to ${alert.stashes.map((stash) => stash.name).join(', ')}`"
icon="folder-heart"
class="trigger"
/>
<Icon
v-else-if="alert.stashes.length > 0"
v-tooltip="alert.stashes.length > 0 ? 'Add to Favorites' : undefined"
icon="heart7"
class="trigger"
/>
<!--
<Icon
icon="envelop5"
title="E-mail me"
:class="{ trigger: alert.email }"
/>
-->
</div>
<div class="alert-actions">
<span
v-tooltip="format(alert.createdAt, 'yyyy-MM-dd hh:mm')"
class="alert-id"
title="Alert ID"
>#{{ alert.id }}</span>
<Icon
icon="bin"
@click="removeAlert(alert)"
/>
</div>
</div>
</li>
</ul>
<AlertDialog
v-if="showAlertDialog"
@close="showAlertDialog = false; reloadAlerts();"
/>
</section>
</template>
<script setup>
import { ref, computed, inject } from 'vue';
import { format } from 'date-fns';
import AlertDialog from '#/components/alerts/create.vue';
import { get, del } from '#/src/api.js';
const pageContext = inject('pageContext');
const alerts = ref(pageContext.pageProps.alerts);
const showAlertDialog = ref(false);
const done = ref(true);
const query = ref('');
const filterActors = ref(false);
const filterEntities = ref(false);
const filterTags = ref(false);
const filterExpressions = ref(false);
const filterCombined = ref(null);
const filteredAlerts = computed(() => {
const queryRegex = new RegExp(query.value, 'i');
return alerts.value.filter((alert) => {
if (filterActors.value && alert.actors.length === 0) {
return false;
}
if (filterEntities.value && alert.entities.length === 0) {
return false;
}
if (filterTags.value && alert.tags.length === 0) {
return false;
}
if (filterExpressions.value && alert.matches.length === 0) {
return false;
}
if (filterCombined.value === false && [...alert.actors, ...alert.entities, ...alert.tags, ...alert.matches].length > 1) {
return false;
}
if (filterCombined.value === true && [...alert.actors, ...alert.entities, ...alert.tags, ...alert.matches].length === 1) {
return false;
}
if (queryRegex.test(alert.id)) {
return true;
}
if (alert.actors.some((actor) => queryRegex.test(actor.name))) {
return true;
}
if (alert.tags.some((tag) => queryRegex.test(tag.name))) {
return true;
}
if (alert.entities.some((entity) => queryRegex.test(entity.name))) {
return true;
}
if (alert.matches.some((match) => queryRegex.test(match.expression))) {
return true;
}
return false;
});
});
async function reloadAlerts() {
alerts.value = await get('/alerts');
}
async function removeAlert(alert) {
if (done.value === false) {
return;
}
if (!confirm(`Are you sure you want to remove alert #${alert.id}?`)) { // eslint-disable-line no-restricted-globals, no-alert
return;
}
done.value = false;
const alertLabel = [
...alert.actors.map((actor) => actor.name),
...alert.tags.map((tag) => tag.name),
...alert.entities.map((entity) => entity.name),
...alert.matches.map((match) => match.expression),
].filter(Boolean).join(', ');
try {
await del(`/alerts/${alert.id}`, {
undoFeedback: `Removed alert for '${alertLabel}'`,
errorFeedback: `Failed to remove alert for '${alertLabel}'`,
appendErrorMessage: true,
});
await reloadAlerts();
} finally {
done.value = true;
}
}
const filterCombinedStates = [null, true, false];
function toggleFilterCombined() {
const index = filterCombinedStates.indexOf(filterCombined.value);
filterCombined.value = filterCombinedStates[(index + 1) % filterCombinedStates.length];
}
</script>
<style scoped>
.alerts {
width: 100%;
margin-bottom: 1rem;
}
.filters {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .5rem;
box-sizing: border-box;
padding: 0 .5rem;
margin: .5rem 0 .75rem 0;
overflow: hidden;
.input {
margin-right: 1rem;
}
.icon {
width: 1.25rem;
height: 1.25rem;
padding: .25rem .5rem;
fill: var(--glass);
&:hover {
fill: var(--glass-strong-10);
cursor: pointer;
}
&.active {
fill: var(--primary);
}
&.success {
fill: var(--success);
}
&.error {
fill: var(--error);
}
}
}
.filters-uncombined.icon {
padding-left: .75rem;
border-left: solid 1px var(--glass-weak-30);
margin-left: .5rem;
}
.alert {
padding: 0 0 0 .5rem;
display: flex;
align-items: stretch;
border-bottom: solid 1px var(--glass-weak-40);
&:hover {
border-color: var(--glass-weak-30);
}
}
.alert-triggers {
display: flex;
align-items: center;
gap: .5rem;
margin-right: .75rem;
.icon {
fill: var(--glass-weak-40);
}
.icon.trigger {
fill: var(--glass-weak-10);
}
}
.alert-details {
padding: .25rem 0;
flex-grow: 1;
line-height: 2.5;
color: var(--glass);
&.and .alert-detail:not(:last-child):after {
content: ' and ';
}
&.or .alert-detail:not(:last-child):after {
content: ' or ';
}
}
.alert-value {
color: var(--text);
.icon {
margin-right: .25rem;
fill: var(--glass);
transform: translateY(2px);
}
}
.alert-values {
padding: .5rem .5rem;
border-bottom: solid 1px var(--primary-light-20);
border-radius: .3rem;
}
.alert-detail {
&.and .alert-key:not(:last-child):after {
content: ' and ';
}
&.or .alert-key:not(:last-child):after {
content: ' or ';
}
}
.alert-regex {
&:before,
&:after {
content: '';
padding: 0 .1rem;
color: var(--primary-light-20);
}
}
.alert-meta {
display: flex;
}
.alert-actions {
display: flex;
align-items: center;
font-size: .9rem;
color: var(--glass-weak-10);
.icon {
height: 100%;
padding: 0 .75rem;
fill: var(--glass);
&:hover {
cursor: pointer;
fill: var(--primary);
}
}
}
@media(--compact) {
.profile-header {
border-radius: 0;
}
.section-header {
padding: .5rem 1rem .5rem 1rem;
}
.stashes {
padding: 0 1rem 1rem 1rem;
}
.alert {
padding: 0 .5rem;
}
.filters {
justify-content: space-between;
}
}
@media(--small-20) {
.alert {
flex-direction: column;
padding-right: 0;
}
.alert-meta {
padding: .5rem 0 .5rem 0;
justify-content: space-between;
}
}
@media(--small-30) {
.filters {
flex-direction: column;
justify-content: center;
.input {
width: 100%;
}
}
}
</style>

View File

@@ -329,13 +329,13 @@
/>
</li>
<template v-if="stashes.length < user.stashes.length">
<template v-if="stashes.length < assets.stashes.length">
<li class="field-add">
<button
v-if="stashes.length === 0"
type="button"
class="button favorites"
@click="selectStash(user.primaryStash)"
@click="selectStash(assets.primaryStash)"
><Icon icon="heart7" />Add to favorites</button>
</li>
@@ -349,7 +349,7 @@
<template #popper>
<ul class="nolist">
<li
v-for="stash in user.stashes.filter((stash) => !stashes.some((selectedStash) => selectedStash.id === stash.id))"
v-for="stash in assets.stashes.filter((stash) => !stashes.some((selectedStash) => selectedStash.id === stash.id))"
:key="`stash-result-${stash.id}`"
v-close-popper
class="result-item result-stash result-label"
@@ -384,21 +384,36 @@ import getPath from '#/src/get-path.js';
import Dialog from '#/components/dialog/dialog.vue';
import Checkbox from '#/components/form/checkbox.vue';
const { user } = inject('pageContext');
const { assets } = inject('pageContext');
const emit = defineEmits(['close']);
const props = defineProps({
presetActors: {
type: Array,
default: () => [],
},
presetEntities: {
type: Array,
default: () => [],
},
presetTags: {
type: Array,
default: () => [],
},
});
const actors = ref([]);
const emit = defineEmits(['alert', 'close']);
const actors = ref(props.presetActors);
const actorResults = ref([]);
const actorInput = ref(null);
const actorQuery = ref('');
const entities = ref([]);
const entities = ref(props.presetEntities);
const entityResults = ref([]);
const entityInput = ref(null);
const entityQuery = ref('');
const tags = ref([]);
const tags = ref(props.presetTags);
const tagResults = ref([]);
const tagInput = ref(null);
const tagQuery = ref('');
@@ -418,22 +433,32 @@ const email = ref(false);
const stashes = ref([]);
async function createAlert() {
console.log('creating alert');
const alertLabel = [
...actors.value.map((actor) => actor.name),
...tags.value.map((tag) => tag.name),
...entities.value.map((entity) => entity.name),
...matches.value.map((match) => match.expression),
].filter(Boolean).join(', ');
await post('/alerts', {
const newAlert = await post('/alerts', {
all: fieldsAnd.value,
allActors: actorAnd.value,
allTags: tagAnd.value,
allMatches: matchAnd.value,
actors: actors.value.map((actor) => actor.id),
tags: tags.value.map((tag) => tag.id),
tagIds: tags.value.map((tag) => tag.id),
matches: matches.value,
entities: entities.value.map((entity) => entity.id),
entityIds: entities.value.map((entity) => entity.id),
notify: notify.value,
email: email.value,
stashes: stashes.value.map((stash) => stash.id),
}, { appendErrorMessage: true });
}, {
successFeedback: `Alert for '${alertLabel}' set`,
errorFeedback: `Failed to set alert for '${alertLabel}'`,
appendErrorMessage: true,
});
emit('alert', newAlert);
emit('close', true);
}
@@ -466,7 +491,6 @@ async function searchTags() {
function focusActorInput() {
setTimeout(() => {
console.log(actorInput.value);
actorInput.value.focus();
}, 100);
}

View File

@@ -1,6 +1,17 @@
<template>
<div
v-if="restriction"
class="restricted"
>
<div>Traxxx is restricted in your region</div>
<a
href="/sfw"
class="link"
>Learn more</a>
</div>
<iframe
v-if="campaign?.banner?.type === 'html'"
v-else-if="campaign?.banner?.type === 'html'"
ref="iframe"
:width="campaign.banner.width"
:height="campaign.banner.height"
@@ -18,27 +29,40 @@
:href="campaign.url || campaign.affiliate?.url"
target="_blank"
class="campaign"
:style="{ 'background-image': backdrop && `url(${bannerSrc})` }"
:class="{ backdrop }"
data-umami-event="campaign-click"
:data-umami-event-campaign-id="`${campaign.entity.slug}-${campaign.id}`"
>
<img
:src="bannerSrc"
:width="campaign.banner.width"
:height="campaign.banner.height"
class="campaign-banner"
>
<div class="campaign-overlay">
<img
:src="bannerSrc"
:width="campaign.banner.width"
:height="campaign.banner.height"
class="campaign-banner"
>
</div>
</a>
</template>
<script setup>
import { inject } from 'vue';
const pageContext = inject('pageContext');
const { restriction } = pageContext;
const props = defineProps({
campaign: {
type: Object,
default: null,
},
backdrop: {
type: Boolean,
default: false,
},
});
// console.log(props.campaign?.banner);
// console.log(props.campaign);
const bannerSrc = (() => {
if (props.campaign.banner) {
@@ -60,6 +84,10 @@ const bannerSrc = (() => {
height: 100%;
display: flex;
justify-content: center;
border-radius: .25rem;
overflow: hidden;
background-size: cover;
background-position: center;
}
.frame {
@@ -67,11 +95,45 @@ const bannerSrc = (() => {
overflow: hidden;
}
.campaign-overlay {
width: 100%;
height: 100%;
display: flex;
box-sizing: border-box;
justify-content: center;
align-items: center;
}
.campaign-banner {
width: auto;
height: auto;
max-height: 100%;
max-width: 100%;
object-fit: contain;
}
.backdrop {
.campaign-overlay {
padding: 4px; /* margin for drop shadow */
backdrop-filter: blur(1rem) saturate(70%) brightness(125%);
}
.dark .campaign-overlay {
backdrop-filter: blur(1rem) saturate(70%) brightness(75%);
}
.campaign-banner {
filter: drop-shadow(0 0 2px var(--shadow-weak-20));
}
}
.restricted {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: .5rem;
font-weight: bold;
padding: .5rem;
}
</style>

View File

@@ -2,7 +2,7 @@
<Teleport to="#container">
<div
class="dialog-container"
@click="emit('close')"
@click="close"
>
<div
class="dialog"
@@ -14,11 +14,11 @@
<Icon
icon="cross2"
class="dialog-close"
@click="emit('close')"
@click="close"
/>
</div>
<slot />
<slot @event="({ type, data }) => emit('event', { type, data })" />
</div>
</div>
</Teleport>
@@ -27,14 +27,24 @@
<script setup>
import { onMounted } from 'vue';
defineProps({
const props = defineProps({
title: {
type: String,
default: null,
},
confirmClose: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['open', 'close']);
const emit = defineEmits(['open', 'close', 'event']);
function close() {
if (!props.confirmClose || confirm('You have unchanged changes, are you sure you want to close the dialog?')) { // eslint-disable-line no-restricted-globals, no-alert
emit('close');
}
}
onMounted(() => emit('open'));
</script>

View File

@@ -1,7 +1,8 @@
<template>
<nav class="domains">
<Link
:href="getPath('scenes')"
v-if="domains.includes('scenes')"
:href="`${path}/scenes`"
class="domain domain-scenes nolink"
:active="domain === 'scenes'"
>
@@ -10,7 +11,8 @@
</Link>
<Link
:href="getPath('actors')"
v-if="domains.includes('actors')"
:href="`${path}/actors`"
class="domain domain-actors nolink"
:active="domain === 'actors'"
>
@@ -19,7 +21,8 @@
</Link>
<Link
:href="getPath('movies')"
v-if="domains.includes('movies')"
:href="`${path}/movies`"
class="domain domain-movies nolink"
:active="domain === 'movies'"
>
@@ -30,20 +33,24 @@
</template>
<script setup>
const props = defineProps({
defineProps({
stash: {
type: Object,
default: null,
},
path: {
type: String,
default: '',
},
domains: {
type: Array,
default: () => ['scenes', 'actors', 'movies'],
},
domain: {
type: String,
default: 'scenes',
},
});
function getPath(targetDomain) {
return `/stash/${props.stash.user.username}/${props.stash.slug}/${targetDomain}`;
}
</script>
<style scoped>
@@ -52,10 +59,30 @@ function getPath(targetDomain) {
justify-content: center;
gap: .5rem;
padding: 0 1rem;
&.light .domain,
&.light .domain {
color: var(--glass-strong-10);
.icon {
fill: var(--glass-strong-10);
}
&.active,
&:hover {
color: var(--primary);
.icon {
fill: var(--primary);
}
}
}
}
.domain {
height: 100%;
display: flex;
align-items: center;
box-sizing: border-box;
padding: .75rem 1rem;
color: var(--highlight-strong-10);
@@ -82,7 +109,7 @@ function getPath(targetDomain) {
transform: translateY(1px);
}
.domains-bar {
.domains-bar:not(.light) {
background: var(--grey-dark-50);
box-shadow: inset 0 0 3px var(--shadow);
}

169
components/edit/actors.vue Normal file
View File

@@ -0,0 +1,169 @@
<template>
<ul
class="actors nolist"
:class="{ disabled: !editing.has(item.key) }"
>
<li
v-for="actor in [...item.value, ...newActors]"
:key="`actor-${actor.id}`"
class="actor"
:class="{ deleted: edits.actors && !edits.actors.some((actorId) => actorId === actor.id) }"
>
<span class="actor-name">{{ actor.name }}</span>
<Icon
v-if="edits.actors && !edits.actors.some((actorId) => actorId === actor.id)"
icon="checkmark"
class="add"
@click="emit('actors', edits.actors.concat(actor.id))"
/>
<Icon
v-else
icon="cross2"
class="remove"
@click="emit('actors', edits.actors.filter((actorId) => actorId !== actor.id))"
/>
</li>
<li class="new">
<ActorSearch
:disabled="!editing.has(item.key)"
@actor="addActor"
>
<Icon
icon="plus3"
class="add"
/>
</ActorSearch>
</li>
</ul>
</template>
<script setup>
import { ref, watch } from 'vue';
import ActorSearch from '#/components/actors/search.vue';
const newActors = ref([]);
const props = defineProps({
item: {
type: Object,
default: null,
},
scene: {
type: Object,
default: null,
},
edits: {
type: Object,
default: () => {},
},
editing: {
type: Set,
default: null,
},
});
const emit = defineEmits(['actors']);
function addActor(actor) {
newActors.value = newActors.value.concat(actor);
emit('actors', props.edits.actors.concat(actor.id));
}
watch(() => props.scene, () => { newActors.value = []; });
</script>
<style scoped>
.actors {
display: flex;
flex-wrap: wrap;
gap: .35rem .25rem;
&.disabled {
.actor {
background: var(--glass-weak-50);
color: var(--glass-strong-10);
.remove,
.add {
fill: var(--shadow-weak-30);
background: var(--shadow-weak-50);
}
}
.new .icon {
background: var(--shadow-weak-40);
}
}
.new {
display: flex;
align-items: center;
margin-left: .25rem;
&:hover {
box-shadow: 0 0 3px var(--shadow-weak-20);
}
.icon {
height: 100%;
padding: 0 .5rem;
background: var(--success);
fill: var(--text-light);
}
}
}
.actor {
display: flex;
align-items: stretch;
border-radius: .25rem;
background: var(--background);
box-shadow: 0 0 3px var(--shadow-weak-30);
&.deleted {
color: var(--glass);
text-decoration: line-through;
}
}
.actor,
.new {
.remove,
.add {
height: auto;
padding: .25rem .3rem;
border-radius: .25rem;
fill: var(--highlight-strong-10);
&:hover {
fill: var(--text-light);
cursor: pointer;
}
}
.remove {
fill: var(--error);
&:hover {
background: var(--error);
}
}
.add {
fill: var(--success);
&:hover {
background: var(--success);
}
}
}
.actor-name {
padding: .25rem .5rem;
}
</style>

View File

@@ -0,0 +1,230 @@
<template>
<div
class="figure augmentation"
>
<div class="value-section">
<span class="value-label">Natural boobs</span>
<select
v-model="augmentation.naturalBoobs"
class="input"
:disabled="!editing.has('augmentation')"
>
<option :value="null" />
<option :value="true">Yes</option>
<option :value="false">No</option>
</select>
</div>
<div
v-if="augmentation.naturalBoobs === false"
class="value-section"
>
<span class="value-label">Implant CC</span>
<input
v-model="augmentation.boobsVolume"
type="number"
class="volume input"
placeholder="CC"
min="50"
max="10000"
:disabled="!editing.has('augmentation')"
>
</div>
<div
v-if="augmentation.naturalBoobs === false"
class="value-section"
>
<span class="value-label">Implant type</span>
<select
v-model="augmentation.boobsImplant"
class="input"
:disabled="!editing.has('augmentation')"
>
<option :value="null" />
<option value="saline">Saline</option>
<option value="silicone">Silicone</option>
<option value="gummy">Gummy</option>
<option value="fat">Fat transfer</option>
</select>
</div>
<div
v-if="augmentation.naturalBoobs === false"
class="value-section"
>
<span class="value-label">Implant placement</span>
<select
v-model="augmentation.boobsPlacement"
class="input"
:disabled="!editing.has('augmentation')"
>
<option :value="null" />
<option value="over">Over muscle</option>
<option value="under">Under muscle</option>
</select>
</div>
<div
v-if="augmentation.naturalBoobs === false"
class="value-section"
>
<span class="value-label">Incision</span>
<select
v-model="augmentation.boobsIncision"
class="input"
:disabled="!editing.has('augmentation')"
>
<option :value="null" />
<option value="mammary">Under breast</option>
<option value="areolar">Areola</option>
<option value="crescent">Crescent</option>
<option value="lollipop">Lollipop</option>
<option value="anchor">Anchor</option>
<option value="axillary">Armpit</option>
<option value="umbilical">Navel</option>
</select>
</div>
<div
v-if="augmentation.naturalBoobs === false"
class="value-section value-divide"
>
<span class="value-label">Surgeon</span>
<input
v-model="augmentation.boobsSurgeon"
class="volume input"
placeholder="Surgeon"
list="surgeons"
:disabled="!editing.has('augmentation')"
>
<datalist id="surgeons">
<option value="Dr. Revis" />
<option value="Dr. Lenny Roudner" />
</datalist>
</div>
<div class="value-section">
<span class="value-label">Natural butt</span>
<select
v-model="augmentation.naturalButt"
class="input"
:disabled="!editing.has('augmentation')"
>
<option :value="null" />
<option :value="true">Yes</option>
<option :value="false">No</option>
</select>
</div>
<div
v-if="augmentation.naturalButt === false"
class="value-section"
>
<span class="value-label">Implant CC</span>
<input
v-model="augmentation.buttVolume"
type="number"
class="volume input"
placeholder="CC"
min="50"
max="10000"
:disabled="!editing.has('augmentation')"
>
</div>
<div
v-if="augmentation.naturalButt === false"
class="value-section"
>
<span class="value-label">Implant type</span>
<select
v-model="augmentation.buttImplant"
class="input"
:disabled="!editing.has('augmentation')"
>
<option :value="null" />
<option value="bbl">BBL (fat transfer)</option>
<option value="lift">Direct lift</option>
<option value="filler">Filler (Sculptra)</option>
<option value="mms">MMS (CoolTone)</option>
<option value="lipo">Lipo without BBL</option>
</select>
</div>
<div class="value-section">
<span class="value-label">Natural lips</span>
<select
v-model="augmentation.naturalLips"
class="input"
:disabled="!editing.has('augmentation')"
>
<option :value="null" />
<option :value="true">Yes</option>
<option :value="false">No</option>
</select>
</div>
<div
v-if="augmentation.naturalLips === false"
class="value-section"
>
<span class="value-label">Filler CC</span>
<input
v-model="augmentation.lipsVolume"
type="number"
class="volume input"
placeholder="CC"
min="50"
max="10000"
:disabled="!editing.has('augmentation')"
>
</div>
<div class="value-section">
<span class="value-label">Natural labia</span>
<select
v-model="augmentation.naturalLabia"
class="input"
:disabled="!editing.has('augmentation')"
>
<option :value="null" />
<option :value="true">Yes</option>
<option :value="false">No</option>
</select>
</div>
</div>
</template>
<script setup>
import { reactive, watch } from 'vue';
const props = defineProps({
edits: {
type: Object,
default: null,
},
editing: {
type: Set,
default: null,
},
});
const emit = defineEmits(['augmentation']);
const augmentation = reactive(props.edits.augmentation);
watch(augmentation, () => emit('augmentation', augmentation));
</script>

115
components/edit/avatar.vue Normal file
View File

@@ -0,0 +1,115 @@
<template>
<div class="avatar noshrink">
<img
:src="getPath(avatar, 'thumbnail')"
class="avatar-image"
>
<span
class="avatar-credit"
title="Credit"
>{{ avatar.credit }}</span>
<span class="avatar-meta">
<span title="Dimensions">{{ avatar.width }}x{{ avatar.height }}</span>
<span
v-if="avatar.sharpness"
title="Sharpness"
>{{ avatar.sharpness.toFixed(2) }}</span>
</span>
<a
:href="getPath(avatar)"
target="_blank"
class="avatar-zoom"
>
<Icon
icon="search"
/>
</a>
</div>
</template>
<script setup>
import getPath from '#/src/get-path.js';
defineProps({
avatar: {
type: Object,
default: null,
},
});
</script>
<style scoped>
.avatar {
display: inline-block;
position: relative;
border: solid 2px transparent;
border-radius: .35rem;
box-shadow: 0 0 3px var(--shadow-weak-10);
margin: 2px; /* clear outline */
font-size: 0;
overflow: hidden;
&.selected {
border: solid 2px var(--primary);
}
&:hover {
/*
.avatar-meta,
.avatar-credit {
display: none;
}
*/
.icon {
fill: var(--text-light);
}
}
}
.avatar-image {
height: 10rem;
}
.avatar-zoom {
position: absolute;
top: 0;
right: 0;
z-index: 10;
padding: .25rem;
.icon {
fill: var(--highlight-strong-20);
filter: drop-shadow(0 0 1px var(--shadow));
}
}
.avatar-meta,
.avatar-credit {
position: absolute;
z-index: 10;
box-sizing: border-box;
padding: .15rem .25rem;
font-size: .7rem;
color: var(--text-light);
text-shadow: 1px 1px 0 var(--shadow-strong-30);
cursor: default;
}
.avatar-meta {
width: 100%;
display: flex;
justify-content: space-between;
bottom: 0;
left: 0;
}
.avatar-credit {
bottom: .75rem;
left: 0;
}
</style>

147
components/edit/figure.vue Normal file
View File

@@ -0,0 +1,147 @@
<template>
<div class="figure">
<div class="value-section">
<span class="value-label">Units</span>
<select
:value="units"
class="input"
:disabled="!editing.has('figure')"
@change="emit('units', $event.target.value)"
>
<option value="us">USA</option>
<option value="uk">UK</option>
<option value="eu">Europe/Asia</option>
<option value="jp">Japan</option>
<option value="au">Australia</option>
<option value="it">Italy</option>
<option value="fr">France</option>
</select>
</div>
<span class="figure-bust">
<div class="value-section">
<span class="value-label">Bust</span>
<select
v-model="figure.bust"
class="select input"
placeholder="Bust"
:disabled="!editing.has('figure')"
>
<option
:key="`figure-bust-unknown`"
:value="null"
/>
<option
v-for="bust in bustSizes[units]"
:key="`figure-bust-${bust}`"
:value="Array.isArray(bust) ? bust[0] : bust"
>{{ Array.isArray(bust) ? bust.join('/') : bust }}</option>
</select>
</div>
<div class="value-section">
<span class="value-label">Cup</span>
<select
v-model="figure.cup"
class="select input"
placeholder="Cup"
:disabled="!editing.has('figure')"
>
<option
:key="`figure-cup-unknown`"
:value="null"
/>
<option
v-for="cup in cupSizes[units]"
:key="`figure-cup-${cup}`"
:value="Array.isArray(cup) ? cup[0] : cup"
>{{ Array.isArray(cup) ? cup.join('/') : cup }}</option>
</select>
</div>
</span>
<div class="value-section">
<span class="value-label">Waist</span>
<span>
<input
v-model="figure.waist"
type="number"
class="input"
:disabled="!editing.has('figure')"
>
<template v-if="['us', 'uk'].includes(units)">&nbsp;inch</template>
<template v-else>&nbsp;cm</template>
</span>
</div>
<div class="value-section">
<span class="value-label">Hip</span>
<span>
<input
v-model="figure.hip"
type="number"
class="input"
:disabled="!editing.has('figure')"
>
<template v-if="['us', 'uk'].includes(units)">&nbsp;inch</template>
<template v-else>&nbsp;cm</template>
</span>
</div>
</div>
</template>
<script setup>
import { reactive, watch } from 'vue';
const props = defineProps({
edits: {
type: Object,
default: null,
},
editing: {
type: Set,
default: null,
},
units: {
type: String,
default: 'us',
},
});
const emit = defineEmits(['figure', 'units']);
const figure = reactive(props.edits.figure);
watch(figure, () => emit('figure', figure));
const cupSizes = {
us: ['AA', 'A', 'B', 'C', 'D', ['DD', 'E'], ['DDD', 'F'], 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'], // United States
uk: ['AA', 'A', 'B', 'C', 'D', 'DD', 'E', 'F', 'FF', 'G', 'GG', 'H', 'HH', 'J', 'JJ', 'K', 'KK'], // United Kingdom
eu: ['AA', 'A', 'B', 'C', 'D', 'E', 'F', 'G', ' H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'], // Europe
jp: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q'], // Japan
};
cupSizes.fr = cupSizes.eu; // France
cupSizes.it = cupSizes.uk; // Italy
cupSizes.au = cupSizes.uk; // Australia
// bra band sizes
const bustSizes = {
us: [28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56],
eu: [60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130],
fr: [75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145],
it: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
au: [6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34],
};
bustSizes.uk = bustSizes.us;
bustSizes.jp = bustSizes.eu;
</script>

173
components/edit/movies.vue Normal file
View File

@@ -0,0 +1,173 @@
<template>
<ul
class="movies nolist"
:class="{ disabled: !editing.has(item.key) }"
>
<li
v-for="movie in [...item.value, ...newMovies]"
:key="`movie-${movie.id}`"
class="movie"
:class="{ deleted: edits.movies && !edits.movies.some((movieId) => movieId === movie.id) }"
>
<span class="movie-name">{{ movie.title }}</span>
<Icon
v-if="edits.movies && !edits.movies.some((movieId) => movieId === movie.id)"
icon="checkmark"
class="add"
@click="emit('movies', edits.movies.concat(movie.id))"
/>
<Icon
v-else
icon="cross2"
class="remove"
@click="emit('movies', edits.movies.filter((movieId) => movieId !== movie.id))"
/>
</li>
<li class="new">
<MovieSearch
:disabled="!editing.has(item.key)"
@movie="addMovie"
>
<Icon
icon="plus3"
class="add"
/>
</MovieSearch>
</li>
</ul>
</template>
<script setup>
import { ref, watch } from 'vue';
import MovieSearch from '#/components/movies/search.vue';
const newMovies = ref([]);
const props = defineProps({
item: {
type: Object,
default: null,
},
scene: {
type: Object,
default: null,
},
edits: {
type: Object,
default: () => {},
},
editing: {
type: Set,
default: null,
},
});
const emit = defineEmits(['movies']);
function addMovie(movie) {
newMovies.value = newMovies.value.concat(movie);
console.log(movie);
emit('movies', props.edits.movies.concat(movie.id));
console.log(props.edits);
}
watch(() => props.scene, () => { newMovies.value = []; });
</script>
<style scoped>
.movies {
display: flex;
flex-wrap: wrap;
gap: .35rem .25rem;
&.disabled {
.movie {
background: var(--glass-weak-50);
color: var(--glass-strong-10);
.remove,
.add {
fill: var(--shadow-weak-30);
background: var(--shadow-weak-50);
}
}
.new .icon {
background: var(--shadow-weak-40);
}
}
.new {
display: flex;
align-items: center;
margin-left: .25rem;
&:hover {
box-shadow: 0 0 3px var(--shadow-weak-20);
}
.icon {
height: 100%;
padding: 0 .5rem;
background: var(--success);
fill: var(--text-light);
}
}
}
.movie {
display: flex;
align-items: stretch;
border-radius: .25rem;
background: var(--background);
box-shadow: 0 0 3px var(--shadow-weak-30);
&.deleted {
color: var(--glass);
text-decoration: line-through;
}
}
.movie,
.new {
.remove,
.add {
height: auto;
padding: .25rem .3rem;
border-radius: .25rem;
fill: var(--highlight-strong-10);
&:hover {
fill: var(--text-light);
cursor: pointer;
}
}
.remove {
fill: var(--error);
&:hover {
background: var(--error);
}
}
.add {
fill: var(--success);
&:hover {
background: var(--success);
}
}
}
.movie-name {
padding: .25rem .5rem;
}
</style>

109
components/edit/penis.vue Normal file
View File

@@ -0,0 +1,109 @@
<template>
<div
class="figure penis"
>
<div class="value-section">
<span class="value-label">Units</span>
<select
:value="units"
class="input"
:disabled="!editing.has('penis')"
@change="emit('units', $event.target.value)"
>
<option value="imperial">Imperial</option>
<option value="metric">Metric</option>
</select>
</div>
<div class="value-section">
<span class="value-label">Penis length</span>
<span v-if="units === 'metric'">
<input
v-model="penis.metricLength"
type="number"
class="volume input"
min="1"
max="30"
:disabled="!editing.has('penis')"
> cm
</span>
<span v-if="units === 'imperial'">
<input
v-model="penis.imperialLength"
type="number"
class="volume input"
min="1"
max="30"
:disabled="!editing.has('penis')"
> inch
</span>
</div>
<div class="value-section">
<span class="value-label">Penis girth</span>
<span v-if="units === 'metric'">
<input
v-model="penis.metricGirth"
type="number"
class="volume input"
min="1"
max="30"
:disabled="!editing.has('penis')"
> cm
</span>
<span v-if="units === 'imperial'">
<input
v-model="penis.imperialGirth"
type="number"
class="volume input"
min="1"
max="30"
:disabled="!editing.has('penis')"
> inch
</span>
</div>
<div class="value-section">
<span class="value-label">Circumcised</span>
<select
v-model="penis.isCircumcised"
class="input"
:disabled="!editing.has('penis')"
>
<option :value="null" />
<option :value="true">Yes</option>
<option :value="false">No</option>
</select>
</div>
</div>
</template>
<script setup>
import { reactive, watch } from 'vue';
const props = defineProps({
edits: {
type: Object,
default: null,
},
editing: {
type: Set,
default: null,
},
units: {
type: String,
default: null,
},
});
const emit = defineEmits(['penis', 'units']);
const penis = reactive(props.edits.penis);
watch(penis, () => emit('penis', penis));
</script>

77
components/edit/place.vue Normal file
View File

@@ -0,0 +1,77 @@
<template>
<div
class="place"
>
<div class="value-section">
<span class="value-label">Country</span>
<select
v-model="place.country"
class="select input"
placeholder="Country"
:disabled="!editing.has(item.key)"
>
<option
:key="`${item.key}-country-unknown`"
:value="null"
/>
<option
v-for="country in sortedCountries"
:key="`${item.key}-country-${country.alpha2}`"
:value="country.alpha2"
>{{ country.alias || country.name }}</option>
</select>
</div>
<div class="value-section">
<span class="value-label">Place</span>
<input
v-model="place.place"
class="string input"
:disabled="!editing.has(item.key)"
>
</div>
</div>
</template>
<script setup>
import { reactive, watch, inject } from 'vue';
const props = defineProps({
item: {
type: Object,
default: null,
},
edits: {
type: Object,
default: null,
},
editing: {
type: Set,
default: null,
},
});
const pageContext = inject('pageContext');
const countries = pageContext.pageProps.countries;
const emit = defineEmits(['place']);
const place = reactive(props.edits[props.item.key]);
watch(place, () => emit('place', place));
const topCountries = [
'AU',
'BR',
'CZ',
'DE',
'JP',
'RU',
'GB',
'US',
];
const sortedCountries = countries.toSorted((countryA, countryB) => topCountries.indexOf(countryB.alpha2) - topCountries.indexOf(countryA.alpha2));
</script>

View File

@@ -0,0 +1,109 @@
<template>
<ul class="socials nolist">
<li
v-for="social in socials"
:key="`social-${rev.id}-${index}-${social.id}`"
class="delta-item"
:class="{ modified: social.modified }"
>
<a
:href="getUrl(social)"
target="_blank"
class="link"
>
<Icon
v-if="social.platform && env.socials.urls[social.platform]"
:icon="iconMap[social.platform] || social.platform"
:title="social.platform"
:class="`icon-social icon-${social.platform}`"
/>
<Icon
v-else-if="social.platform"
icon="bubbles10"
:title="social.platform"
:class="`icon-social icon-${social.platform} icon-generic`"
/>
<Icon
v-else-if="social.url"
icon="sphere"
:title="social.platform"
:class="`icon-social icon-${social.platform} icon-generic`"
/>
{{ social.handle || social.url }}
</a>
</li>&nbsp;
</ul>
</template>
<script setup>
import { inject } from 'vue';
import formatTemplate from 'template-format';
const pageContext = inject('pageContext');
const { env } = pageContext;
defineProps({
rev: {
type: Object,
default: null,
},
index: {
type: Number,
default: null,
},
socials: {
type: Array,
default: () => [],
},
});
const iconMap = {
twitter: 'twitter-x',
};
function getUrl(social) {
if (social.url) {
return social.url;
}
if (env.socials.urls[social.platform]) {
return formatTemplate(env.socials.urls[social.platform], { handle: social.handle });
}
return null;
}
</script>
<style scoped>
.socials {
display: flex;
flex-wrap: wrap;
gap: .5rem;
}
.delta-item .link {
display: inline-flex;
align-items: center;
padding: .25rem .5rem;
border-radius: .25rem;
box-shadow: 0 0 3px var(--shadow-weak-20);
color: inherit;
.icon {
margin-right: .5rem;
height: auto;
}
.icon-generic {
fill: var(--glass-strong-10);
}
}
.delta-item.modified {
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,686 @@
<template>
<div class="page">
<div
v-if="context === 'admin'"
class="revs-header"
>
<Checkbox
label="Show finalized"
:checked="showReviewed"
@change="(checked) => { showReviewed = checked; reloadRevisions(); }"
/>
</div>
<ul
class="revs nolist"
:class="{ [`revs-${context}`]: true }"
>
<li
v-for="rev in curatedRevisions"
:key="`rev-${rev.id}`"
class="rev"
:class="{
reviewed: reviewedRevisions.has(rev.id),
expanded: context === 'admin' || expanded.has(rev.id)
}"
>
<template v-if="context === 'admin' || expanded.has(rev.id)">
<div class="rev-header">
<a
:href="`/${domain.slice(0, -1)}/${rev.sceneId || rev.actorId}/${rev.base.slug}`"
target="_blank"
class="rev-link rev-scene nolink noshrink"
>{{ rev.sceneId || rev.actorId }}@{{ rev.hash.slice(0, 6) }}</a>
<a
:href="`/${domain.slice(0, -1)}/${rev.sceneId || rev.actorId}/${rev.base.slug}`"
target="_blank"
class="rev-link rev-title nolink ellipsis"
>{{ rev.base.title || rev.base.name }}</a>
<div class="rev-details noshrink">
<a
v-if="rev.user"
:href="`/user/${rev.user.username}`"
target="_blank"
class="rev-username nolink"
>{{ rev.user.username }}</a>
<time
:datetime="rev.createdAt"
class="rev-created"
>{{ format(rev.createdAt, 'yyyy-MM-dd hh:mm') }}</time>
</div>
<div class="rev-actions noshrink">
<span
v-if="rev.review && context === 'admin'"
class="approved"
:class="{ rejected: !rev.review.isApproved }"
>{{ rev.review.isApproved ? 'Approved' : 'Rejected' }} by&nbsp;<a
:href="`/user/${rev.review.username}`"
target="_blank"
class="nolink"
>{{ rev.review.username }}</a>&nbsp;{{ format(rev.review.reviewedAt, 'yyyy-MM-dd hh:mm') }}</span>
<template v-else-if="context === 'admin'">
<Icon
v-tooltip="`Ban user from submitting revisions`"
icon="user-block"
class="review-reject review-ban"
@click="banEditor(rev)"
/>
<Icon
v-tooltip="`Reject revision`"
icon="blocked"
class="review-reject"
@click="reviewRevision(rev, false)"
/>
<input
v-model="feedbacks[rev.id]"
placeholder="Feedback"
class="input"
>
<Icon
v-tooltip="`Approve and apply revision`"
icon="checkmark"
class="review-approve"
@click="reviewRevision(rev, true)"
/>
</template>
</div>
</div>
<ul class="rev-deltas">
<li
v-for="(delta, index) in rev.deltas"
:key="`delta-${rev.id}-${index}`"
class="delta"
>
<span class="delta-key ellipsis">{{ delta.key }}</span>
<div class="delta-deltas">
<span class="delta-from delta-value">
<Socials
v-if="delta.key === 'socials'"
:rev="rev"
:index="index"
:socials="rev.base[delta.key]"
/>
<ul
v-else-if="Array.isArray(rev.base[delta.key])"
class="nolist"
>[
<li
v-for="item in rev.base[delta.key]"
:key="`item-${rev.id}-${index}-${item.id}`"
class="delta-item"
:class="{ modified: item.modified }"
>{{ item.name || item.id || item }}</li>&nbsp;]
</ul>
<Avatar
v-else-if="delta.key === 'avatar'"
:avatar="avatarsById[rev.base[delta.key]]"
class="delta-avatar"
/>
<template v-else-if="rev.base[delta.key] instanceof Date">{{ format(rev.base[delta.key], 'yyyy-MM-dd hh:mm') }}</template>
<template v-else>{{ rev.base[delta.key] }}</template>
</span>
<span class="delta-arrow"></span>
<span class="delta-to delta-value">
<Socials
v-if="delta.key === 'socials'"
:rev="rev"
:index="index"
:socials="delta.value"
/>
<ul
v-else-if="Array.isArray(delta.value)"
class="nolist"
>[
<li
v-for="item in delta.value"
:key="`item-${rev.id}-${index}-${item.id}`"
class="delta-item"
:class="{ modified: item.modified }"
>{{ item.name || item.id || item }}</li>&nbsp;]
</ul>
<Avatar
v-else-if="delta.key === 'avatar'"
:avatar="avatarsById[delta.value]"
class="delta-avatar"
/>
<template v-else-if="delta.value instanceof Date">{{ format(delta.value, 'yyyy-MM-dd hh:mm') }}</template>
<template v-else>{{ delta.value }}</template>
</span>
</div>
</li>
</ul>
<div
v-if="rev.comment"
class="rev-comment"
>
{{ rev.comment }}
</div>
</template>
<div
v-else
class="rev-compact"
@click="expanded.add(rev.id)"
>
<span class="rev-scene nolink noshrink"><template v-if="context !== 'scene' && context !== 'actor'">{{ rev.sceneId || rev.actorId }}</template>@{{ rev.hash.slice(0, 6) }}</span>
<span
v-if="context !== 'scene'"
class="rev-title nolink ellipsis"
>{{ rev.base.title }}</span>
<span class="rev-summary">
<span class="summary-deltas ellipsis">{{ rev.deltas.map((delta) => delta.key).join(', ') }}</span>
<span
v-if="rev.comment"
class="summary-comment ellipsis"
>{{ rev.comment }}</span>
</span>
<div class="rev-details noshrink">
<a
v-if="rev.user && context !== 'user'"
:href="`/user/${rev.user.username}`"
target="_blank"
class="rev-username nolink ellipsis"
@click.stop
>{{ rev.user.username }}</a>
<a
v-if="rev.user && context !== 'user'"
v-tooltip="rev.user.username"
:href="`/user/${rev.user.username}`"
target="_blank"
class="rev-avatar nolink ellipsis"
@click.stop
><Icon icon="user3-long" /></a>
<time
:datetime="rev.createdAt"
class="rev-created"
>{{ format(rev.createdAt, 'yyyy-MM-dd hh:mm') }}</time>
</div>
</div>
</li>
</ul>
</div>
</template>
<script setup>
import { ref, computed, inject } from 'vue';
import { format } from 'date-fns';
import Avatar from '#/components/edit/avatar.vue';
import Socials from '#/components/edit/revision-socials.vue';
import Checkbox from '#/components/form/checkbox.vue';
import { get, post } from '#/src/api.js';
import { formatDuration } from '#/utils/format.js';
defineProps({
context: {
type: String,
default: 'admin',
},
});
const pageContext = inject('pageContext');
const revisions = ref(pageContext.pageProps.revisions);
const domain = pageContext.routeParams.domain;
const actors = ref(pageContext.pageProps.actors);
const tags = ref(pageContext.pageProps.tags);
const movies = ref(pageContext.pageProps.movies);
const avatars = ref(pageContext.pageProps.avatars);
const actorsById = computed(() => Object.fromEntries(actors.value.map((actor) => [actor.id, actor])));
const tagsById = computed(() => Object.fromEntries(tags.value.map((tag) => [tag.id, tag])));
const moviesById = computed(() => Object.fromEntries(movies.value.map((movie) => [movie.id, movie])));
const avatarsById = computed(() => Object.fromEntries(avatars.value.map((avatar) => [avatar.id, avatar])));
const feedbacks = ref({});
const showReviewed = ref(false);
const reviewedRevisions = ref(new Set());
const expanded = ref(new Set());
const mappedKeys = {
actors: actorsById,
tags: tagsById,
movies: moviesById,
};
const dateKeys = [
'date',
'dateOfBirth',
'dateOfDeath',
'productionDate',
'createdAt',
];
const curatedKeys = {
duration: (duration) => formatDuration(duration),
};
const curatedRevisions = computed(() => revisions.value.map((revision) => {
const curatedBase = Object.fromEntries(Object.entries(revision.base).map(([key, value]) => {
if (Array.isArray(value) && mappedKeys[key]) {
return [key, value.map((itemId) => ({
id: itemId,
name: mappedKeys[key].value[itemId]?.name || mappedKeys[key].value[itemId]?.title,
modified: revision.deltas.some((delta) => delta.key === key && !delta.value.some((deltaItemId) => deltaItemId === itemId)),
}))];
}
if (key === 'socials') {
// new socials don't have IDs yet, so we need to compare the values
return [key, value.map((item) => ({
...item,
modified: revision.deltas.some((delta) => delta.key === key && !delta.value.some((deltaItem) => deltaItem.url === item.url || `${deltaItem.platform}:${deltaItem.handle}` === `${item.platform}:${item.handle}`)),
}))];
}
if (dateKeys.includes(key) && value) {
return [key, new Date(value)];
}
if (curatedKeys[key]) {
return [key, curatedKeys[key](value)];
}
return [key, value];
}));
const curatedDeltas = revision.deltas.map((delta) => {
if (Array.isArray(delta.value) && mappedKeys[delta.key]) {
return {
...delta,
value: delta.value.map((itemId) => ({
id: itemId,
name: mappedKeys[delta.key].value[itemId]?.name || mappedKeys[delta.key].value[itemId]?.title,
modified: !revision.base[delta.key].includes(itemId),
})),
};
}
if (delta.key === 'socials') {
// new socials don't have IDs yet, so we need to compare the values
return {
...delta,
value: delta.value.map((social) => ({
...social,
modified: !revision.base[delta.key].some((baseItem) => baseItem.url === social.url || `${baseItem.platform}:${baseItem.handle}` === `${social.platform}:${social.handle}`),
})),
};
}
if (dateKeys.includes(delta.key) && delta.value) {
return {
...delta,
value: new Date(delta.value),
};
}
if (curatedKeys[delta.key]) {
return {
...delta,
value: curatedKeys[delta.key](delta.value),
};
}
return delta;
});
return {
...revision,
base: curatedBase,
deltas: curatedDeltas,
};
}));
async function reloadRevisions() {
const updatedRevisions = await get(`/revisions/${domain}`, {
isFinalized: showReviewed.value ? undefined : false,
limit: 50,
});
actors.value = updatedRevisions.actors;
tags.value = updatedRevisions.tags;
movies.value = updatedRevisions.movies;
avatars.value = updatedRevisions.avatars;
revisions.value = updatedRevisions.revisions;
}
async function reviewRevision(revision, isApproved) {
reviewedRevisions.value.add(revision.id);
try {
await post(`/revisions/${domain}/${revision.id}/reviews`, {
isApproved,
feedback: feedbacks.value[revision.id],
});
const updatedRevision = await get(`/revisions/${domain}/${revision.id}`, {
revisionId: revision.id,
});
revisions.value = revisions.value.map((rev) => (rev.id === updatedRevision.revision.id ? updatedRevision.revision : rev));
} catch (error) {
reviewedRevisions.value.delete(revision.id);
}
}
async function banEditor(revision) {
await post('/bans', {
userId: revision.user.id,
banIp: true,
});
await reviewRevision(revision, false);
}
</script>
<style scoped>
.page {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.revs-header {
display: flex;
margin-bottom: 1rem;
.check-container {
display: inline-flex;
}
}
.revs {
width: 100%;
flex-grow: 1;
overflow-x: auto;
padding: 3px; /* prevent shadow from getting cut off */
}
.rev {
display: flex;
flex-direction: column;
background: var(--background);
border-radius: .25rem;
box-shadow: 0 0 3px var(--shadow-weak-30);
margin-bottom: .25rem;
font-size: .9rem;
&.expanded {
min-width: 1200px;
margin-bottom: .5rem;
}
&.reviewed {
pointer-events: none;
opacity: .5;
}
&:hover {
box-shadow: 0 0 3px var(--primary-light-20);
}
}
.rev-link {
&:hover {
color: var(--primary);
text-decoration: underline;
}
}
.rev-header {
display: flex;
align-items: stretch;
border-bottom: solid 1px var(--glass-weak-30);
overflow: hidden;
}
.rev-scene {
width: 9rem;
display: flex;
box-sizing: border-box;
padding: .5rem .5rem;
align-items: center;
color: var(--glass-strong-10);
}
.rev-title {
padding: .5rem 0;
}
.rev-details {
display: flex;
flex-grow: 1;
justify-content: flex-end;
gap: 1rem;
align-items: center;
margin: 0 1rem;
}
.rev-username {
display: flex;
align-items: center;
font-weight: bold;
height: 100%;
}
.rev-actions {
display: flex;
align-items: stretch;
.icon {
height: 100%;
padding: 0 1.5rem;
fill: var(--glass);
&:hover {
cursor: pointer;
fill: var(--text-light);
}
}
.trigger {
height: 100%;
}
.review-approve {
fill: var(--success);
&:hover {
background: var(--success);
}
}
.review-reject {
fill: var(--error);
&:hover {
background: var(--error);
}
}
.review-comment {
&:hover {
background: var(--primary);
}
}
.approved {
display: flex;
align-items: center;
color: var(--success);
padding: .5rem;
}
.rejected {
color: var(--error);
}
}
.rev-deltas {
flex-grow: 1;
padding: 0;
margin: .25rem 0;
}
.delta {
display: flex;
justify-content: flex-start;
align-items: center;
padding: .15rem .5rem;
&:not(:last-child) {
border-bottom: solid 1px var(--glass-weak-40);
}
}
.delta-key {
width: 8.5rem;
flex-shrink: 0;
}
.delta-deltas {
display: flex;
flex-grow: 1;
}
.delta-from {
width: 40%;
flex-shrink: 0;
color: var(--reject);
padding: .25rem 0;
margin-right: 1rem;
}
.delta-arrow {
display: flex;
align-items: center;
padding: 0 1rem;
font-size: 1.2rem;
color: var(--glass-weak-10);
}
.delta-value {
display: flex;
align-items: center;
}
.delta-to {
flex-grow: 1;
color: var(--approve);
}
.delta-item {
line-height: 1.5;
&:not(:last-child):after {
content: ',\00a0';
}
&.modified {
font-weight: bold;
}
}
.rev-comment {
padding: .5rem .5rem;
border-top: solid 1px var(--glass-weak-30);
}
.rev-compact {
display: flex;
align-items: stretch;
cursor: pointer;
.rev-title {
width: 15rem;
margin-right: 2rem;
}
.rev-details {
flex-grow: 0;
}
.rev-avatar {
display: none;
.icon {
fill: var(--glass);
}
}
}
.revs-scene .rev-compact .rev-scene {
width: 6rem;
}
.rev-summary {
display: flex;
align-items: center;
flex-grow: 1;
overflow: hidden;
}
.summary-comment {
padding-left: .5rem;
border-left: solid 1px var(--glass-weak-20);
margin-left: .5rem;
}
.summary-deltas {
font-style: italic;
}
@media(--compact) {
.rev-compact .rev-details {
gap: .5rem;
}
.rev-compact .rev-username {
display: none;
}
.rev-compact .rev-avatar {
display: block;
}
}
@media(--small) {
.rev-compact .summary-comment {
padding: 0;
border: none;
margin: 0;
& + .summary-deltas {
display: none;
}
}
}
</style>

285
components/edit/socials.vue Normal file
View File

@@ -0,0 +1,285 @@
<template>
<ul
class="list nolist"
:class="{ disabled: !editing.has('socials') }"
>
<li
v-for="(social, index) in socials"
:key="`socials-${social}`"
class="list-item"
:class="{ deleted: !socials.some((listItem) => listItem.social === social.social || listItem.url === social.url) }"
>
<a
:href="getUrl(social)"
target="_blank"
class="link"
>
<Icon
v-if="social.platform && env.socials.urls[social.platform]"
:icon="iconMap[social.platform] || social.platform"
:title="social.platform"
:class="`icon-social icon-${social.platform}`"
/>
<Icon
v-else-if="social.platform"
icon="bubbles10"
:title="social.platform"
:class="`icon-social icon-${social.platform} icon-generic`"
/>
<Icon
v-else-if="social.url"
icon="sphere"
:title="social.platform"
:class="`icon-social icon-${social.platform} icon-generic`"
/>
{{ social.handle || social.url }}
</a>
<Icon
v-if="!socials.some((listItem) => listItem.social === social.social || listItem.url === social.url)"
icon="checkmark"
class="add noselect"
@click="socials = socials.concat(social)"
/>
<Icon
v-else
icon="cross2"
class="remove noselect"
@click="socials = socials.filter((listItem, listIndex) => listIndex !== index)"
/>
</li>
<li class="list-new">
<VDropdown>
<Icon
icon="plus2"
class="add noselect"
/>
<template #popper>
<form
class="new"
@submit.prevent="addSocial"
>
<div class="new-section">
<input
v-model="platform"
list="platforms"
class="input"
placeholder="Platform"
pattern="[a-z]+"
>
<datalist id="platforms">
<option
v-for="platformSlug in Object.keys(env.socials.urls)"
:key="`platform-${platformSlug}`"
:value="platformSlug"
>{{ platformSlug }}</option>
</datalist>
<input
v-model="handle"
class="input"
placeholder="Handle"
pattern="[\w-]+"
:disabled="!!url"
>
</div>
<div class="new-section">
OR<input
v-model="url"
class="input"
placeholder="Website URL"
:disabled="!!handle"
>
<button
class="button"
>Add</button>
</div>
</form>
</template>
</VDropdown>
</li>
</ul>
</template>
<script setup>
import {
ref,
watch,
inject,
} from 'vue';
import formatTemplate from 'template-format';
const pageContext = inject('pageContext');
const { env } = pageContext;
const props = defineProps({
edits: {
type: Object,
default: null,
},
editing: {
type: Set,
default: null,
},
});
const emit = defineEmits(['socials']);
const socials = ref(props.edits.socials);
const platform = ref('');
const handle = ref('');
const url = ref('');
watch(socials, () => emit('socials', socials));
const iconMap = {
twitter: 'twitter-x',
};
function addSocial() {
if (!handle.value && !url.value) {
return;
}
if (handle.value && !platform.value) {
return;
}
socials.value = socials.value.concat({
platform: platform.value || null,
handle: handle.value || null,
url: url.value || null,
});
emit('socials', socials.value);
platform.value = '';
handle.value = '';
url.value = '';
}
function getUrl(social) {
if (social.url) {
return url;
}
if (env.socials.urls[social.platform]) {
return formatTemplate(env.socials.urls[social.platform], { handle: social.handle });
}
return null;
}
</script>
<style scoped>
.list {
display: flex;
gap: .5rem;
&.disabled {
opacity: .5;
}
}
.list-item {
.icon-social {
margin-right: .5rem;
}
.icon-generic {
fill: var(--glass-strong-20);
}
&.deleted {
color: var(--glass);
text-decoration: line-through;
.icon.icon-social {
fill: var(--glass-weak-10);
}
}
}
.list-item,
.list-new {
display: inline-flex;
align-items: stretch;
border-radius: .25rem;
box-shadow: 0 0 3px var(--shadow-weak-30);
background: var(--background);
.link {
padding: .25rem 0 .25rem .5rem;
display: inline-flex;
align-items: center;
color: inherit;
}
.icon {
height: auto;
}
.add,
.remove {
padding: 0 .3rem;
margin-left: .5rem;
border-radius: .25rem;
&:hover {
fill: var(--text-light);
cursor: pointer;
}
}
.add {
fill: var(--success);
&:hover {
background: var(--success);
}
}
.remove {
fill: var(--error);
&:hover {
background: var(--error);
}
}
}
.list-new .add {
padding: .25rem .5rem;
background: var(--background);
margin: 0;
}
.new {
padding: .25rem;
}
.new-section {
display: flex;
align-items: center;
gap: .5rem;
padding: .25rem;
.input {
flex-grow: 1;
&:disabled {
opacity: .5;
}
}
}
</style>

180
components/edit/tags.vue Normal file
View File

@@ -0,0 +1,180 @@
<template>
<ul
class="tags nolist"
:class="{ disabled: !editing.has(item.key) }"
>
<li
v-for="tag in [...item.value, ...newTags]"
:key="`tag-${tag.id}`"
class="tag"
:class="{ deleted: edits.tags && !edits.tags.some((tagId) => tagId === tag.id) }"
>
<span class="tag-name">{{ tag.name }}</span>
<Icon
v-if="edits.tags && !edits.tags.some((tagId) => tagId === tag.id)"
icon="checkmark"
class="add"
@click="emit('tags', edits.tags.concat(tag.id))"
/>
<Icon
v-else
icon="cross2"
class="remove"
@click="emit('tags', edits.tags.filter((tagId) => tagId !== tag.id))"
/>
</li>
<li class="new">
<TagSearch
:disabled="!editing.has(item.key)"
@tag="addTag"
>
<Icon
icon="plus3"
class="add"
/>
</TagSearch>
</li>
</ul>
</template>
<script setup>
import { ref, watch } from 'vue';
import events from '#/src/events.js';
import TagSearch from '#/components/tags/search.vue';
const newTags = ref([]);
const props = defineProps({
item: {
type: Object,
default: null,
},
scene: {
type: Object,
default: null,
},
edits: {
type: Object,
default: () => {},
},
editing: {
type: Set,
default: null,
},
});
const emit = defineEmits(['tags']);
function addTag(tag) {
if (props.edits.tags.some((tagId) => tagId === tag.id)) {
events.emit('feedback', {
type: 'error',
message: 'Tag already added',
});
return;
}
newTags.value = newTags.value.concat(tag);
emit('tags', props.edits.tags.concat(tag.id));
}
watch(() => props.scene, () => { newTags.value = []; });
</script>
<style scoped>
.tags {
display: flex;
flex-wrap: wrap;
gap: .35rem .25rem;
&.disabled {
.tag {
background: var(--glass-weak-50);
color: var(--glass-strong-10);
.remove,
.add {
fill: var(--shadow-weak-30);
background: var(--shadow-weak-50);
}
}
.new .icon {
background: var(--shadow-weak-40);
}
}
.new {
display: flex;
align-items: center;
margin-left: .25rem;
&:hover {
box-shadow: 0 0 3px var(--shadow-weak-20);
}
.icon {
height: 100%;
padding: 0 .5rem;
background: var(--success);
fill: var(--text-light);
}
}
}
.tag {
display: flex;
align-items: stretch;
border-radius: .25rem;
background: var(--background);
box-shadow: 0 0 3px var(--shadow-weak-30);
&.deleted {
color: var(--glass);
text-decoration: line-through;
}
}
.tag,
.new {
.remove,
.add {
height: auto;
padding: .25rem .3rem;
border-radius: .25rem;
fill: var(--highlight-strong-10);
&:hover {
fill: var(--text-light);
cursor: pointer;
}
}
.remove {
fill: var(--error);
&:hover {
background: var(--error);
}
}
.add {
fill: var(--success);
&:hover {
background: var(--success);
}
}
}
.tag-name {
padding: .25rem .5rem;
}
</style>

View File

@@ -5,12 +5,20 @@
>
<img
v-if="entity.hasLogo"
:src="entity.type === 'network' || entity.isIndependent ? `/logos/${entity.slug}/thumbs/network.png` : `/logos/${entity.parent?.slug}/thumbs/${entity.slug}.png`"
:src="entity.type === 'network' || entity.isIndependent || !entity.parent
? `/logos/${entity.slug}/thumbs/network.png`
: `/logos/${entity.parent.slug}/thumbs/${entity.slug}.png`"
:alt="entity.name"
loading="lazy"
class="logo"
>
<span v-else>{{ entity.name }}</span>
<Icon
v-if="showNetworkSymbol && entity.type === 'network'"
icon="device_hub"
/>
</a>
</template>
@@ -20,6 +28,10 @@ defineProps({
type: Object,
default: null,
},
showNetworkSymbol: {
type: Boolean,
default: true,
},
});
</script>
@@ -33,10 +45,24 @@ defineProps({
box-sizing: border-box;
padding: 1rem;
border-radius: .5rem;
position: relative;
background: var(--shadow-strong-30);
text-align: center;
line-height: 1.25;
color: var(--text-light);
font-size: 1.25rem;
font-weight: bold;
overflow: hidden;
.icon {
position: absolute;
top: -.25rem;
right: -.25rem;
padding: .4rem .55rem .25rem .25rem;
border-radius: .25rem;
background: var(--highlight-weak-30);
fill: var(--text-light);
}
&:hover {
box-shadow: 0 0 3px var(--shadow);

View File

@@ -6,12 +6,16 @@
>Some actors may not be listed, apply a filter or search to narrow down results.</div>
<div class="filters-sort">
<input
v-model="search"
type="search"
:placeholder="`Filter ${availableActors.length} actors`"
class="input input-inline filters-search"
>
<label class="filter-search">
<input
v-model="search"
type="search"
:placeholder="`Filter ${availableActors.length} actors`"
class="input input-inline filters-search"
>
<Icon icon="search" />
</label>
<div
class="filter-sort noselect"

View File

@@ -1,12 +1,16 @@
<template>
<div class="filter channels-container">
<div class="filters-sort">
<input
v-model="search"
type="search"
:placeholder="`Filter ${channels.length} channels`"
class="input input-inline filters-search"
>
<label class="filter-search">
<input
v-model="search"
type="search"
:placeholder="`Filter ${channels.length} channels`"
class="input input-inline filters-search"
>
<Icon icon="search" />
</label>
<div
v-show="order === 'name'"
@@ -47,7 +51,7 @@
:title="entity.name"
>
<img
v-if="entity.isIndependent || entity.type === 'network'"
v-if="entity.isIndependent || entity.type === 'network' || !entity.parent"
:src="`/logos/${entity.slug}/favicon_dark.png`"
class="favicon"
>
@@ -131,14 +135,14 @@ const entities = computed(() => {
return acc;
}
if (channel.parent && !acc[channel.parent.id] && channel.type === 'channel') {
if (!acc[channel.id] && channel.parent && !acc[channel.parent.id] && (channel.type === 'channel' || channel.type === 'studio')) {
acc[channel.parent.id] = {
...channel.parent,
children: [],
};
}
if (channel.parent && channel.type === 'channel') {
if (!acc[channel.id] && channel.parent && (channel.type === 'channel' || channel.type === 'studio')) {
acc[channel.parent.id].children.push(channel);
}

Some files were not shown because too many files have changed in this diff Show More