1
0
mirror of https://github.com/s427/MARL.git synced 2025-01-31 03:24:48 +01:00

Code refactor with Astro

This commit is contained in:
Vincent CLAVIEN 2025-01-06 18:14:41 +01:00
parent 7238d20e83
commit 86d1fe05d0
101 changed files with 9129 additions and 1909 deletions

8
.gitignore vendored
View File

@ -1,2 +1,10 @@
.data
.docs
.astro
node_modules
# unused Astro generated files
dist/content-assets.mjs
dist/content-modules.mjs
dist/data-store.json
dist/settings.json

View File

@ -1,9 +1,6 @@
FROM nginx:latest
COPY index.html /usr/share/nginx/html/
COPY css/ /usr/share/nginx/html/css/
COPY js/ /usr/share/nginx/html/js/
COPY img/ /usr/share/nginx/html/img/
COPY svg/ /usr/share/nginx/html/svg/
COPY dist/ /usr/share/nginx/html/dist/
EXPOSE 80

View File

@ -1,170 +0,0 @@
@mixin theme-vars($theme: "") {
@if $theme == "dark" {
--bg0: #{$dark-bg0};
--bg1: #{$dark-bg1};
--bg2: #{$dark-bg2};
--bg3: #{$dark-bg3};
--bg4: #{$dark-bg4};
--fg0: #{$dark-fg0};
--fg1: #{$dark-fg1};
--fg2: #{$dark-fg2};
--fg-inv: #{$dark-fg-inv};
--menu-bg: #{$dark-menu-bg};
--menu-fg: #{$dark-menu-fg};
--menu-fg-active: #{$dark-menu-fg-active};
--menu-icon: #{$dark-menu-icon};
--menu-filter-active: #{$dark-menu-filter-active};
--panel-close: #{$dark-panel-close};
--panel-close-hover: #{$dark-panel-close-hover};
--accent: #{$dark-accent};
--accent-dark: #{$dark-accent-dark};
--accent-light: #{$dark-accent-light};
--accent-light2: #{$dark-accent-light2};
--accent-light3: #{$dark-accent-light3};
--overlay-icon: #{$dark-overlay-icon};
--overlay-icon-hover: #{$dark-overlay-icon-hover};
--overlay-backdrop: #{$dark-overlay-backdrop};
--menu-backdrop: #{$dark-menu-backdrop};
--bg-input: #{$dark-bg-input};
--bg-input-hover: #{$dark-bg-input-hover};
--bg-input-focus: #{$dark-bg-input-focus};
--bg-button: #{$dark-bg-button};
--bg-button-hover: #{$dark-bg-button-hover};
--button-svg: #{$dark-button-svg};
--button-svg-hover: #{$dark-button-svg-hover};
--button-svg-focus: #{$dark-button-svg-focus};
--button-svg-active: #{$dark-button-svg-active};
--fg-button-focus: #{$dark-fg-button-focus};
--fg-button-active: #{$dark-fg-button-active};
--posts-count: #{$dark-posts-count};
--selection-text: #{$dark-selection-text};
--selection-bg: #{$dark-selection-bg};
--stripe1: #{$dark-stripe1};
--stripe2: #{$dark-stripe2};
--stripe-fg: #{$dark-stripe-fg};
--private-post-bg: #{$dark-private-post-bg};
--private-post-border: #{$dark-private-post-border};
.actors-wrapper {
--actor-hue: 0;
--actor-bg0: #{$dark-actor-bg0};
--actor-bg0-ok: #{$dark-actor-bg0-ok};
--actor-bg1: #{$dark-actor-bg1};
--actor-bg1-ok: #{$dark-actor-bg1-ok};
--actor-bg2: #{$dark-actor-bg2};
--actor-bg2-ok: #{$dark-actor-bg2-ok};
--actor-bg3: #{$dark-actor-bg3};
--actor-bg3-ok: #{$dark-actor-bg3-ok};
--actor-bg4: #{$dark-actor-bg4};
--actor-bg4-ok: #{$dark-actor-bg4-ok};
--actor-fg0: #{$dark-actor-fg0};
--actor-fg1: #{$dark-actor-fg1};
--actor-fg1-ok: #{$dark-actor-fg1-ok};
--actor-tabs-bg: #{$dark-actor-tabs-bg};
--actor-accent: #{$dark-actor-accent};
--actor-accent-ok: #{$dark-actor-accent-ok};
--actor-accent2-ok: #{$dark-actor-accent2-ok};
}
.actors-tabs button {
--actor-accent-ok: #{$dark-actor-accent-ok};
--actor-accent2-ok: #{$dark-actor-accent2-ok};
}
.toot-content {
--actor-accent-ok: #{$dark-actor-accent-ok};
--actor-accent2-ok: #{$dark-actor-accent2-ok};
}
} @else {
--bg0: #{$bg0};
--bg1: #{$bg1};
--bg2: #{$bg2};
--bg3: #{$bg3};
--bg4: #{$bg4};
--fg0: #{$fg0};
--fg1: #{$fg1};
--fg2: #{$fg2};
--fg-inv: #{$fg-inv};
--menu-bg: #{$menu-bg};
--menu-fg: #{$menu-fg};
--menu-fg-active: #{$menu-fg-active};
--menu-icon: #{$menu-icon};
--menu-filter-active: #{$menu-filter-active};
--panel-close: #{$panel-close};
--panel-close-hover: #{$panel-close-hover};
--accent: #{$accent};
--accent-dark: #{$accent-dark};
--accent-light: #{$accent-light};
--accent-light2: #{$accent-light2};
--accent-light3: #{$accent-light3};
--overlay-icon: #{$overlay-icon};
--overlay-icon-hover: #{$overlay-icon-hover};
--overlay-backdrop: #{$overlay-backdrop};
--menu-backdrop: #{$menu-backdrop};
--bg-input: #{$bg-input};
--bg-input-hover: #{$bg-input-hover};
--bg-input-focus: #{$bg-input-focus};
--bg-button: #{$bg-button};
--bg-button-hover: #{$bg-button-hover};
--button-svg: #{$button-svg};
--button-svg-hover: #{$button-svg-hover};
--button-svg-focus: #{$button-svg-focus};
--button-svg-active: #{$button-svg-active};
--fg-button-focus: #{$fg-button-focus};
--fg-button-active: #{$fg-button-active};
--posts-count: #{$posts-count};
--selection-text: #{$selection-text};
--selection-bg: #{$selection-bg};
--stripe1: #{$stripe1};
--stripe2: #{$stripe2};
--stripe-fg: #{$stripe-fg};
--private-post-bg: #{$private-post-bg};
--private-post-border: #{$private-post-border};
.actors-wrapper {
--actor-hue: 0;
--actor-bg0: #{$actor-bg0};
--actor-bg0-ok: #{$actor-bg0-ok};
--actor-bg1: #{$actor-bg1};
--actor-bg1-ok: #{$actor-bg1-ok};
--actor-bg2: #{$actor-bg2};
--actor-bg2-ok: #{$actor-bg2-ok};
--actor-bg3: #{$actor-bg3};
--actor-bg3-ok: #{$actor-bg3-ok};
--actor-bg4: #{$actor-bg4};
--actor-bg4-ok: #{$actor-bg4-ok};
--actor-fg0: #{$actor-fg0};
--actor-fg1: #{$actor-fg1};
--actor-fg1-ok: #{$actor-fg1-ok};
--actor-tabs-bg: #{$actor-tabs-bg};
--actor-accent: #{$actor-accent};
--actor-accent-ok: #{$actor-accent-ok};
--actor-accent2-ok: #{$actor-accent2-ok};
}
.actors-tabs button {
--actor-accent-ok: #{$actor-accent-ok};
--actor-accent2-ok: #{$actor-accent2-ok};
}
.toot-content {
--actor-accent-ok: #{$actor-accent-ok};
--actor-accent2-ok: #{$actor-accent2-ok};
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,12 +0,0 @@
@import "colors";
@import "mixins";
@import "global";
@import "layout";
@import "welcome";
@import "actor";
@import "filters";
@import "toot";
@import "tags";
@import "menu";
@import "overlay";

48
dev/README.md Normal file
View File

@ -0,0 +1,48 @@
# Astro Starter Kit: Basics
```sh
npm create astro@latest -- --template basics
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554)
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
│ └── favicon.svg
├── src/
│ ├── layouts/
│ │ └── Layout.astro
│ └── pages/
│ └── index.astro
└── package.json
```
To learn more about the folder structure of an Astro project, refer to [our guide on project structure](https://docs.astro.build/en/basics/project-structure/).
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

10
dev/astro.config.mjs Normal file
View File

@ -0,0 +1,10 @@
// @ts-check
import { defineConfig } from "astro/config";
import relativeLinks from "astro-relative-links";
// https://astro.build/config
export default defineConfig({
integrations: [relativeLinks()],
outDir: "../dist",
});

5870
dev/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
dev/package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "marl",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^5.1.1",
"astro-relative-links": "^0.4.2"
},
"devDependencies": {
"sass-embedded": "^1.83.0"
}
}

View File

@ -0,0 +1,27 @@
---
import WelcomeScreen from './screens/WelcomeScreen.astro';
import LoadingScreen from './screens/LoadingScreen.astro';
import MainScreen from './screens/MainScreen.astro';
---
<div
class="main-page"
id="app"
x-data
:class="$store.ui.appClasses"
x-init="$watch('$store.files.appReady', value => checkAppReady(value))"
>
<template x-if="!$store.files.appReady && !$store.files.someFilesLoaded && !$store.files.loading">
<WelcomeScreen />
</template>
<template x-if="(!$store.files.appReady && $store.files.someFilesLoaded) || $store.files.loading">
<LoadingScreen />
</template>
<template x-if="$store.files.appReady">
<MainScreen />
</template>
</div>

View File

@ -0,0 +1,47 @@
<template x-if="$store.lightbox.show && $store.lightbox.data.length">
<div
id="lightbox"
class="lightbox overlay"
tabindex="-1"
@keyup.left="$store.lightbox.showPrev()"
@keyup.right="$store.lightbox.showNext()"
@keyup.esc="$store.lightbox.close()"
>
<div class="overlay-content">
<img
:alt="$store.lightbox.data[$store.lightbox.index].name"
:src="`data:${$store.lightbox.data[$store.lightbox.index].mediaType}; base64,${$store.files.sources[$store.lightbox.source][$store.lightbox.data[$store.lightbox.index].url].content}`"
@click="$store.lightbox.showNext()"
/>
</div>
<div class="overlay-ui">
<div class="backdrop" @click="$store.lightbox.close()"></div>
<button
x-show="$store.lightbox.data.length > 1"
class="viewer-next"
@click="$store.lightbox.showNext()"
>
<svg class="btn-icon" aria-hidden="true">
<use href="#viewer-next" /></svg
><span class="visually-hidden">Next image</span>
</button>
<button
x-show="$store.lightbox.data.length > 1"
class="viewer-prev"
@click="$store.lightbox.showPrev()"
>
<svg class="btn-icon" aria-hidden="true">
<use href="#viewer-prev" /></svg
><span class="visually-hidden">Previous image</span>
</button>
<button class="viewer-close" @click="$store.lightbox.close()">
<svg class="btn-icon" aria-hidden="true">
<use href="#close" /></svg
><span class="visually-hidden">Close image</span>
</button>
</div>
</div>
</template>

View File

@ -0,0 +1,44 @@
<div class="mobile-menu" id="mobile-menu">
<nav>
<ul>
<li>
<button class="menu-actor" @click="$store.ui.menuToggle('actor')">
<svg aria-hidden="true"><use href="#menu-actor" /></svg>
<span>Profile</span>
</button>
</li>
<li>
<button
class="menu-filters"
:class="$store.files.filtersActive ? 'filters-active' : ''"
@click="$store.ui.menuToggle('filters')"
>
<svg aria-hidden="true"><use href="#menu-filters" /></svg>
<span
>Filters
<em x-show="$store.files.filtersActive" class="visually-hidden">(some filters are active)</em>
</span>
</button>
</li>
<li>
<button class="menu-tags" @click="$store.ui.menuToggle('tags')">
<svg aria-hidden="true"><use href="#menu-tags" /></svg>
<span>Tags</span>
</button>
</li>
<li>
<button class="menu-new" @click="startOver">
<svg aria-hidden="true"><use href="#menu-new" /></svg>
<span>New File</span>
</button>
</li>
</ul>
</nav>
</div>
<div
class="panel-backdrop"
aria-hidden="true"
@click="$store.ui.menuClose()"
@keyup.esc="$store.ui.menuClose()"
/>

View File

@ -0,0 +1,40 @@
<div class="paging">
<div class="direction-back">
<button
@click="$store.files.firstPage('toots')"
:disabled="$store.files.currentPage <= 1"
>
<svg class="btn-icon" aria-hidden="true">
<use href="#nav-first" /></svg
><span class="btn-label">First</span>
</button>
<button
@click="$store.files.prevPage('paging-btn-prev')"
:disabled="$store.files.currentPage <= 1"
>
<svg class="btn-icon" aria-hidden="true">
<use href="#nav-prev" /></svg
><span class="btn-label">Prev</span>
</button>
</div>
<div class="direction-fwd">
<button
@click="$store.files.nextPage('paging-btn-next')"
:disabled="$store.files.currentPage >= $store.files.totalPages"
>
<span class="btn-label">Next</span
><svg class="btn-icon" aria-hidden="true">
<use href="#nav-next" />
</svg>
</button>
<button
@click="$store.files.lastPage('toots')"
:disabled="$store.files.currentPage >= $store.files.totalPages"
>
<span class="btn-label">Last</span
><svg class="btn-icon" aria-hidden="true">
<use href="#nav-last" />
</svg>
</button>
</div>
</div>

View File

@ -0,0 +1,38 @@
<div class="paging-options" id="paging-options" :class="$store.ui.pagingOptionsClass" tabindex="-1">
<div class="paging-options-inner">
<label for="paging-current-page">Page</label>
<input
class="current-page"
id="paging-current-page"
type="number"
min="1"
max="$store.files.totalPages"
x-model="$store.files.currentPage"
@keyup="$store.files.checkPagingValue()"
@change="$store.files.checkPagingValue()"
onclick="this.select()"
/>
/
<span class="total-pages" x-text="formatNumber($store.files.totalPages)"></span>
(
<input
class="page-size"
id="paging-page-size"
type="number"
min="1"
x-model="$store.files.pageSize"
@keyup="$store.files.checkPagingValue()"
@change="$store.files.checkPagingValue()"
onclick="this.select()"
/>
<label for="paging-page-size">posts per page)</label>
</div>
<div class="paging-options-reverse">
<button @click="$store.files.toggleTootsOrder()">
<svg class="btn-icon" aria-hidden="true">
<use href="#toggle-order" />
</svg>
<span class="btn-label">Reverse order</span>
</button>
</div>
</div>

View File

@ -0,0 +1,59 @@
---
import PagingOptions from "./PagingOptions.astro";
---
<div class="paging">
<div class="direction-back">
<button
id="paging-btn-first"
@click="$store.files.firstPage()"
:disabled="$store.files.currentPage <= 1"
>
<svg class="btn-icon" aria-hidden="true">
<use href="#nav-first" /></svg
><span class="btn-label">First</span>
</button>
<button
id="paging-btn-prev"
@click="$store.files.prevPage()"
:disabled="$store.files.currentPage <= 1"
>
<svg class="btn-icon" aria-hidden="true">
<use href="#nav-prev" /></svg
><span class="btn-label">Prev</span>
</button>
</div>
<div class="paging-options-toggle">
<button @click="$store.ui.togglePagingOptions()">
<svg class="btn-icon" aria-hidden="true">
<use href="#options" /></svg
><span class="btn-label">Paging options</span>
</button>
</div>
<PagingOptions />
<div class="direction-fwd">
<button
id="paging-btn-next"
@click="$store.files.nextPage()"
:disabled="$store.files.currentPage >= $store.files.totalPages"
>
<span class="btn-label">Next</span
><svg class="btn-icon" aria-hidden="true">
<use href="#nav-next" />
</svg>
</button>
<button
id="paging-btn-last"
@click="$store.files.lastPage()"
:disabled="$store.files.currentPage >= $store.files.totalPages"
>
<span class="btn-label">Last</span
><svg class="btn-icon" aria-hidden="true">
<use href="#nav-last" />
</svg>
</button>
</div>
</div>

View File

@ -0,0 +1,102 @@
<svg style="display: none">
<symbol viewBox="0 -960 960 960" id="reset-filters">
<path
d="m653-208-46 46q-17 17-42 17.5T522-162q-17-17-17-42.5t17-42.5l46-46q-4-11-6-23t-2-24q0-58 41-99t99-41q9 0 18 .5t17 3.5q11 4 13.5 16.5T743-439l-43 43q-11 11-11 28t11 28q11 11 28 11t28-11l43-43q8-8 20.5-5.5T836-375q3 8 3.5 17t.5 18q0 58-41 99t-99 41q-13 0-24.5-2t-22.5-6ZM480-760q-117 0-198.5 81.5T200-480q0 72 32.5 132t87.5 98v-70q0-17 11.5-28.5T360-360q17 0 28.5 11.5T400-320v160q0 17-11.5 28.5T360-120H200q-17 0-28.5-11.5T160-160q0-17 11.5-28.5T200-200h54q-62-50-98-122.5T120-480q0-75 28.5-140.5t77-114q48.5-48.5 114-77T480-840q113 0 203.5 63T814-615q6 16 0 31t-22 21q-16 6-31.5 0T739-585q-31-78-100.5-126.5T480-760Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="toggle-order">
<path
d="M360-440q-17 0-28.5-11.5T320-480v-247l-75 75q-11 11-27.5 11T189-652q-12-12-12-28.5t12-28.5l143-143q6-6 13-8.5t15-2.5q8 0 15 2.5t13 8.5l144 144q12 12 11.5 28T531-652q-12 11-28 11.5T475-652l-75-75v247q0 17-11.5 28.5T360-440ZM600-97q-8 0-15-2.5t-13-8.5L428-252q-12-12-11.5-28t12.5-28q12-11 28-11.5t28 11.5l75 75v-247q0-17 11.5-28.5T600-520q17 0 28.5 11.5T640-480v247l75-75q11-11 27.5-11t28.5 11q12 12 12 28.5T771-251L628-108q-6 6-13 8.5T600-97Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="load-file">
<path
d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h207q16 0 30.5 6t25.5 17l57 57h360q17 0 28.5 11.5T880-680q0 17-11.5 28.5T840-640H447l-80-80H160v480l79-263q8-26 29.5-41.5T316-560h516q41 0 64.5 32.5T909-457l-72 240q-8 26-29.5 41.5T760-160H160Zm84-80h516l72-240H316l-72 240Zm-84-262v-218 218Zm84 262 72-240-72 240Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="nav-first">
<path
d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="nav-prev">
<path
d="m432-480 156 156q11 11 11 28t-11 28q-11 11-28 11t-28-11L348-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 28-11t28 11q11 11 11 28t-11 28L432-480Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="nav-next">
<path
d="M504-480 348-636q-11-11-11-28t11-28q11-11 28-11t28 11l184 184q6 6 8.5 13t2.5 15q0 8-2.5 15t-8.5 13L404-268q-11 11-28 11t-28-11q-11-11-11-28t11-28l156-156Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="nav-last">
<path
d="M383-480 228-636q-11-11-11.5-27.5T228-692q11-11 28-11t28 11l184 184q6 6 8.5 13t2.5 15q0 8-2.5 15t-8.5 13L284-268q-11 11-27.5 11.5T228-268q-11-11-11-28t11-28l155-156Zm264 0L492-636q-11-11-11.5-27.5T492-692q11-11 28-11t28 11l184 184q6 6 8.5 13t2.5 15q0 8-2.5 15t-8.5 13L548-268q-11 11-27.5 11.5T492-268q-11-11-11-28t11-28l155-156Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="close">
<path
d="M480-424 284-228q-11 11-28 11t-28-11q-11-11-11-28t11-28l196-196-196-196q-11-11-11-28t11-28q11-11 28-11t28 11l196 196 196-196q11-11 28-11t28 11q11 11 11 28t-11 28L536-480l196 196q11 11 11 28t-11 28q-11 11-28 11t-28-11L480-424Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="viewer-next">
<path
d="M504-480 348-636q-11-11-11-28t11-28q11-11 28-11t28 11l184 184q6 6 8.5 13t2.5 15q0 8-2.5 15t-8.5 13L404-268q-11 11-28 11t-28-11q-11-11-11-28t11-28l156-156Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="viewer-prev">
<path
d="m432-480 156 156q11 11 11 28t-11 28q-11 11-28 11t-28-11L348-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 28-11t28 11q11 11 11 28t-11 28L432-480Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="menu-actor">
<path
d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-240v-32q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v32q0 33-23.5 56.5T720-160H240q-33 0-56.5-23.5T160-240Zm80 0h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="menu-filters">
<path
d="M440-240q-17 0-28.5-11.5T400-280q0-17 11.5-28.5T440-320h80q17 0 28.5 11.5T560-280q0 17-11.5 28.5T520-240h-80ZM280-440q-17 0-28.5-11.5T240-480q0-17 11.5-28.5T280-520h400q17 0 28.5 11.5T720-480q0 17-11.5 28.5T680-440H280ZM160-640q-17 0-28.5-11.5T120-680q0-17 11.5-28.5T160-720h640q17 0 28.5 11.5T840-680q0 17-11.5 28.5T800-640H160Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="menu-tags">
<path
d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h440q19 0 36 8.5t28 23.5l180 240q16 21 16 48t-16 48L664-192q-11 15-28 23.5t-36 8.5H160Zm0-80h440l180-240-180-240H160v480Zm310-240Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="menu-new">
<path
d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h207q16 0 30.5 6t25.5 17l57 57h360q17 0 28.5 11.5T880-680q0 17-11.5 28.5T840-640H447l-80-80H160v480l79-263q8-26 29.5-41.5T316-560h516q41 0 64.5 32.5T909-457l-72 240q-8 26-29.5 41.5T760-160H160Zm84-80h516l72-240H316l-72 240Zm-84-262v-218 218Zm84 262 72-240-72 240Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="fetch-data">
<path
d="M440-474v-242q-76 14-118 73.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41h480q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-48-22-89.5T600-680v-93q74 35 117 103.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H260q-91 0-155.5-63T40-377q0-78 47-139t123-78q17-72 85-137t145-65q33 0 56.5 23.5T520-716v242l36-35q11-11 27.5-11t28.5 12q11 11 11 28t-11 28L508-348q-12 12-28 12t-28-12L348-452q-11-11-11.5-27.5T348-508q11-11 27.5-11.5T404-509l36 35Zm40-44Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="options">
<path
d="M433-80q-27 0-46.5-18T363-142l-9-66q-13-5-24.5-12T307-235l-62 26q-25 11-50 2t-39-32l-47-82q-14-23-8-49t27-43l53-40q-1-7-1-13.5v-27q0-6.5 1-13.5l-53-40q-21-17-27-43t8-49l47-82q14-23 39-32t50 2l62 26q11-8 23-15t24-12l9-66q4-26 23.5-44t46.5-18h94q27 0 46.5 18t23.5 44l9 66q13 5 24.5 12t22.5 15l62-26q25-11 50-2t39 32l47 82q14 23 8 49t-27 43l-53 40q1 7 1 13.5v27q0 6.5-2 13.5l53 40q21 17 27 43t-8 49l-48 82q-14 23-39 32t-50-2l-60-26q-11 8-23 15t-24 12l-9 66q-4 26-23.5 44T527-80h-94Zm7-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="json">
<path
d="M190-360h70q17 0 28.5-11.5T300-400v-200h-60v190h-40v-50h-50v60q0 17 11.5 28.5T190-360Zm177 0h60q17 0 28.5-11.5T467-400v-60q0-17-11.5-28.5T427-500h-50v-50h40v20h50v-30q0-17-11.5-28.5T427-600h-60q-17 0-28.5 11.5T327-560v60q0 17 11.5 28.5T367-460h50v50h-40v-20h-50v30q0 17 11.5 28.5T367-360Zm176-60v-120h40v120h-40Zm-10 60h60q17 0 28.5-11.5T633-400v-160q0-17-11.5-28.5T593-600h-60q-17 0-28.5 11.5T493-560v160q0 17 11.5 28.5T533-360Zm127 0h50v-105l40 105h50v-240h-50v105l-40-105h-50v240ZM120-160q-33 0-56.5-23.5T40-240v-480q0-33 23.5-56.5T120-800h720q33 0 56.5 23.5T920-720v480q0 33-23.5 56.5T840-160H120Zm0-80h720v-480H120v480Zm0 0v-480 480Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="question">
<path
d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="bookmark">
<path
d="M200-120v-640q0-33 23.5-56.5T280-840h400q33 0 56.5 23.5T760-760v640L480-240 200-120Zm80-122 200-86 200 86v-518H280v518Zm0-518h400-400Z"
/>
</symbol>
<symbol viewBox="0 -960 960 960" id="favorite">
<path
d="m354-287 126-76 126 77-33-144 111-96-146-13-58-136-58 135-146 13 111 97-33 143ZM233-120l65-281L80-590l288-25 112-265 112 265 288 25-218 189 65 281-247-149-247 149Zm247-350Z"
/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -0,0 +1,145 @@
---
import CloseBtn from './CloseBtn.astro'
import ImgAvatar from './actor/ImgAvatar.astro'
import ImgHeader from './actor/ImgHeader.astro'
import LikesBookmarks from './actor/LikesBookmarks.astro'
---
<div
class="actor mobile-menu-panel"
id="panel-actor"
role="region"
aria-labelledby="actor-title"
tabindex="-1"
>
<CloseBtn />
<h2 class="visually-hidden" id="actor-title">Account info</h2>
<h3 class="visually-hidden" id="actor-tabs-title">Accounts</h3>
<div class="actors-wrapper" :style="'--actor-hue: '+ $store.files.sources[$store.ui.actorPanel].hue">
<template x-if="$store.files.sources.length > 1">
<div class="actors-tabs" role="tablist" aria-labelledby="actor-tabs-title">
<template x-for="(_, source) in $store.files.sources">
<button
type="button"
role="tab"
:id="'actortab-' + source"
:aria-selected="source === $store.ui.actorPanel ? 'true': 'false'"
:aria-controls="'actorpanel-' + source"
:style="'--actor-hue: '+ $store.files.sources[source].hue"
@click="$store.ui.openActorPanel(source)"
@keyup.right="$store.ui.switchActorPanel('up')"
@keyup.left="$store.ui.switchActorPanel('down')"
>
<span x-text="$store.files.sources[source].actor.name"></span>
</button>
</template>
</div>
</template>
<template x-if="$store.files.sources.length === 1">
<span class="visually-hidden" id="actortab-0" x-text="$store.files.sources[0].actor.name"></span>
</template>
<template x-for="source in $store.files.sources">
<div
class="actor-panel"
:id="'actorpanel-' + source.id"
role="tabpanel"
tabindex="0"
:aria-labelledby="'actortab-' + source.id"
x-data="{a: source.actor}"
x-show="$store.ui.actorPanel === source.id"
>
<div class="actor-pretty">
<div class="actor-banner">
<ImgHeader />
</div>
<div class="actor-id">
<ImgAvatar />
<h1 class="actor-name" x-text="await a.name"></h1>
<div class="actor-url">
<a :href="await a.url" x-text="await a.url"></a>
</div>
</div>
<div class="actor-summary" x-show="a.summary" x-html="await a.summary"></div>
<div class="actor-infos">
<dl>
<dt>Member since</dt>
<dd x-text="await formatDate(a.published)"></dd>
</dl>
<template x-for="item in await a.attachment">
<dl>
<dt x-text="item.name"></dt>
<dd x-html="item.value"></dd>
</dl>
</template>
</div>
<div class="actor-posts-count">
<div class="total">
<span class="count" x-text="formatNumber(source.outbox.totalItems)"></span>
<span class="label">posts</span>
</div>
<div class="archive">
<span class="count" x-text="formatNumber(source.nbToots)"></span>
<span class="label">in archive</span>
</div>
<details class="comment" x-show="source.nbToots != source.outbox.totalItems">
<summary>
<span class="summary-icon">
<svg aria-hidden="true">
<use href="#question" />
</svg>
</span>
<span class="summary-label">Why are those two numbers different?</span>
</summary>
<div class="details-content">
<p>
Posts that are not directly hosted on your instance are kept in a cache by your instance
for a given time, after what they are deleted from that cache. Posts that are not in your
instance cache any more are not included in your archive. This affects boosts, likes, and
bookmarks.
</p>
</div>
</details>
</div>
</div>
<div class="actor-raw">
<div class="actor-raw-inner">
<details>
<summary>
<span class="summary-icon">
<svg aria-hidden="true">
<use href="#json" />
</svg>
</span>
<span class="summary-label" :id="source + 'raw-data-label'"
>Raw data <em>(actor.json)</em></span
>
</summary>
<div class="details-content">
<textarea
x-text="formatJson(await a)"
:aria-labelledby="source + 'raw-data-label'"
readonly
></textarea>
</div>
</details>
</div>
</div>
<div class="actor-likes-bookmarks">
<LikesBookmarks type="likes" />
<LikesBookmarks type="bookmarks" />
</div>
</div>
</template>
</div>
</div>

View File

@ -0,0 +1,5 @@
<button class="panel-close" @click="$store.ui.menuClose()" x-show="$store.ui.menuIsActive">
<svg class="btn-icon" aria-hidden="true">
<use href="#close" /></svg
><span class="visually-hidden">Close panel</span>
</button>

View File

@ -0,0 +1,130 @@
---
import CloseBtn from './CloseBtn.astro'
import FilterText from './filters/FilterText.astro';
import FilterCheckbox from './filters/FilterCheckbox.astro';
---
<div
class="toots-filters mobile-menu-panel"
id="panel-filters"
role="search"
aria-labelledby="toots-filter-title"
tabindex="-1"
>
<CloseBtn />
<h2 id="toots-filter-title">Filter posts</h2>
<p class="visually-hidden">
The list of posts will be automatically updated based on the active filters below.
</p>
<div class="toots-filters-group">
<FilterText label="Full text" name="fullText" />
<FilterText label="Hashtags" name="hashtagText" />
<FilterText label="Mentions" name="mentionText" />
<FilterText label="External links" name="externalLink" />
<FilterText label="Summary (CW)" name="summary" />
</div>
<div class="toots-filters-group">
<FilterCheckbox label="Has been edited" name="isEdited" />
<template x-show="$store.files.duplicates">
<FilterCheckbox label="Non-exact duplicates" name="isDuplicate" />
</template>
</div>
<div class="toots-filters-group">
<h3 class="toots-filters-group-title">Must contain</h3>
<FilterCheckbox label="Hashtag(s)" name="hasHashtags" />
<FilterCheckbox label="Mention(s)" name="hasMentions" />
<FilterCheckbox label="External link(s)" name="hasExternalLink" />
<FilterCheckbox label="Summary (CW)" name="hasSummary" />
</div>
<div class="toots-filters-group">
<h3 class="toots-filters-group-title">Type</h3>
<FilterCheckbox label="Original posts (incl. replies)" name="typeOriginal" />
<FilterCheckbox label="Boosts" name="typeBoost" />
</div>
<div class="toots-filters-group">
<FilterCheckbox label='Does not start with "@"' name="noStartingAt" />
<FilterCheckbox label="Marked as sensitive" name="isSensitive" />
</div>
<div class="toots-filters-group">
<h3 class="toots-filters-group-title">Must have attachment</h3>
<FilterCheckbox label="Any type" name="attachmentAny" />
<FilterCheckbox label="Image(s)" name="attachmentImage" />
<FilterCheckbox label="Video(s)" name="attachmentVideo" />
<FilterCheckbox label="Sound(s)" name="attachmentSound" />
</div>
<div class="toots-filters-group">
<FilterCheckbox label="Without alt text" name="attachmentNoAltText" />
<FilterCheckbox label="With alt text" name="attachmentWithAltText" />
</div>
<div class="toots-filters-group">
<h3 class="toots-filters-group-title">Visibility</h3>
<FilterCheckbox label="Public" name="visibilityPublic" />
<FilterCheckbox label="Unlisted" name="visibilityUnlisted" />
<FilterCheckbox label="Followers only" name="visibilityFollowers" />
<FilterCheckbox label="Mentioned people only" name="visibilityMentioned" />
</div>
<div class="toots-filters-group" x-show="$store.files.toots.length">
<h3 class="toots-filters-group-title">Language</h3>
<template x-for="lang in $store.files.sortedLanguages">
<div class="toots-filter checkbox" :class="isFilterActive('lang_' + lang[0]) ? 'active' : ''">
<label :for="'filter-lang-' + lang[0]">
<input
:id="'filter-lang-' + lang[0]"
type="checkbox"
x-model="$store.files.filters['lang_' + lang[0]]"
@change="$store.files.setFilter()"
/>
<span>
<span x-text="lang[0]"></span>
(<span x-text="lang[1]"></span>)
</span>
</label>
</div>
</template>
</div>
<template x-if="$store.files.sources.length > 1">
<div class="toots-filters-group">
<h3 class="toots-filters-group-title">Author</h3>
<template x-for="source in $store.files.sources">
<div class="toots-filter checkbox" :class="isFilterActive('actor_' + source.id) ? 'active' : ''">
<label :for="'filter-actor-' + source.id">
<input
:id="'filter-actor-' + source.id"
type="checkbox"
x-model="$store.files.filters['actor_' + source.id]"
@change="$store.files.setFilter()"
/>
<span>
<span x-text="source.actor.name"></span>
</span>
</label>
</div>
</template>
</div>
</template>
<div class="toots-filters-reset">
<button @click="$store.files.resetFilters(true)" :disabled="!$store.files.filtersActive">
<svg class="btn-icon" aria-hidden="true">
<use href="#reset-filters" /></svg
><span class="btn-label">Reset filters</span>
</button>
</div>
</div>

View File

@ -0,0 +1,31 @@
---
import PagingTop from '../PagingTop.astro';
---
<header class="toots-header" aria-labelledby="toots-header-title">
<h2 id="toots-header-title">
<span class="count">
<span x-text="await formatNumber($store.files.filteredToots.length)" class="nb"></span>
posts
</span>
<span class="order">
-
<span x-text="await $store.files.sortAsc ? 'oldest' : 'latest'"></span>
first
<button @click="$store.files.toggleTootsOrder()" class="toggle-order">
<svg class="btn-icon" aria-hidden="true">
<use href="#toggle-order" /></svg
><span class="btn-label">Reverse</span>
</button>
</span>
<button @click="startOver" class="load-new">
<svg class="btn-icon" aria-hidden="true">
<use href="#load-file" /></svg
><span class="btn-label">Load new file</span>
</button>
</h2>
<PagingTop />
</header>

View File

@ -0,0 +1,47 @@
---
import PagingBottom from '../PagingBottom.astro';
import Post from './posts/Post.astro';
---
<div
class="toots"
id="toots"
role="region"
aria-labelledby="toots-title"
tabindex="-1"
>
<h2 id="toots-title" class="visually-hidden">Posts</h2>
<template
x-show="$store.files.pagedToots.length"
x-for="toot in await $store.files.pagedToots"
>
<Post />
</template>
<div
class="toots-no-results"
x-show="$store.files.pagedToots.length === 0"
>
<div x-show="$store.files.filtersActive">
<p>No results for the specified filters</p>
<p class="action">
<button
@click="$store.files.resetFilters(true)"
:disabled="!$store.files.filtersActive"
>
<svg class="btn-icon" aria-hidden="true">
<use href="#reset-filters" />
</svg>
<span class="btn-label">Reset filters</span>
</button>
</p>
</div>
<div x-show="!$store.files.filtersActive">
<p>No posts found in archive (?!)</p>
</div>
</div>
<PagingBottom />
</div>

View File

@ -0,0 +1,19 @@
---
import CloseBtn from './CloseBtn.astro'
import TagsGroup from './tags/TagsGroup.astro';
---
<aside
class="toots-tags mobile-menu-panel"
id="panel-tags"
aria-labelledby="toots-tags-title"
tabindex="-1"
>
<CloseBtn />
<h2 class="tags-title" id="toots-tags-title">Tags</h2>
<TagsGroup type="hashtags" />
<TagsGroup type="mentions" />
<TagsGroup type="boosts" />
</aside>

View File

@ -0,0 +1,20 @@
---
import CloseBtn from './CloseBtn.astro'
---
<aside
class="panel-tools mobile-menu-panel"
id="panel-tools"
aria-labelledby="panel-tools-title"
tabindex="-1"
>
<CloseBtn />
<h2 class="tools-title visually-hidden" id="panel-tools-title">Tools</h2>
<button @click="startOver" class="load-new">
<svg class="btn-icon" aria-hidden="true">
<use href="#load-file" /></svg
><span class="btn-label">Load new file</span>
</button>
</aside>

View File

@ -0,0 +1,22 @@
---
import NoAvatar from '../../../img/no-avatar.png'
---
<template x-if="source.avatar.noImg">
<div class="actor-avatar no-avatar">
<img :alt="a.name" src={NoAvatar.src} />
</div>
</template>
<template x-if="! source.avatar.noImg">
<button
id="actor-avatar"
class="actor-avatar"
@click="$store.lightbox.openProfileImg('avatar', 'actor-avatar', source.id)"
>
<img
:alt="a.name"
:src="`data:${source.avatar.type}; base64,${source.avatar.content}`"
/>
</button>
</template>

View File

@ -0,0 +1,22 @@
---
import NoHeader from '../../../img/no-header.png'
---
<template x-if="source.header.noImg">
<div class="actor-header no-header">
<img alt="header" src={NoHeader.src} />
</div>
</template>
<template x-if="! source.header.noImg">
<button
id="actor-header"
class="actor-header"
@click="$store.lightbox.openProfileImg('header', 'actor-header', source.id)"
>
<img
alt="header"
:src="`data:${source.header.type}; base64,${source.header.content}`"
/>
</button>
</template>

View File

@ -0,0 +1,37 @@
---
const { type } = Astro.props;
let label = "Favorites";
if (type === "bookmarks") {
label = "Bookmarks";
}
---
<div class={`actor-${type}`}>
<details>
<summary>
<span class="summary-icon">
<svg aria-hidden="true">
<use href="#favorite" />
</svg>
</span>
<h2 class="summary-label">
{label} (<span class="count" x-text={`source.${type}.length`}></span>)
</h2>
</summary>
<div class="details-content">
<template x-if={`source.${type}.length`}>
<ul>
<template x-for={`url in source.${type}`}>
<li>
<a :href="url" x-html="formatLikesBookmarks(url)"></a>
</li>
</template>
</ul>
</template>
<template x-if={`! source.${type}.length`}>
<p class="no-content">... no {label?.toLowerCase()} ...</p>
</template>
</div>
</details>
</div>

View File

@ -0,0 +1,15 @@
---
const { label, name } = Astro.props;
---
<div class="toots-filter checkbox" :class={`isFilterActive('${name}') ? 'active' : ''`}>
<label for={"filter-"+name}>
<input
id={"filter-"+name}
type="checkbox"
x-model.debounce={"$store.files.filters."+name}
@change.debounce="$store.files.setFilter()"
/>
<span>{label}</span>
</label>
</div>

View File

@ -0,0 +1,19 @@
---
const { label, name } = Astro.props;
---
<div
class="toots-filter text"
:class={"isFilterActive('"+name+"') ? 'active' : ''"}
>
<label for={"filter-"+name}>{label}</label>
<div class="input-wrapper">
<input
id={"filter-"+name}
type="text"
x-model.debounce={"$store.files.filters."+name}
@keyup.debounce="$store.files.setFilter()"
@change.debounce="$store.files.setFilter()"
/>
</div>
</div>

View File

@ -0,0 +1,11 @@
<div class="att-description">
<span
class="desc-body"
x-text="att.name ?? 'No description provided'"
:id="'att-' + toot._marl.id + '-' + index + '-desc'"
aria-hidden="true"
></span>
<span class="desc-source">
<strong>In archive:</strong> <span x-text="att.url"></span>
</span>
</div>

View File

@ -0,0 +1,24 @@
---
import AttachmentDescription from './AttachmentDescription.astro'
---
<template
x-if="attachmentIsImage(att) && $store.files.sources[toot._marl.source][att.url]"
>
<div class="att-wrapper">
<button
class="att-img-wrapper"
@click="$store.lightbox.open(toot, index, toot._marl.id + '-' + index)"
:id="'att-' + toot._marl.id + '-' + index"
>
<img
alt=""
:src="`data:${att.mediaType}; base64,${await $store.files.sources[toot._marl.source][att.url].content}`"
:aria-labelledby="'att-' + toot._marl.id + '-' + index + '-desc'"
/>
</button>
<AttachmentDescription />
</div>
</template>

View File

@ -0,0 +1,20 @@
---
import AttachmentDescription from './AttachmentDescription.astro'
---
<template
x-if="attachmentIsSound(att) && $store.files.sources[toot._marl.source][att.url]"
>
<div class="att-wrapper">
<audio
controls
:src="`data:${att.mediaType}; base64,${await $store.files.sources[toot._marl.source][att.url].content}`"
:aria-labelledby="'att-' + toot._marl.id + '-' + index + '-desc'"
></audio>
<AttachmentDescription />
</div>
</template>

View File

@ -0,0 +1,20 @@
---
import AttachmentDescription from './AttachmentDescription.astro'
---
<template
x-if="attachmentIsVideo(att) && $store.files.sources[toot._marl.source][att.url]"
>
<div class="att-wrapper">
<video
controls
:width="att.width"
:height="att.height"
:src="`data:${att.mediaType}; base64,${await $store.files.sources[toot._marl.source][att.url].content}`"
:aria-labelledby="'att-' + toot._marl.id + '-' + index + '-desc'"
></video>
<AttachmentDescription />
</div>
</template>

View File

@ -0,0 +1,150 @@
---
import AttachmentImage from "./AttachmentImage.astro";
import AttachmentSound from "./AttachmentSound.astro";
import AttachmentVideo from "./AttachmentVideo.astro";
---
<div
class="toot"
:class="['toot-type-' + contentType(toot.type).toLowerCase(), 'toot-visibility-' + toot._marl.visibility[0]]"
:style="'--actor-hue: '+ $store.files.sources[toot._marl.source].hue"
>
<h3 class="toot-header visually-hidden">
<span x-text="contentType(toot.type)"></span>
by <span x-text="formatAuthor(toot.actor, true)"></span>,
<span x-text="formatDateTime(toot.published)"></span>
</h3>
<template x-if="toot.type === 'Create'">
<div class="toot-pretty">
<div class="toot-summary" x-show="toot.object.summary">
<span x-text="toot.object.summary"></span>
</div>
<div class="toot-content" x-html="toot.object.content"></div>
<div class="toot-attachments" x-show="toot.object.attachment && toot.object.attachment.length">
<ul>
<template x-for="(att, index) in toot.object.attachment">
<li
:class="attachmentWrapperClass(att)"
:data-trigger="loadAttachedMedia(att, toot._marl.source)"
>
<AttachmentImage />
<AttachmentSound />
<AttachmentVideo />
</li>
</template>
</ul>
</div>
</div>
</template>
<template x-if="toot.type === 'Announce' && toot.object && toot.object.content">
<div class="toot-pretty">
<div class="toot-content">
<div class="toot-content-inner" x-html="toot.object.content"></div>
</div>
</div>
</template>
<template x-if="toot.type === 'Announce' && toot.object && !toot.object.content">
<div class="toot-pretty">
<div class="toot-content">
<div class="toot-content-inner">
<a :href="toot.object" x-text="toot.object"></a>
</div>
</div>
</div>
</template>
<div class="toot-infos">
<span class="type"> <span x-text="contentType(toot.type)"></span>  </span>
<span class="author">
by
<span x-html="formatAuthor(toot.actor)"></span>
</span>
<span class="published"
><span x-text="formatDateTime(toot.published)"></span>
<span x-show="toot.object && toot.object.updated" class="updated">
Last updated
<span x-text="formatDateTime(toot.object.updated)"></span>
</span>
</span>
<span class="visibility">
<span x-text="toot._marl.visibility[1]"></span>
</span>
<span class="link">
<a x-show="toot.object.id" :href="toot.object.id">link</a>
</span>
</div>
<template
x-if="(toot.object.tag && toot.object.tag.length) || (toot._marl.externalLinks && toot._marl.externalLinks.length)"
>
<div class="toot-meta">
<template x-if="toot.object.tag && toot.object.tag.filter(x => x.type === 'Mention').length">
<div class="toot-people">
<h4>People</h4>
<ul>
<template x-for="tag in toot.object.tag">
<li x-show="tag.type === 'Mention'">
<a :href="tag.href" x-text="tag.name"></a>
</li>
</template>
</ul>
</div>
</template>
<template x-if="toot.object.tag && toot.object.tag.filter(x => x.type === 'Hashtag').length">
<div class="toot-hashtags">
<h4>Hashtags</h4>
<ul>
<template x-for="tag in toot.object.tag">
<li x-show="tag.type === 'Hashtag'">
<a :href="tag.href" x-text="tag.name"></a>
</li>
</template>
</ul>
</div>
</template>
<template x-if="toot._marl.externalLinks.length">
<div class="toot-links">
<h4>External links</h4>
<ul>
<template x-for="link in toot._marl.externalLinks">
<li>
<a :href="link.href" x-text="link.text"></a>
</li>
</template>
</ul>
</div>
</template>
</div>
</template>
<div class="toot-raw">
<details>
<summary>
<span class="summary-icon">
<svg aria-hidden="true">
<use href="#json" />
</svg>
</span>
<span class="summary-label" :id="'raw-' + toot._marl.id">Raw data</span>
</summary>
<div class="details-content">
<textarea
x-text="formatJson(toot)"
:aria-labelledby="'raw-' + toot._marl.id"
readonly
></textarea>
</div>
</details>
</div>
</div>

View File

@ -0,0 +1,10 @@
---
import TagsGroupHeader from './TagsGroupHeader.astro';
import TagsGroupMain from './TagsGroupMain.astro';
const { type } = Astro.props;
---
<div class="tags-group">
<TagsGroupHeader type={type} />
<TagsGroupMain type={type} />
</div>

View File

@ -0,0 +1,43 @@
---
const { type } = Astro.props;
let title, filterLabel, storeEntry, filterId;
switch (type) {
case 'hashtags':
title = "Hashtags";
filterLabel = "filter hashtags";
filterId = 'hashtags';
storeEntry = "listHashtags";
break;
case 'mentions':
title = "Mentions";
filterLabel = "filter mentions";
filterId = 'mentions';
storeEntry = "listMentions";
break;
case 'boosts':
title = "Boosted users";
filterLabel = "filter boosted users";
filterId = 'boostsAuthors';
storeEntry = "listBoostsAuthors";
break;
}
---
<div class="tags-group-header">
<h3>
{title}
<span class="count">
(<span x-text={`formatNumber($store.files.${storeEntry}.length)`}></span>)
</span>
</h3>
<div class="tags-group-filter">
<label for={`tags-group-${filterId}-filter`} class="visually-hidden">{filterLabel}</label>
<input
id={`tags-group-${filterId}-filter`}
type="text"
onclick="this.select()"
x-model.debounce={`$store.files.tagsFilters.${filterId}`}
/>
</div>
</div>

View File

@ -0,0 +1,58 @@
---
const { type } = Astro.props;
let storeData, storeFilter, filterProp, btnId, clickCmd;
switch (type) {
case 'hashtags':
storeData = "listHashtags";
storeFilter = "hashtagText";
filterProp = "name";
btnId = "hashtag";
clickCmd = `.slice(1)`;
break;
case 'mentions':
storeData = "listMentions";
storeFilter = "mentionText";
filterProp = "name";
btnId = "mention";
clickCmd = `.replaceAll('@', '_').replaceAll('.', '_')`;
break;
case 'boosts':
storeData = "listBoostsAuthors";
storeFilter = "fullText";
filterProp = "url";
btnId = "boost-author";
clickCmd = `.replaceAll('/', '_')`;
break;
}
---
<div class="tags-group-scroll">
<ul x-show={`$store.files.${storeData}.length`}>
<template x-for={`item in await $store.files.${storeData}`}>
<li :class={`item.${filterProp} === $store.files.filters.${storeFilter} ? 'active' : ''`}>
<button
:id={`'filter-${btnId}-' + item.${filterProp}.toLowerCase()${clickCmd}`}
@click={`$store.files.filterByTag('${storeFilter}', item.${filterProp}, 'filter-${btnId}-' + item.${filterProp}.toLowerCase()${clickCmd})`}
>
<div>
<span
class="visually-hidden"
x-text={`item.${filterProp} === $store.files.filters.${storeFilter} ? 'active item:' : ''`}
></span>
<span class="count" x-text="`(${item.nb})`"></span>
{type !== 'boosts' && (
<span class="name" x-text="item.name"></span>
)}
{type === 'boosts' && (
<span class="name">
<span x-text="item.name"></span>
<span x-text="item.domain" class="domain"></span>
</span>
)}
</div>
</button>
</li>
</template>
</ul>
</div>

View File

@ -0,0 +1,8 @@
<main class="welcome loading-more">
<div class="intro">
<div class="file-loader loading">
<div>... Loading ...</div>
<div class="filename" x-text="$store.files.loadingName"></div>
</div>
</div>
</main>

View File

@ -0,0 +1,32 @@
---
import MobileMenu from '../MobileMenu.astro';
import LightBox from '../LightBox.astro';
import ActorPanel from '../panels/ActorPanel.astro';
import FiltersPanel from '../panels/FiltersPanel.astro';
import HeaderPanel from '../panels/HeaderPanel.astro';
import PostsPanel from '../panels/PostsPanel.astro';
import TagsPanel from '../panels/TagsPanel.astro';
---
<main
class="main-section"
id="main-section"
tabindex="-1"
x-on:resize.window="$store.ui.checkMenuState()"
>
<div
class="main-section-inner"
id="main-section-inner"
:class="$store.files.sources.length > 1 ? 'multiple-actors' : ''"
>
<MobileMenu />
<ActorPanel />
<FiltersPanel />
<HeaderPanel />
<PostsPanel />
<TagsPanel />
</div>
<LightBox />
</main>

View File

@ -0,0 +1,35 @@
<main class="welcome">
<div class="intro">
<h1>
Welcome to MARL
<span class="accronym">(Mastodon Archive Reader Lite)</span>
</h1>
<p>
MARL allows you to explore the content of your Mastodon archive file in a user-friendly interface.
Everything takes place in the browser: your archive stays strictly on your computer; none of its data is
sent to any server.
</p>
<p>
You can request your Mastodon archive by logging into your account on the web, then visiting
"Preferences > Import and export > Request your archive".<br />
<strong>Please note:</strong> only ZIP files are supported (not GZ).
</p>
<p>
<strong>Start by opening your archive file with MARL.</strong><br />
You can drag and drop it anywhere on this page, or
<label for="file-loader" tabindex="0">click here to select it</label>.
</p>
<input
id="file-loader"
class="file-loader"
type="file"
accept=".zip"
multiple
@change="unZip(Object.values($event.target.files))"
/>
</div>
<div class="about">
<a href="https://github.com/s427/MARL">Project page (github)</a>
</div>
</main>

View File

@ -0,0 +1,172 @@
@use 'colors';
@mixin theme-vars($theme: "") {
@if $theme == "dark" {
--bg0: #{colors.$dark-bg0};
--bg1: #{colors.$dark-bg1};
--bg2: #{colors.$dark-bg2};
--bg3: #{colors.$dark-bg3};
--bg4: #{colors.$dark-bg4};
--fg0: #{colors.$dark-fg0};
--fg1: #{colors.$dark-fg1};
--fg2: #{colors.$dark-fg2};
--fg-inv: #{colors.$dark-fg-inv};
--menu-bg: #{colors.$dark-menu-bg};
--menu-fg: #{colors.$dark-menu-fg};
--menu-fg-active: #{colors.$dark-menu-fg-active};
--menu-icon: #{colors.$dark-menu-icon};
--menu-filter-active: #{colors.$dark-menu-filter-active};
--panel-close: #{colors.$dark-panel-close};
--panel-close-hover: #{colors.$dark-panel-close-hover};
--accent: #{colors.$dark-accent};
--accent-dark: #{colors.$dark-accent-dark};
--accent-light: #{colors.$dark-accent-light};
--accent-light2: #{colors.$dark-accent-light2};
--accent-light3: #{colors.$dark-accent-light3};
--overlay-icon: #{colors.$dark-overlay-icon};
--overlay-icon-hover: #{colors.$dark-overlay-icon-hover};
--overlay-backdrop: #{colors.$dark-overlay-backdrop};
--menu-backdrop: #{colors.$dark-menu-backdrop};
--bg-input: #{colors.$dark-bg-input};
--bg-input-hover: #{colors.$dark-bg-input-hover};
--bg-input-focus: #{colors.$dark-bg-input-focus};
--bg-button: #{colors.$dark-bg-button};
--bg-button-hover: #{colors.$dark-bg-button-hover};
--button-svg: #{colors.$dark-button-svg};
--button-svg-hover: #{colors.$dark-button-svg-hover};
--button-svg-focus: #{colors.$dark-button-svg-focus};
--button-svg-active: #{colors.$dark-button-svg-active};
--fg-button-focus: #{colors.$dark-fg-button-focus};
--fg-button-active: #{colors.$dark-fg-button-active};
--posts-count: #{colors.$dark-posts-count};
--selection-text: #{colors.$dark-selection-text};
--selection-bg: #{colors.$dark-selection-bg};
--stripe1: #{colors.$dark-stripe1};
--stripe2: #{colors.$dark-stripe2};
--stripe-fg: #{colors.$dark-stripe-fg};
--private-post-bg: #{colors.$dark-private-post-bg};
--private-post-border: #{colors.$dark-private-post-border};
.actors-wrapper {
--actor-hue: 0;
--actor-bg0: #{colors.$dark-actor-bg0};
--actor-bg0-ok: #{colors.$dark-actor-bg0-ok};
--actor-bg1: #{colors.$dark-actor-bg1};
--actor-bg1-ok: #{colors.$dark-actor-bg1-ok};
--actor-bg2: #{colors.$dark-actor-bg2};
--actor-bg2-ok: #{colors.$dark-actor-bg2-ok};
--actor-bg3: #{colors.$dark-actor-bg3};
--actor-bg3-ok: #{colors.$dark-actor-bg3-ok};
--actor-bg4: #{colors.$dark-actor-bg4};
--actor-bg4-ok: #{colors.$dark-actor-bg4-ok};
--actor-fg0: #{colors.$dark-actor-fg0};
--actor-fg1: #{colors.$dark-actor-fg1};
--actor-fg1-ok: #{colors.$dark-actor-fg1-ok};
--actor-tabs-bg: #{colors.$dark-actor-tabs-bg};
--actor-accent: #{colors.$dark-actor-accent};
--actor-accent-ok: #{colors.$dark-actor-accent-ok};
--actor-accent2-ok: #{colors.$dark-actor-accent2-ok};
}
.actors-tabs button {
--actor-accent-ok: #{colors.$dark-actor-accent-ok};
--actor-accent2-ok: #{colors.$dark-actor-accent2-ok};
}
.toot-content {
--actor-accent-ok: #{colors.$dark-actor-accent-ok};
--actor-accent2-ok: #{colors.$dark-actor-accent2-ok};
}
} @else {
--bg0: #{colors.$bg0};
--bg1: #{colors.$bg1};
--bg2: #{colors.$bg2};
--bg3: #{colors.$bg3};
--bg4: #{colors.$bg4};
--fg0: #{colors.$fg0};
--fg1: #{colors.$fg1};
--fg2: #{colors.$fg2};
--fg-inv: #{colors.$fg-inv};
--menu-bg: #{colors.$menu-bg};
--menu-fg: #{colors.$menu-fg};
--menu-fg-active: #{colors.$menu-fg-active};
--menu-icon: #{colors.$menu-icon};
--menu-filter-active: #{colors.$menu-filter-active};
--panel-close: #{colors.$panel-close};
--panel-close-hover: #{colors.$panel-close-hover};
--accent: #{colors.$accent};
--accent-dark: #{colors.$accent-dark};
--accent-light: #{colors.$accent-light};
--accent-light2: #{colors.$accent-light2};
--accent-light3: #{colors.$accent-light3};
--overlay-icon: #{colors.$overlay-icon};
--overlay-icon-hover: #{colors.$overlay-icon-hover};
--overlay-backdrop: #{colors.$overlay-backdrop};
--menu-backdrop: #{colors.$menu-backdrop};
--bg-input: #{colors.$bg-input};
--bg-input-hover: #{colors.$bg-input-hover};
--bg-input-focus: #{colors.$bg-input-focus};
--bg-button: #{colors.$bg-button};
--bg-button-hover: #{colors.$bg-button-hover};
--button-svg: #{colors.$button-svg};
--button-svg-hover: #{colors.$button-svg-hover};
--button-svg-focus: #{colors.$button-svg-focus};
--button-svg-active: #{colors.$button-svg-active};
--fg-button-focus: #{colors.$fg-button-focus};
--fg-button-active: #{colors.$fg-button-active};
--posts-count: #{colors.$posts-count};
--selection-text: #{colors.$selection-text};
--selection-bg: #{colors.$selection-bg};
--stripe1: #{colors.$stripe1};
--stripe2: #{colors.$stripe2};
--stripe-fg: #{colors.$stripe-fg};
--private-post-bg: #{colors.$private-post-bg};
--private-post-border: #{colors.$private-post-border};
.actors-wrapper {
--actor-hue: 0;
--actor-bg0: #{colors.$actor-bg0};
--actor-bg0-ok: #{colors.$actor-bg0-ok};
--actor-bg1: #{colors.$actor-bg1};
--actor-bg1-ok: #{colors.$actor-bg1-ok};
--actor-bg2: #{colors.$actor-bg2};
--actor-bg2-ok: #{colors.$actor-bg2-ok};
--actor-bg3: #{colors.$actor-bg3};
--actor-bg3-ok: #{colors.$actor-bg3-ok};
--actor-bg4: #{colors.$actor-bg4};
--actor-bg4-ok: #{colors.$actor-bg4-ok};
--actor-fg0: #{colors.$actor-fg0};
--actor-fg1: #{colors.$actor-fg1};
--actor-fg1-ok: #{colors.$actor-fg1-ok};
--actor-tabs-bg: #{colors.$actor-tabs-bg};
--actor-accent: #{colors.$actor-accent};
--actor-accent-ok: #{colors.$actor-accent-ok};
--actor-accent2-ok: #{colors.$actor-accent2-ok};
}
.actors-tabs button {
--actor-accent-ok: #{colors.$actor-accent-ok};
--actor-accent2-ok: #{colors.$actor-accent2-ok};
}
.toot-content {
--actor-accent-ok: #{colors.$actor-accent-ok};
--actor-accent2-ok: #{colors.$actor-accent2-ok};
}
}
}

View File

@ -181,27 +181,3 @@ $dark-stripe-fg: $dark-fg0;
$dark-private-post-bg: $dark-bg2;
$dark-private-post-border: $dark-accent-dark;
@import "colors-mixins";
html {
@include theme-vars();
@media (prefers-color-scheme: dark) {
color-scheme: dark;
@include theme-vars("dark");
}
}
html.light {
color-scheme: light;
@include theme-vars();
}
html.dark {
color-scheme: dark;
@include theme-vars("dark");
}

View File

@ -1,3 +1,6 @@
@use 'mixins';
@use 'colors-mixins';
/* mini-reset */
*,
*::before,
@ -26,6 +29,27 @@
}
}
/* color schemes */
html {
@include colors-mixins.theme-vars();
@media (prefers-color-scheme: dark) {
color-scheme: dark;
@include colors-mixins.theme-vars('dark');
}
}
html.light {
color-scheme: light;
@include colors-mixins.theme-vars();
}
html.dark {
color-scheme: dark;
@include colors-mixins.theme-vars('dark');
}
/* global rules */
body {
@ -158,14 +182,14 @@ input {
}
}
input[type="number"] {
input[type='number'] {
width: 6ch;
text-align: center;
-moz-appearance: textfield;
appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
@ -219,7 +243,7 @@ details {
// focusable elements
*:focus-visible,
.tags-group button:focus-visible div,
.toots-filter:has([type="checkbox"]:focus-visible) {
.toots-filter:has([type='checkbox']:focus-visible) {
text-decoration: none;
outline: 2px solid var(--accent-light);
outline-offset: 2px;
@ -229,7 +253,7 @@ details {
}
.visually-hidden:not(:focus):not(:active) {
@include visually-hidden();
@include mixins.visually-hidden();
}
.nojs {

View File

@ -1,3 +1,5 @@
@use "mixins";
html {
width: 100dvw;
height: 100dvh;
@ -100,7 +102,7 @@ html {
.direction-fwd,
.paging-options-toggle {
.btn-label {
@include visually-hidden();
@include mixins.visually-hidden();
}
}
}
@ -194,7 +196,7 @@ html {
.direction-fwd,
.paging-options-toggle {
.btn-label {
@include visually-hidden();
@include mixins.visually-hidden();
}
}
}

View File

@ -1,3 +1,5 @@
@use "mixins";
$meta-visible: 100ch;
.toot {
@ -45,7 +47,7 @@ $meta-visible: 100ch;
var(--stripe2) 10px,
var(--stripe2) 20px
);
@include box-shadow-inner();
@include mixins.box-shadow-inner();
border-radius: 0.5rem;
@media (forced-colors: active) {
outline: 1px solid Highlight;
@ -226,7 +228,7 @@ $meta-visible: 100ch;
padding: 1rem;
background-color: var(--bg2);
border-radius: 0.5rem;
@include box-shadow-inner();
@include mixins.box-shadow-inner();
& + .att-sound {
margin-top: 1rem;
@ -286,7 +288,7 @@ $meta-visible: 100ch;
padding: 1rem;
background-color: var(--bg2);
border-radius: 0.5rem;
@include box-shadow-inner();
@include mixins.box-shadow-inner();
@media (forced-colors: active) {
border: 1px solid CanvasText;
@ -342,7 +344,7 @@ $meta-visible: 100ch;
container-type: inline-size;
background: var(--bg2);
border-radius: 0.5rem;
@include box-shadow-inner();
@include mixins.box-shadow-inner();
& > span {
margin-bottom: 0.25rem;
@ -474,7 +476,7 @@ $meta-visible: 100ch;
details[open] {
background-color: var(--bg2);
@include box-shadow-inner();
@include mixins.box-shadow-inner();
@media (forced-colors: active) {
border: 1px solid CanvasText;

13
dev/src/css/main.scss Normal file
View File

@ -0,0 +1,13 @@
@use 'colors';
@use 'colors-mixins';
@use 'mixins';
@use 'global';
@use 'layout';
@use 'welcome';
@use 'actor';
@use 'filters';
@use 'toot';
@use 'tags';
@use 'menu';
@use 'overlay';

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

26
dev/src/pages/index.astro Normal file
View File

@ -0,0 +1,26 @@
---
import '../css/main.scss';
import App from '../components/App.astro';
import SvgSprites from '../components/SvgSprites.astro';
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>MARL - Mastodon Archive Reader Lite</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="data:," />
<base target="_blank" />
</head>
<body>
<noscript>
<div class="nojs">JavaScript is required for this app to run.</div>
</noscript>
<App />
<SvgSprites />
<script src="/js/main.js" is:inline></script>
</body>
</html>

View File

Before

Width:  |  Height:  |  Size: 649 B

After

Width:  |  Height:  |  Size: 649 B

View File

Before

Width:  |  Height:  |  Size: 1018 B

After

Width:  |  Height:  |  Size: 1018 B

View File

Before

Width:  |  Height:  |  Size: 548 B

After

Width:  |  Height:  |  Size: 548 B

View File

Before

Width:  |  Height:  |  Size: 400 B

After

Width:  |  Height:  |  Size: 400 B

View File

Before

Width:  |  Height:  |  Size: 567 B

After

Width:  |  Height:  |  Size: 567 B

View File

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

View File

Before

Width:  |  Height:  |  Size: 529 B

After

Width:  |  Height:  |  Size: 529 B

View File

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View File

Before

Width:  |  Height:  |  Size: 860 B

After

Width:  |  Height:  |  Size: 860 B

View File

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 660 B

View File

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 500 B

View File

Before

Width:  |  Height:  |  Size: 649 B

After

Width:  |  Height:  |  Size: 649 B

View File

Before

Width:  |  Height:  |  Size: 603 B

After

Width:  |  Height:  |  Size: 603 B

View File

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 528 B

View File

Before

Width:  |  Height:  |  Size: 1018 B

After

Width:  |  Height:  |  Size: 1018 B

View File

Before

Width:  |  Height:  |  Size: 548 B

After

Width:  |  Height:  |  Size: 548 B

View File

Before

Width:  |  Height:  |  Size: 400 B

After

Width:  |  Height:  |  Size: 400 B

View File

Before

Width:  |  Height:  |  Size: 567 B

After

Width:  |  Height:  |  Size: 567 B

View File

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

View File

Before

Width:  |  Height:  |  Size: 455 B

After

Width:  |  Height:  |  Size: 455 B

View File

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 448 B

View File

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View File

Before

Width:  |  Height:  |  Size: 315 B

After

Width:  |  Height:  |  Size: 315 B

View File

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 337 B

View File

Before

Width:  |  Height:  |  Size: 529 B

After

Width:  |  Height:  |  Size: 529 B

View File

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View File

Before

Width:  |  Height:  |  Size: 860 B

After

Width:  |  Height:  |  Size: 860 B

View File

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 660 B

View File

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 500 B

View File

Before

Width:  |  Height:  |  Size: 649 B

After

Width:  |  Height:  |  Size: 649 B

View File

Before

Width:  |  Height:  |  Size: 603 B

After

Width:  |  Height:  |  Size: 603 B

View File

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 528 B

5
dev/tsconfig.json Normal file
View File

@ -0,0 +1,5 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}

1
dist/_astro/index.uwcihdcd.css vendored Normal file

File diff suppressed because one or more lines are too long

BIN
dist/_astro/no-avatar.DeffBVR2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
dist/_astro/no-header.DKXzxkmt.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

47
dist/index.html vendored Normal file

File diff suppressed because one or more lines are too long

5
dist/js/alpinejs.min.js vendored Normal file

File diff suppressed because one or more lines are too long

13
dist/js/jszip.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1561
dist/js/main.js vendored Normal file

File diff suppressed because it is too large Load Diff

1688
index.html

File diff suppressed because it is too large Load Diff

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