diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9da0969..25ec282d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,12 +17,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 # workaround for npm registry key change # ref. `pnpm@10.1.0` / `pnpm@9.15.4` cannot be installed due to key id mismatch · Issue #612 · nodejs/corepack # - https://github.com/nodejs/corepack/issues/612#issuecomment-2629496091 - run: npm i -g corepack@latest && corepack enable - - uses: actions/setup-node@v4.4.0 + - uses: actions/setup-node@v6.0.0 with: node-version-file: .nvmrc diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 47793600..c5689d9c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -16,7 +16,7 @@ jobs: packages: write steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Docker meta id: metal uses: docker/metadata-action@v5 diff --git a/.github/workflows/provenance.yml b/.github/workflows/provenance.yml new file mode 100644 index 00000000..b94dd6b0 --- /dev/null +++ b/.github/workflows/provenance.yml @@ -0,0 +1,22 @@ +name: ci + +on: + push: + branches: + - main + pull_request: + branches: + - main +permissions: + contents: read +jobs: + check-provenance: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Check provenance downgrades + uses: danielroe/provenance-action@41bcc969e579d9e29af08ba44fcbfdf95cee6e6c # v0.1.1 + with: + fail-on-provenance-change: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b733190d..f2bed5c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,12 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - name: Set node - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: .nvmrc diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml index f3e6813a..6948034e 100644 --- a/.github/workflows/semantic-pull-request.yml +++ b/.github/workflows/semantic-pull-request.yml @@ -19,6 +19,6 @@ jobs: name: Semantic Pull Request steps: - name: Validate PR title - uses: amannn/action-semantic-pull-request@v5.5.3 + uses: amannn/action-semantic-pull-request@v6.1.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 5e134baa..4116c624 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ dist .output .pnpm-store .nuxt +.data .env .DS_Store .idea/ diff --git a/.npmrc b/.npmrc deleted file mode 100644 index c6d7600b..00000000 --- a/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -shamefully-hoist=true -shell-emulator=true -ignore-workspace-root-check=true -package-manager-strict=false diff --git a/.nvmrc b/.nvmrc index 8fdd954d..b009dfb9 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22 \ No newline at end of file +lts/* diff --git a/.stackblitz/codeflow.json b/.stackblitz/codeflow.json deleted file mode 100644 index 21acb9d4..00000000 --- a/.stackblitz/codeflow.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "bot": { - "issues": { - "trigger": "all-issues" - } - } -} diff --git a/.stackblitzrc b/.stackblitzrc deleted file mode 100644 index fdb11220..00000000 --- a/.stackblitzrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "installDependencies": true, - "startCommand": "npm run dev:mocked" -} diff --git a/Dockerfile b/Dockerfile index 64974e1f..12661f15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,13 +17,12 @@ RUN apk add git --no-cache # Prepare build deps ( ignore postinstall scripts for now ) COPY package.json ./ -COPY .npmrc ./ COPY pnpm-lock.yaml ./ -COPY patches ./patches RUN pnpm i --frozen-lockfile --ignore-scripts # Copy all source files COPY . ./ +RUN pnpm nuxt prepare # Run full install with every postinstall script ( This needs project file ) RUN pnpm i --frozen-lockfile diff --git a/README.md b/README.md index 20cca13d..96a947d2 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,36 @@ -

- - Elk logo - -

+# Yolk -

Yolk

-

-Yolk is cusom fork of Elk, a nimble Mastodon client -

+Hi! Yolk is my custom fork of [Elk](https://github.com/elk-zon/elk), a nimble Mastodon client. -
-

- discord chat - Open board on Volta -

-
+I [decided](https://social.ayco.io/@ayo/114921112446517000) to have a personal fork of Elk because I really like the cross-account functionalities I use it for (e.g., I can open the Explore tab of my fosstodon account, then engage in a post with my self-hosted account, etc)... but I find sometimes I want to change little things which will make the app a bit more opinionated on my tastes (e.g., icons, colors, spacing, etc)... and some behavioral features. -

- - Elk screenshots - -

+I think doing this will make me use it as my main app daily. I have been switching between multiple apps because each one have strengths & weaknesses of their own. + +Crucial fixes (if I find them), quality of life improvements, and mastodon API feature parity will still go upstream to the main Elk project. + +~ Ayo Ayco + +## Docker + +An official docker image is published in: https://hub.docker.com/repository/docker/ayoayco/yolk + +For local development, [setup docker](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04). + +To build and publish: + +```bash +docker build -t ayoayco/yolk . +docker push ayoayco/yolk + +# or +npm run build:docker +npm run publish:docker +``` + +## Official deployment +- 🥚 WIP: [yolk.ayo.run](https://yolk.ayo.run) + +--- ## ⚠️ Elk is in Alpha @@ -44,61 +54,13 @@ One could put Elk behind popular reverse proxies with SSL Handling like Traefik, 1. got into new source dir: ```cd elk``` 1. create local storage directory for settings: ```mkdir elk-storage``` 1. adjust permissions of storage dir: ```sudo chown 911:911 ./elk-storage``` -1. start container: ```docker-compose up --build -d``` +1. start container: ```docker compose up --build -d``` > [!NOTE] > The provided Dockerfile creates a container which will eventually run Elk as non-root user and create a persistent named Docker volume upon first start (if that volume does not yet exist). This volume is always created with root permission. Failing to change the permissions of ```/elk/data``` inside this volume to UID:GID 911 (as specified for Elk in the Dockerfile) will prevent Elk from storing it's config for user accounts. You either have to fix the permission in the created named volume, or mount a directory with the correct permission to ```/elk/data``` into the container. -### Ecosystem - -These are known deployments using Elk as an alternative Web client for Mastodon servers or as a base for other projects in the fediverse: - -- [elk.fedified.com](https://elk.fedified.com) - Use Elk to log into any compatible instance -- [elk.mastodon.com.pl](https://elk.mastodon.com.pl) - Use Elk for the `mastodon.com.pl` Server -- [elk.me.uk](https://elk.me.uk) - Use Elk to log into any compatible instance, hosted on Google Cloud Run with no Cloudflare proxy -- [elk.h4.io](https://elk.h4.io) - Use Elk for the `h4.io` Server -- [elk.universeodon.com](https://elk.universeodon.com) - Use Elk for the Universeodon Server -- [elk.vmst.io](https://elk.vmst.io) - Use Elk for the `vmst.io` Server -- [elk.hostux.social](https://elk.hostux.social) - Use Elk for the `hostux.social` Server -- [elk.cupoftea.social](https://elk.cupoftea.social) - Use Elk for the `cupoftea.social` Server -- [elk.aus.social](https://elk.aus.social) - Use Elk for the `aus.social` Server -- [elk.mstdn.ca](https://elk.mstdn.ca) - Use Elk for the `mstdn.ca` Server -- [elk.mastodonapp.uk](https://elk.mastodonapp.uk) - Use Elk for the `mastodonapp.uk` Server -- [elk.bolha.us](https://elk.bolha.us) - Use Elk for the `bolha.us` Server -- [crab.bumscode.com](https://crab.bumscode.com) - Use [crab](https://github.com/maybeanerd/crab) - a soft fork of Elk - for the `bumscode.com` Server - > **Note**: Community deployments are **NOT** maintained by the Elk team. It may not be synced with Elk's source code. Please do your own research about the host servers before using them. -## 💖 Sponsors - -We are grateful for the generous sponsorship and help of: - - - NuxtLabs - -

- - StackBlitz - -

- -And all the companies and individuals sponsoring Elk Team and the members. If you're enjoying the app, consider sponsoring us: - -- [Elk Team's GitHub Sponsors](https://github.com/sponsors/elk-zone) - -Or you can sponsor our core team members individually: - -- [Anthony Fu](https://github.com/sponsors/antfu) -- [Daniel Roe](https://github.com/sponsors/danielroe) -- [三咲智子 Kevin Deng](https://github.com/sponsors/sxzz) -- [Patak](https://github.com/sponsors/patak-dev) - -We would also appreciate sponsoring other contributors to the Elk project. If someone helps you solve an issue or implement a feature you wanted, supporting them would help make this project and OS more sustainable. - -## 📍 Roadmap - -[Open board on Volta](https://volta.net/elk-zone/elk) - ## 🧑‍💻 Contributing We're really excited that you're interested in contributing to Elk! Before submitting your contribution, please read through the following guide. diff --git a/app/augments.d.ts b/app/augments.d.ts new file mode 100644 index 00000000..e94466b0 --- /dev/null +++ b/app/augments.d.ts @@ -0,0 +1,19 @@ +export {} + +declare module '#app' { + interface PageMeta { + wideLayout?: boolean + } + + interface RuntimeNuxtHooks { + 'elk-logo:click': () => void + } +} + +declare global { + namespace NodeJS { + interface Process { + mock?: Record + } + } +} diff --git a/app/components/main/MainContent.vue b/app/components/main/MainContent.vue index 0c50edc3..8f45d773 100644 --- a/app/components/main/MainContent.vue +++ b/app/components/main/MainContent.vue @@ -30,12 +30,11 @@ const containerClass = computed(() => { sticky top-0 z-20 pt="[env(safe-area-inset-top,0)]" bg="[rgba(var(--rgb-bg-base),0.7)]" - class="native:lg:w-[calc(100vw-5rem)] native:xl:w-[calc(135%+(100vw-1200px)/2)]" :class="{ 'backdrop-blur': !getPreferences(userSettings, 'optimizeForLowPerformanceDevice'), }" > -
+
-
+
diff --git a/app/components/modal/DurationPicker.vue b/app/components/modal/DurationPicker.vue index 897dcd02..90614ae9 100644 --- a/app/components/modal/DurationPicker.vue +++ b/app/components/modal/DurationPicker.vue @@ -13,9 +13,9 @@ watchEffect(() => { } const duration - = days.value * 24 * 60 * 60 - + hours.value * 60 * 60 - + minutes.value * 60 + = days.value * 24 * 60 * 60 + + hours.value * 60 * 60 + + minutes.value * 60 if (duration <= 0) { isValid.value = false diff --git a/app/components/modal/ModalContainer.vue b/app/components/modal/ModalContainer.vue index 28f43b62..a8fb7896 100644 --- a/app/components/modal/ModalContainer.vue +++ b/app/components/modal/ModalContainer.vue @@ -82,7 +82,7 @@ function handleFavouritedBoostedByClose() { > - + diff --git a/app/components/modal/ModalDialog.vue b/app/components/modal/ModalDialog.vue index d6ae88d2..d88836a9 100644 --- a/app/components/modal/ModalDialog.vue +++ b/app/components/modal/ModalDialog.vue @@ -10,6 +10,7 @@ const { closeByMask = true, useVIf = true, keepAlive = false, + focusFirstElement = true, } = defineProps<{ // level of depth zIndex?: number @@ -21,6 +22,8 @@ const { keepAlive?: boolean // The aria-labelledby id for the dialog. dialogLabelledBy?: string + // Whether to focus on the first element when the modal opens. + focusFirstElement?: boolean }>() const emit = defineEmits<{ @@ -45,6 +48,7 @@ const { activate } = useFocusTrap(elDialogRoot, { escapeDeactivates: true, preventScroll: true, returnFocusOnDeactivate: true, + initialFocus: focusFirstElement ? undefined : false, }) defineExpose({ diff --git a/app/components/modal/ModalMediaPreviewCarousel.vue b/app/components/modal/ModalMediaPreviewCarousel.vue index 43eb6f7d..2e56e608 100644 --- a/app/components/modal/ModalMediaPreviewCarousel.vue +++ b/app/components/modal/ModalMediaPreviewCarousel.vue @@ -159,14 +159,16 @@ function handleTap([positionX, positionY]: Vector2) { goToFocusedSlide() } else { - const focusedSlideBounding = slide.value[modelValue.value].getBoundingClientRect() - const slideCenterX = focusedSlideBounding.left + focusedSlideBounding.width / 2 - const slideCenterY = focusedSlideBounding.top + focusedSlideBounding.height / 2 + const focusedSlideBounding = slide.value[modelValue.value]?.getBoundingClientRect() + if (focusedSlideBounding) { + const slideCenterX = focusedSlideBounding.left + focusedSlideBounding.width / 2 + const slideCenterY = focusedSlideBounding.top + focusedSlideBounding.height / 2 - scale.value = 3 - x.value += positionX - slideCenterX - y.value += positionY - slideCenterY - restrictShiftToInsideSlide() + scale.value = 3 + x.value += positionX - slideCenterX + y.value += positionY - slideCenterY + restrictShiftToInsideSlide() + } } } diff --git a/app/components/nav/NavLogo.vue b/app/components/nav/NavLogo.vue index 6fc5549d..5bb827a0 100644 --- a/app/components/nav/NavLogo.vue +++ b/app/components/nav/NavLogo.vue @@ -2,47 +2,13 @@ diff --git a/app/components/nav/NavTitle.vue b/app/components/nav/NavTitle.vue index 630f425c..40306b7a 100644 --- a/app/components/nav/NavTitle.vue +++ b/app/components/nav/NavTitle.vue @@ -18,7 +18,7 @@ router.afterEach(() => {