Merge branch 'main' of https://git.ayo.run/ayo/yolk into dev

This commit is contained in:
Ayo Ayco 2025-11-23 18:37:22 +01:00
commit c33b595008
136 changed files with 11792 additions and 10503 deletions

View file

@ -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

View file

@ -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

22
.github/workflows/provenance.yml vendored Normal file
View file

@ -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

View file

@ -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

View file

@ -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 }}

1
.gitignore vendored
View file

@ -4,6 +4,7 @@ dist
.output
.pnpm-store
.nuxt
.data
.env
.DS_Store
.idea/

4
.npmrc
View file

@ -1,4 +0,0 @@
shamefully-hoist=true
shell-emulator=true
ignore-workspace-root-check=true
package-manager-strict=false

2
.nvmrc
View file

@ -1 +1 @@
22
lts/*

View file

@ -1,7 +0,0 @@
{
"bot": {
"issues": {
"trigger": "all-issues"
}
}
}

View file

@ -1,4 +0,0 @@
{
"installDependencies": true,
"startCommand": "npm run dev:mocked"
}

View file

@ -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

100
README.md
View file

@ -1,26 +1,36 @@
<p align="center">
<a href="https://elk.zone" target="_blank" rel="noopener noreferrer">
<img width="160" height="160" src="./public/logo.svg" alt="Elk logo">
</a>
</p>
# Yolk
<h1 align="center"/>Yolk</h1>
<p align="center">
Yolk is cusom fork of Elk, a nimble Mastodon client
</p>
Hi! Yolk is my custom fork of [Elk](https://github.com/elk-zon/elk), a nimble Mastodon client.
<br/>
<p align="center">
<a href="https://chat.elk.zone"><img src="https://img.shields.io/badge/chat-discord-blue?style=flat&logo=discord" alt="discord chat"></a>
<a href="https://volta.net/elk-zone/elk?utm_source=elk_readme"><img src="https://user-images.githubusercontent.com/904724/209143798-32345f6c-3cf8-4e06-9659-f4ace4a6acde.svg" alt="Open board on Volta"></a>
</p>
<br/>
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.
<p align="center">
<a href="https://elk.zone/" target="_blank" rel="noopener noreferrer" >
<img src="./public/elk-og.png" alt="Elk screenshots" width="600" height="auto">
</a>
</p>
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:
<a href="https://nuxtlabs.com/" target="_blank" rel="noopener noreferrer" >
<img src="./images/nuxtlabs.svg" alt="NuxtLabs" height="85">
</a>
<br><br>
<a href="https://stackblitz.com/" target="_blank" rel="noopener noreferrer" >
<img src="./images/stackblitz.svg" alt="StackBlitz" height="85">
</a>
<br><br>
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.

19
app/augments.d.ts vendored Normal file
View file

@ -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<string, any>
}
}
}

View file

@ -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'),
}"
>
<div flex justify-between gap-2 min-h-53px px5 py1 :class="{ 'xl:hidden': $route.name !== 'tag' }" class="native:xl:flex" border="b base">
<div flex justify-between gap-2 min-h-53px px5 py1 :class="{ 'xl:hidden': $route.name !== 'tag' }" border="b base">
<div flex gap-2 items-center :overflow-hidden="!noOverflowHidden ? '' : false" w-full>
<button
v-if="backOnSmallScreen || back"
@ -45,7 +44,7 @@ const containerClass = computed(() => {
>
<div text-lg i-ri:arrow-left-line class="rtl-flip" />
</button>
<div :truncate="!noOverflowHidden ? '' : false" flex w-full data-tauri-drag-region class="native-mac:justify-start native-mac:text-center">
<div :truncate="!noOverflowHidden ? '' : false" flex w-full class="native-mac:justify-start native-mac:text-center">
<slot name="title" />
</div>
<div sm:hidden h-7 w-1px />

View file

@ -82,7 +82,7 @@ function handleFavouritedBoostedByClose() {
>
<ModalMediaPreview v-if="isMediaPreviewOpen" @close="closeMediaPreview()" />
</ModalDialog>
<ModalDialog v-model="isEditHistoryDialogOpen" max-w-125>
<ModalDialog v-model="isEditHistoryDialogOpen" :focus-first-element="false" max-w-125>
<StatusEditPreview v-if="statusEdit" :edit="statusEdit" />
</ModalDialog>
<ModalDialog v-model="isCommandPanelOpen" max-w-fit flex>

View file

@ -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({

View file

@ -159,7 +159,8 @@ function handleTap([positionX, positionY]: Vector2) {
goToFocusedSlide()
}
else {
const focusedSlideBounding = slide.value[modelValue.value].getBoundingClientRect()
const focusedSlideBounding = slide.value[modelValue.value]?.getBoundingClientRect()
if (focusedSlideBounding) {
const slideCenterX = focusedSlideBounding.left + focusedSlideBounding.width / 2
const slideCenterY = focusedSlideBounding.top + focusedSlideBounding.height / 2
@ -168,6 +169,7 @@ function handleTap([positionX, positionY]: Vector2) {
y.value += positionY - slideCenterY
restrictShiftToInsideSlide()
}
}
}
function handleSwipe([horiz, vert]: Vector2, [movementX, movementY]: Vector2) {

View file

@ -2,47 +2,13 @@
</script>
<template>
<span shrink-0 aspect="1/1" sm:h-8 xl:h-10 class="rtl-flip"><svg
<span shrink-0 aspect="1/1" sm:h-8 xl:h-10 class="rtl-flip">
<!-- <svg
xmlns="http://www.w3.org/2000/svg" w-full
aspect="1/1" sm:h-8 xl:h-10 sm:w-8 xl:w-10 viewBox="0 0 250 250" fill="none"
>
<mask
id="a"
width="240"
height="234"
x="4"
y="1"
maskUnits="userSpaceOnUse"
style="mask-type:alpha"
>
<path
id="path19"
fill="#D9D9D9"
d="M244 123c0 64.617-38.383 112-103 112-64.617 0-103-30.883-103-95.5C38 111.194-8.729 36.236 8 16 29.46-9.959 88.689 6 125 6c64.617 0 119 52.383 119 117Z"
/>
</mask>
<g
id="g28"
mask="url(#a)"
transform="matrix(0.90923731,0,0,1.0049564,13.520015,-3.1040835)"
>
<path
id="path22"
class="body"
d="m 116.94,88.1 c -13.344,1.552 -20.436,-2.019 -24.706,10.71 0,0 14.336,21.655 52.54,21.112 -2.135,8.848 -1.144,15.368 -1.144,23.207 0,26.079 -20.589,48.821 -65.961,48.821 -23.03,0 -51.015,4.191 -72.367,15.911 -15.175,8.305 -27.048,20.336 -32.302,37.023 l 5.956,8.461 11.4,0.155 v 47.889 l -13.91,21.966 3.998,63.645 H -6.364 L -5.22,335.773 C 1.338,331.892 16.36,321.802 29.171,306.279 46.557,285.4 59.902,255.052 44.193,217.486 l 11.744,-5.045 c 12.887,30.814 8.388,57.514 -2.898,79.013 21.58,-0.698 40.11,-2.095 55.819,-4.734 l -3.584,-43.698 12.659,-1.087 L 129.98,387 h 13.116 l 2.212,-94.459 c 10.447,-4.502 34.239,-21.034 45.372,-78.47 1.372,-6.986 2.135,-12.885 2.516,-17.93 1.754,-12.806 2.745,-27.243 3.051,-43.698 l -18.683,-5.976 h 57.42 l 5.567,-12.807 c -5.414,0.233 -11.896,-2.639 -11.896,-2.639 l 1.297,-6.209 H 242 L 176.801,90.428 c -7.244,2.794 -14.87,6.442 -20.208,10.866 -4.27,-3.105 -19.063,-12.807 -39.653,-13.195 z"
/>
<path
id="path24"
class="wood"
d="M 6.217,24.493 18.494,21 c 5.948,21.577 13.345,33.375 22.648,39.352 8.388,5.099 19.75,5.239 31.799,4.579 C 69.433,63.767 66.154,62.137 63.104,59.886 56.317,54.841 50.522,46.458 46.175,31.246 l 12.201,-3.649 c 3.279,11.488 7.092,18.085 12.201,21.888 5.11,3.726 11.286,4.657 18.606,5.433 13.726,1.553 30.884,2.174 52.312,12.264 2.898,1.086 5.872,2.483 8.769,4.036 -0.381,-0.776 -0.762,-1.553 -1.296,-2.406 -3.66,-5.822 -10.828,-11.953 -24.097,-16.92 l 4.27,-12.109 c 21.581,7.917 30.121,19.171 33.553,28.097 3.965,10.168 1.525,18.124 1.525,18.124 -3.05,1.009 -6.1,2.406 -9.608,3.492 -6.634,-4.579 -12.887,-8.033 -18.835,-10.75 C 113.814,70.442 92.31,76.108 73.246,77.893 58.91,79.213 45.794,78.591 34.432,71.295 23.222,64.155 13.385,50.495 6.217,24.493 Z"
/>
<path
id="path26"
class="wood"
d="M 90.098,45.294 C 87.582,39.55 86.057,32.487 86.743,23.794 l 12.659,0.932 c -0.763,10.555 2.897,17.696 7.015,22.353 -5.338,-0.931 -10.447,-1.04 -16.319,-1.785 z m 80.069,-1.32 8.312,-9.702 c 21.58,19.094 8.159,46.415 8.159,46.415 l -11.819,-1.32 c -0.382,-6.24 -1.144,-17.836 -6.635,-24.371 3.584,1.84 6.635,3.865 9.99,6.908 0,-5.666 -1.754,-12.341 -8.007,-17.93 z"
/>
</g>
</svg>
> -->
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><defs><mask id="ipTEgg0"><g fill="none" stroke="#fff" stroke-width="4"><circle cx="24" cy="24" r="10" fill="#555555" stroke-linecap="round" stroke-linejoin="round" /><path d="M44 24c0 2.633-.508 5.146-1.433 7.448c-.936 2.331-4.129.071-7.346 3.521c-3.216 3.45-.71 6.267-3.204 7.36A19.9 19.9 0 0 1 24 44C12.954 44 4 35.046 4 24S12.954 4 24 4s20 8.954 20 20Z" /><path stroke-linecap="round" d="M20 25s.21 1.21 1 2s2 1 2 1" /></g></mask></defs><path fill="#ff8d00" d="M0 0h48v48H0z" mask="url(#ipTEgg0)" /></svg>
</span>
</template>

View file

@ -18,7 +18,7 @@ router.afterEach(() => {
</script>
<template>
<div flex justify-between sticky top-0 bg-base z-1 py-4 native:py-7 data-tauri-drag-region>
<div flex justify-between sticky top-0 bg-base z-1 py-4>
<NuxtLink
flex items-end gap-3
py2 px-5
@ -29,8 +29,9 @@ router.afterEach(() => {
@click.prevent="onClickLogo"
>
<NavLogo shrink-0 aspect="1/1" sm:h-8 xl:h-10 class="rtl-flip" />
<div v-show="isHydrated" hidden xl:block text-secondary>
{{ $t('app_name') }} <sup text-sm italic mt-1>{{ env === 'release' ? 'alpha' : env }}</sup>
<div v-show="isHydrated" hidden xl:block>
<span pr-1>{{ $t('app_name') }}</span>
<sup text-sm italic mt-1>{{ env === 'release' ? 'alpha' : env }}</sup>
</div>
</NuxtLink>
<div hidden xl:flex items-center me-6 mt-2 gap-1>

View file

@ -6,14 +6,21 @@ const isSlow = computed(() => isSupported.value && effectiveType.value && ['slow
const limit = computed(() => isSlow.value ? 10 : 30)
const paginator = useMastoClient().v1.timelines.home.list({ limit: limit.value })
const stream = useStreaming(client => client.user.subscribe())
// streaming requires user session
let stream: Ref<mastodon.streaming.Subscription | undefined>
if (currentUser.value !== undefined)
stream = useStreaming(client => client.user.subscribe())
function reorderAndFilter(items: mastodon.v1.Status[]) {
return reorderedTimeline(items, 'home')
}
let followedTags: mastodon.v1.Tag[] | undefined
let followedTags: mastodon.v1.Tag[]
if (currentUser.value !== undefined) {
followedTags = (await useMasto().client.value.v1.followedTags.list({ limit: 0 }))
const { client } = useMasto()
const paginator = client.value.v1.followedTags.list()
followedTags = (await paginator.values().next()).value ?? []
}
</script>

View file

@ -13,7 +13,12 @@ const options = { limit: 30, types: filter ? [filter] : [] }
// Default limit is 20 notifications, and servers are normally caped to 30
const paginator = useMastoClient().v1.notifications.list(options)
const stream = useStreaming(client => client.user.notification.subscribe())
// streaming requires user session
let stream: Ref<mastodon.streaming.Subscription | undefined>
if (currentUser.value !== undefined)
// @ts-expect-error Type error should be fixed with the following PR to masto.js: https://github.com/neet/masto.js/pull/1355
stream = useStreaming(client => client.user.notification.subscribe())
lastAccessedNotificationRoute.value = route.path.replace(/\/notifications\/?/, '')

View file

@ -2,14 +2,21 @@
import type { mastodon } from 'masto'
const paginator = useMastoClient().v1.timelines.public.list({ limit: 30 })
const stream = useStreaming(client => client.public.subscribe())
// streaming requires user session
let stream: Ref<mastodon.streaming.Subscription | undefined>
if (currentUser.value !== undefined)
stream = useStreaming(client => client.public.subscribe())
function reorderAndFilter(items: mastodon.v1.Status[]) {
return reorderedTimeline(items, 'public')
}
let followedTags: mastodon.v1.Tag[] | undefined
let followedTags: mastodon.v1.Tag[]
if (currentUser.value !== undefined) {
followedTags = (await useMasto().client.value.v1.followedTags.list({ limit: 0 }))
const { client } = useMasto()
const paginator = client.value.v1.followedTags.list()
followedTags = (await paginator.values().next()).value ?? []
}
</script>

View file

@ -2,14 +2,21 @@
import type { mastodon } from 'masto'
const paginator = useMastoClient().v1.timelines.public.list({ limit: 30, local: true })
const stream = useStreaming(client => client.public.local.subscribe())
// streaming requires user session
let stream: Ref<mastodon.streaming.Subscription | undefined>
if (currentUser.value !== undefined)
stream = useStreaming(client => client.public.local.subscribe())
function reorderAndFilter(items: mastodon.v1.Status[]) {
return reorderedTimeline(items, 'public')
}
let followedTags: mastodon.v1.Tag[] | undefined
let followedTags: mastodon.v1.Tag[]
if (currentUser.value !== undefined) {
followedTags = (await useMasto().client.value.v1.followedTags.list({ limit: 0 }))
const { client } = useMasto()
const paginator = client.value.v1.followedTags.list()
followedTags = (await paginator.values().next()).value ?? []
}
</script>

View file

@ -74,7 +74,7 @@ export async function fetchAccountByHandle(acct: string): Promise<mastodon.v1.Ac
}
else {
const userAcctDomain = userAcct.includes('@') ? userAcct : `${userAcct}@${domain}`
account = (await client.v1.search.fetch({ q: `@${userAcctDomain}`, type: 'accounts' })).accounts[0]
account = (await client.v1.search.list({ q: `@${userAcctDomain}`, type: 'accounts' })).accounts[0]
}
if (account.acct && !account.acct.includes('@') && domain)

View file

@ -67,6 +67,11 @@ const sanitizer = sanitize({
li: {
value: keep,
},
// Hollo supports <ruby> tags
// https://github.com/fedify-dev/hollo/blob/80e7184aa805f579be8712ff9231be655343c661/src/xss.ts#L92-L94
ruby: {},
rp: {},
rt: {},
})
/**
@ -104,11 +109,12 @@ export function parseMastodonHTML(
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/`/g, '&#96;')
.replace(/\*/g, '&ast;')
const classes = lang ? ` class="language-${lang}"` : ''
return `><pre><code${classes}>${code}</code></pre>`
})
.replace(/`([^`\n]*)`/g, (_1, raw) => {
return raw ? `<code>${htmlToText(raw).replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code>` : ''
return raw ? `<code>${htmlToText(raw).replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\*/g, '&ast;')}</code>` : ''
})
}

View file

@ -26,12 +26,16 @@ export function mastoLogin(masto: ElkMasto, user: Pick<UserLogin, 'server' | 'to
const instance: ElkInstance = reactive(getInstanceCache(server) || { uri: server, accountDomain: server })
const accessToken = user.token
const createStreamingClient = (streamingApiUrl: string | undefined) => {
let createStreamingClient: (streamingApiUrl: string | undefined) => mastodon.streaming.Client | undefined
masto.client.value = createRestAPIClient({ url, accessToken })
if (currentUser.value !== undefined) {
createStreamingClient = (streamingApiUrl: string | undefined) => {
return streamingApiUrl ? createStreamingAPIClient({ streamingApiUrl, accessToken, implementation: globalThis.WebSocket }) : undefined
}
const streamingApiUrl = instance?.configuration?.urls?.streaming
masto.client.value = createRestAPIClient({ url, accessToken })
masto.streamingClient.value = createStreamingClient(streamingApiUrl)
// Refetch instance info in the background on login
@ -74,6 +78,7 @@ export function mastoLogin(masto: ElkMasto, user: Pick<UserLogin, 'server' | 'to
instanceStorage.value[server] = newInstance
})
}
return instance
}

View file

@ -45,9 +45,10 @@ export function useNotifications() {
const position = await client.value.v1.markers.fetch({ timeline: ['notifications'] })
const paginator = client.value.v1.notifications.list({ limit: 30 })
const paginatorValues = paginator.values()
do {
const result = await paginator.next()
const result = await paginatorValues.next()
if (!result.done && result.value.length) {
for (const notification of result.value) {
if (notification.id === position.notifications.lastReadId)

View file

@ -77,7 +77,7 @@ export function useSearch(query: MaybeRefOrGetter<string>, options: UseSearchOpt
...resolveUnref(options),
resolve: !!currentUser.value,
})
const nextResults = await paginator.next()
const nextResults = await paginator.values().next()
done.value = !!nextResults.done
if (!nextResults.done)
@ -91,7 +91,7 @@ export function useSearch(query: MaybeRefOrGetter<string>, options: UseSearchOpt
return
loading.value = true
const nextResults = await paginator.next()
const nextResults = await paginator.values().next()
loading.value = false
done.value = !!nextResults.done

View file

@ -3,7 +3,7 @@ import type { mastodon } from 'masto'
import type { Ref } from 'vue'
export function usePaginator<T, P, U = T>(
_paginator: mastodon.Paginator<T[], P>,
paginator: mastodon.Paginator<T[], P>,
stream: Ref<mastodon.streaming.Subscription | undefined>,
eventType: 'update' | 'notification' = 'update',
preprocess: (items: (T | U)[]) => U[] = items => items as unknown as U[],
@ -12,8 +12,8 @@ export function usePaginator<T, P, U = T>(
// called `next` method will mutate the internal state of the variable,
// and we need its initial state after HMR
// so clone it
const paginator = _paginator.clone()
const paginatorValues = paginator.values()
const state = ref<PaginatorState>(isHydrated.value ? 'idle' : 'loading')
const items = ref<U[]>([])
const nextItems = ref<U[]>([])
@ -75,7 +75,7 @@ export function usePaginator<T, P, U = T>(
state.value = 'loading'
try {
const result = await paginator.next()
const result = await paginatorValues.next()
if (!result.done && result.value.length) {
const preprocessedItems = preprocess([...nextItems.value, ...result.value] as (U | T)[])

View file

@ -17,6 +17,7 @@ export const shikiParser: Parser = (options) => {
return promise ?? []
if (!parser)
// @ts-expect-error will be fixed when shiki upgrades
parser = createParser(highlighter)
return parser(options)

View file

@ -28,7 +28,7 @@ export const TiptapMentionSuggestion: Partial<SuggestionOptions> = import.meta.s
return []
const paginator = useMastoClient().v2.search.list({ q: query, type: 'accounts', limit: 25, resolve: true })
return (await paginator.next()).value?.accounts ?? []
return (await paginator.values().next()).value?.accounts ?? []
},
render: createSuggestionRenderer(TiptapMentionList),
}
@ -47,7 +47,7 @@ export const TiptapHashtagSuggestion: Partial<SuggestionOptions> = {
resolve: false,
excludeUnreviewed: true,
})
return (await paginator.next()).value?.hashtags ?? []
return (await paginator.values().next()).value?.hashtags ?? []
},
render: createSuggestionRenderer(TiptapHashtagList),
}

View file

@ -1,6 +1,6 @@
import type { mastodon } from 'masto'
export const APP_NAME = 'Elk'
export const APP_NAME = 'Yolk'
export const DEFAULT_POST_CHARS_LIMIT = 500
export const DEFAULT_FONT_SIZE = '15px'

View file

@ -16,9 +16,9 @@ const instance = instanceStorage.value[currentServer.value]
</script>
<template>
<div h-full :data-mode="isHydrated && isGrayscale ? 'grayscale' : ''" data-tauri-drag-region>
<main flex w-full mxa lg:max-w-80rem class="native:grid native:sm:grid-cols-[auto_1fr] native:lg:grid-cols-[auto_minmax(600px,2fr)_1fr]">
<aside class="native:w-auto w-1/8 md:w-1/6 lg:w-1/5 xl:w-1/4 zen-hide" hidden sm:flex justify-end xl:me-4 native:me-0 relative>
<div h-full :data-mode="isHydrated && isGrayscale ? 'grayscale' : ''">
<main flex w-full mxa lg:max-w-80rem>
<aside class="w-1/8 md:w-1/6 lg:w-1/5 xl:w-1/4 zen-hide" hidden sm:flex justify-end xl:me-4 relative>
<div sticky top-0 w-20 xl:w-100 h-100dvh flex="~ col" lt-xl-items-center>
<slot name="left">
<div flex="~ col" overflow-y-auto justify-between h-full max-w-full overflow-x-hidden>
@ -60,7 +60,7 @@ const instance = instanceStorage.value[currentServer.value]
<NavBottom v-if="isHydrated" sm:hidden />
</div>
</div>
<aside v-if="isHydrated && !wideLayout" class="hidden lg:w-1/5 xl:w-1/4 sm:none xl:block native:w-full zen-hide">
<aside v-if="isHydrated && !wideLayout" class="hidden lg:w-1/5 xl:w-1/4 sm:none xl:block zen-hide">
<div sticky top-0 h-100dvh flex="~ col" gap-2 py3 ms-2>
<slot name="right">
<SearchWidget mt-4 mx-1 hidden xl:block />

View file

@ -44,7 +44,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
// If we're logged in, search for the local id the account or status corresponds to
const paginator = masto.client.value.v2.search.list({ q: `https:/${to.fullPath}`, resolve: true, limit: 1 })
const { accounts, statuses } = (await paginator.next()).value ?? { accounts: [], statuses: [] }
const { accounts, statuses } = (await paginator.values().next()).value ?? { accounts: [], statuses: [] }
if (statuses[0])
return getStatusRoute(statuses[0])

View file

@ -10,6 +10,6 @@ catch (err) {
<template>
<MainContent text-base grid gap-3 m3>
<img rounded-3 :src="instance.thumbnail.url">
<img v-if="instance !== undefined" rounded-3 :src="instance.thumbnail.url">
</MainContent>
</template>

View file

@ -1,4 +1,6 @@
<script setup lang="ts">
import type { mastodon } from 'masto'
definePageMeta({
name: 'list',
})
@ -9,7 +11,11 @@ const listId = computed(() => params.list as string)
const client = useMastoClient()
const paginator = client.v1.timelines.list.$select(listId.value).list()
const stream = useStreaming(client => client.list.subscribe({ list: listId.value }))
// streaming requires user session
let stream: Ref<mastodon.streaming.Subscription | undefined>
if (currentUser.value !== undefined)
stream = useStreaming(client => client.list.subscribe({ list: listId.value }))
</script>
<template>

View file

@ -12,7 +12,11 @@ const { client } = useMasto()
const { data: tag, refresh } = await useAsyncData(() => `tag-${tagName.value}`, () => client.value.v1.tags.$select(tagName.value).fetch(), { default: () => shallowRef() })
const paginator = client.value.v1.timelines.tag.$select(tagName.value).list()
const stream = useStreaming(client => client.hashtag.subscribe({ tag: tagName.value }))
// streaming requires user session
let stream: Ref<mastodon.streaming.Subscription | undefined>
if (currentUser.value !== undefined)
stream = useStreaming(client => client.hashtag.subscribe({ tag: tagName.value }))
if (tag.value) {
useHydratedHead({
@ -26,9 +30,11 @@ onReactivated(() => {
refresh()
})
let followedTags: mastodon.v1.Tag[] | undefined
let followedTags: mastodon.v1.Tag[]
if (currentUser.value !== undefined) {
followedTags = (await useMasto().client.value.v1.followedTags.list({ limit: 0 }))
const { client } = useMasto()
const paginator = client.value.v1.followedTags.list()
followedTags = (await paginator.values().next()).value ?? []
}
</script>

View file

@ -38,7 +38,7 @@ export default defineNuxtPlugin(({ $scrollToTop }) => {
}
whenever(logicAnd(isAuthenticated, notUsingInput, keys.c), defaultPublishDialog)
const instanceDomain = currentInstance.value ? getInstanceDomain(currentInstance.value) : 'm.webtoo.ls'
const instanceDomain = currentInstance.value ? getInstanceDomain(currentInstance.value) : 'social.ayco.io'
whenever(logicAnd(notUsingInput, useMagicSequence(['g', 'h'])), () => navigateTo('/home'))
whenever(logicAnd(isAuthenticated, notUsingInput, useMagicSequence(['g', 'n'])), () => navigateTo('/notifications'))
// TODO: always overridden by 'c' (compose) shortcut

View file

@ -19,11 +19,11 @@ export default defineNuxtPlugin(async (nuxt) => {
if (!supportLanguages.includes(lang.value))
userSettings.value.language = getDefaultLanguage(supportLanguages)
if (lang.value !== i18n.locale)
if (lang.value !== i18n.locale.value)
await setLocale(userSettings.value.language as Locale)
watch([lang, isHydrated], () => {
if (isHydrated.value && lang.value !== i18n.locale)
if (isHydrated.value && lang.value !== i18n.locale.value)
setLocale(lang.value)
}, { immediate: true })
}

View file

@ -5,7 +5,7 @@ export { version } from '../package.json'
/**
* Environment variable `PULL_REQUEST` provided by Netlify.
* @see {@link https://docs.netlify.com/configure-builds/environment-variables/#git-metadata}
* @see {@link https://docs.netlify.com/build/configure-builds/environment-variables/#git-metadata}
*
* Whether triggered by a GitHub PR
*/
@ -13,7 +13,7 @@ export const isPR = process.env.PULL_REQUEST === 'true'
/**
* Environment variable `BRANCH` provided by Netlify.
* @see {@link https://docs.netlify.com/configure-builds/environment-variables/#git-metadata}
* @see {@link https://docs.netlify.com/build/configure-builds/environment-variables/#git-metadata}
*
* Git branch
*/
@ -21,7 +21,7 @@ export const gitBranch = process.env.BRANCH
/**
* Environment variable `CONTEXT` provided by Netlify.
* @see {@link https://docs.netlify.com/configure-builds/environment-variables/#build-metadata}
* @see {@link https://docs.netlify.com/build/configure-builds/environment-variables/#build-metadata}
*
* Whether triggered by PR, `deploy-preview` or `dev`.
*/

View file

@ -75,13 +75,11 @@ export const countryLocaleVariants: Record<string, (LocaleObjectData & { country
const locales: LocaleObjectData[] = [
{
// @ts-expect-error en used as placeholder
code: 'en',
file: 'en.json',
name: 'English',
},
({
// @ts-expect-error ar used as placeholder
{
code: 'ar',
file: 'ar.json',
name: 'العربية',
@ -90,8 +88,8 @@ const locales: LocaleObjectData[] = [
const name = new Intl.PluralRules('ar-EG').select(choice)
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
},
} satisfies LocaleObjectData),
({
} satisfies LocaleObjectData,
{
code: 'ckb',
file: 'ckb.json',
name: 'کوردیی ناوەندی',
@ -100,8 +98,8 @@ const locales: LocaleObjectData[] = [
const name = new Intl.PluralRules('ckb').select(choice)
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
},
} satisfies LocaleObjectData),
({
} satisfies LocaleObjectData,
{
code: 'fa-IR',
file: 'fa-IR.json',
name: 'فارسی',
@ -110,9 +108,8 @@ const locales: LocaleObjectData[] = [
const name = new Intl.PluralRules('fa-IR').select(choice)
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
},
} satisfies LocaleObjectData),
} satisfies LocaleObjectData,
{
// @ts-expect-error ca used as placeholder
code: 'ca',
file: 'ca.json',
name: 'Català',
@ -153,7 +150,6 @@ const locales: LocaleObjectData[] = [
name: 'Nederlands',
},
{
// @ts-expect-error es used as placeholder
code: 'es',
file: 'es.json',
name: 'Español',
@ -207,7 +203,6 @@ const locales: LocaleObjectData[] = [
},
},
{
// @ts-expect-error pt used as placeholder
code: 'pt',
file: 'pt.json',
name: 'Português',
@ -242,6 +237,11 @@ const locales: LocaleObjectData[] = [
file: 'it-IT.json',
name: 'Italiano',
},
{
code: 'sv',
file: 'sv.json',
name: 'Svenska',
},
{
code: 'th-TH',
file: 'th-TH.json',
@ -283,7 +283,7 @@ function buildLocales() {
acc.push(data)
}
return acc
}, <LocaleObjectData[]>[])
}, [] as LocaleObjectData[])
return useLocales.sort((a, b) => a.code.localeCompare(b.code))
}
@ -313,7 +313,7 @@ export const datetimeFormats = Object.values(currentLocales).reduce((acc, data)
}
return acc
}, <DateTimeFormats>{})
}, {} as DateTimeFormats)
export const numberFormats = Object.values(currentLocales).reduce((acc, data) => {
const numberFormats = data.numberFormats
@ -345,7 +345,7 @@ export const numberFormats = Object.values(currentLocales).reduce((acc, data) =>
}
return acc
}, <NumberFormats>{})
}, {} as NumberFormats)
export const pluralRules = Object.values(currentLocales).reduce((acc, data) => {
const pluralRule = data.pluralRule
@ -355,4 +355,4 @@ export const pluralRules = Object.values(currentLocales).reduce((acc, data) => {
}
return acc
}, <PluralizationRules>{})
}, {} as PluralizationRules)

View file

@ -1,12 +1,15 @@
services:
elk:
redis:
image: redis:latest
yolk:
image: ayoayco/yolk:latest
build:
context: .
dockerfile: Dockerfile
volumes:
# make sure this directory has the same ownership as the elk user from the Dockerfile
# otherwise Elk will not be able to store configs for accounts
# e.q. mkdir ./elk-storage; sudo chown 911:911 ./elk-storage
# e.g., mkdir ./elk-storage; sudo chown 911:911 ./elk-storage
- './elk-storage:/elk/data'
ports:
- 5314:5314

View file

@ -1,35 +1,41 @@
export default defineAppConfig({
docus: {
seo: {
title: 'Elk',
description: 'A nimble Mastodon web client.',
image: 'https://docs.elk.zone/elk-screenshot.png',
socials: {
// twitter: 'elk_zone',
github: 'elk-zone/elk',
mastodon: {
label: 'Mastodon',
icon: 'IconMastodon',
href: 'https://elk.zone/@elk@webtoo.ls',
},
},
aside: {
level: 0,
exclude: [],
description: 'A nimble Mastodon web client with modern features and elegant design.',
},
header: {
logo: true,
showLinkIcon: true,
exclude: [],
title: 'Elk',
logo: {
alt: 'Elk',
light: '/logo.svg',
dark: '/logo.svg',
},
footer: {
iconLinks: [
},
socials: {
github: 'https://github.com/elk-zone/elk',
mastodon: 'https://elk.zone/@elk@webtoo.ls',
},
github: {
url: 'https://github.com/elk-zone/elk',
branch: 'main',
rootDir: 'docs',
},
toc: {
title: 'On this page',
bottom: {
title: 'Community',
links: [
{
href: 'https://nuxt.com',
icon: 'IconNuxtLabs',
icon: 'i-ph-shooting-star-duotone',
label: 'Star on GitHub',
to: 'https://github.com/elk-zone/elk',
target: '_blank',
},
{
href: 'https://m.webtoo.ls/@elk',
icon: 'IconMastodon',
icon: 'i-simple-icons-mastodon',
label: 'Follow on Mastodon',
to: 'https://elk.zone/@elk@webtoo.ls',
target: '_blank',
},
],
},

View file

@ -1,5 +0,0 @@
<template>
<AppLayout>
<NuxtPage />
</AppLayout>
</template>

View file

@ -0,0 +1,4 @@
/* Elk brand colors for light and dark modes */
:root {
--ui-primary: #f0943c;
}

View file

@ -1,9 +1,9 @@
<script setup lang="ts">
import type { TranslationStatus } from '../../types'
import type { TranslationStatus } from '../../../types'
const localesStatuses: TranslationStatus = await import('../../translation-status.json').then(m => m.default)
const localesStatuses: TranslationStatus = await import('../../../translation-status.json').then(m => m.default)
const totalReference = localesStatuses.en.total
const totalReference = localesStatuses.en!.total
type Tab = 'missing' | 'outdated'
@ -32,7 +32,7 @@ const missingEntries = computed<string[]>(() => {
if (hidden.value || !currentLocale.value || localeTab.value !== 'missing')
return []
return localesStatuses[locale.value].missing
return localesStatuses[locale.value]!.missing
})
const outdatedEntries = computed<string[]>(() => {

View file

@ -1,36 +0,0 @@
---
title: Elk
navigation: false
layout: page
---
::block-hero
---
cta:
- Read more
- /guide
secondary:
- Try it out →
- https://elk.zone
---
#title
Elk
#description
An in-progress, nimble Mastodon web client
#support
![Screenshot of Elk](/screenshot.png)
#extra
::list
- markdown support
- code blocks
- reordering and connecting posts in timelines
- multi account
- GitHub HTML cards
- and more...
::
::

View file

@ -1,3 +1,8 @@
---
title: Introduction
description: Get started with Elk, the nimble Mastodon web client.
---
# Introduction
## What is Elk?
@ -16,7 +21,7 @@ Elk provides some features not available through the standard Mastodon web app i
You can use Elk right in your browser.
On a mobile device, you can install the app to your home screen right from your browser for easy access.
(This is called a Progressive Web App, or [PWA](../80.pwa.md).)
(This is called a Progressive Web App, or [PWA](../pwa.md).)
Want to try it out?
Visit https://elk.zone, type in your Mastodon server address, then log in.
@ -50,7 +55,7 @@ Using a client, you can
- View, add, or participate in polls
- Follow, unfollow, mute, and block accounts
::alert{type="info"}
::callout{type="info"}
**Note:** Not all clients provide all features.
::

View file

@ -1,6 +1,11 @@
---
title: Features
description: Discover the features that make Elk a delightful Mastodon client.
---
# Features
::alert{type=warning}
::callout{type=warning}
🚧 This section is a work in progress. 🚧
::
@ -23,7 +28,7 @@ Elk renders basic Markdown-like text markup in post texts as the expected HTML.
- Use one asterisk (`*`) before and after a word or phrase to *italicize** the text.
- Surround the text with two asterisks (`**word**`) to **bold** it.
::alert{type="warning"}
::callout{type="warning"}
Many apps do not support Markdown in posts.
Mastodon itself does not support Markdown in posts.

View file

@ -1,3 +1,8 @@
---
title: Contributing
description: Learn how to contribute to Elk and help build the future of Mastodon clients.
---
# Contributing
We're really excited that you're interested in contributing to Elk! Before submitting your contribution, please read through the following guide.

View file

@ -1,3 +1,8 @@
---
title: Sponsoring
description: Support the development of Elk by sponsoring the team.
---
# Sponsoring
If you're enjoying the app, consider sponsoring our team:

View file

@ -1,4 +1,7 @@
# Netlify and Cloudflare
---
title: Netlify and Cloudflare
description: Deploy your own Elk instance.
---
Want to host Elk for your Mastodon instance? You came to the right place!

View file

@ -1,3 +1,8 @@
---
title: Progressive Web App
description: Learn about Elk's PWA capabilities and how to install it on your device.
---
# PWA
Elk provides a PWA (Progressive Web App) that can be installed on your desktop/device. This allows you to use Elk as a native app on your device, and it will work offline.

View file

@ -1,3 +1,8 @@
---
title: Privacy Policy
description: Elk's privacy policy and data handling practices.
---
# Privacy
> Last updated January 27, 2023
@ -8,7 +13,7 @@ This privacy notice for Elk describes how we handle your information when you:
- Download and use our mobile or desktop application (Elk)
::alert{type=warning}
::callout{type="warning"}
Elk is [open source](https://github.com/elk-zone/elk) and other websites that link to this privacy notice may not be affiliated with Elk or bound by this policy.
::

158
docs/content/index.md Executable file
View file

@ -0,0 +1,158 @@
---
seo:
title: "Elk - A Nimble Mastodon Web Client"
description: A nimble Mastodon web client that provides a fresh and intuitive social media experience with modern features and elegant design.
ogImage: https://docs.elk.zone/elk-screenshot.png
---
::u-page-hero
---
orientation: horizontal
---
:::div{.hidden.lg:flex.items-center.justify-center}
![Elk Screenshot](/screenshot.png){.rounded-lg.shadow-lg}
:::
#title
A Nimble [Mastodon Web Client]{.text-primary}
#description
Experience Mastodon like never before. Elk brings you a fresh, intuitive interface with modern features that make social networking delightful.
#links
:::u-button
---
icon: i-ph-rocket-launch-duotone
size: xl
to: /guide
---
Read the docs
:::
:::u-button
---
icon: i-ph-arrow-square-out-duotone
size: xl
variant: outline
to: https://elk.zone
target: _blank
---
Try it live
:::
::
::u-page-section
#title
Everything you need for the perfect [Mastodon experience]{.text-primary}
#features
:::u-page-card
---
spotlight: true
icon: i-ph-markdown-logo-duotone
to: /guide/features
---
#title
Rich Content Support
#description
Full Markdown support with syntax highlighting, emoji reactions, and rich media previews that bring your posts to life.
:::
:::u-page-card
---
spotlight: true
icon: i-ic-twotone-view-timeline
to: /guide/features
---
#title
Smart Timeline Management
#description
Reorder and connect posts in your timeline with intelligent grouping and enhanced notification management.
:::
:::u-page-card
---
spotlight: true
icon: i-ph-users-duotone
to: /guide/features
---
#title
Multi-Account Support
#description
Seamlessly manage multiple Mastodon accounts with quick switching and unified notifications.
:::
:::u-page-card
---
spotlight: true
icon: i-ph-github-logo-duotone
to: /guide/features
---
#title
GitHub Integration
#description
Beautiful HTML cards for GitHub links with repository previews and rich metadata display.
:::
:::u-page-card
---
spotlight: true
icon: i-ph-device-mobile-duotone
to: /pwa
---
#title
Progressive Web App
#description
Install Elk on any device for a native-like experience with offline support and push notifications.
:::
:::u-page-card
---
spotlight: true
icon: i-ph-heart-duotone
to: /guide/contributing
---
#title
Open Source & Community Driven
#description
Built with love by the community. Contribute to the future of federated social media.
:::
::
::u-page-section
---
orientation: horizontal
---
#title
Ready to dive in?
#description
Join thousands of users who have already discovered a better way to experience Mastodon. Get started in minutes with our comprehensive documentation.
#links
:::u-button
---
icon: i-ph-book-open-duotone
size: xl
to: /guide
---
Read the docs
:::
:::u-button
---
icon: i-ph-github-logo-duotone
size: xl
variant: outline
to: https://github.com/elk-zone/elk
target: _blank
---
View on GitHub
:::
::

View file

@ -1,5 +1,17 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
extends: '@nuxt-themes/docus',
extends: ['docus'],
css: ['~/assets/css/main.css'],
site: {
name: 'Elk',
url: 'https://docs.elk.zone',
},
llms: {
domain: 'https://docs.elk.zone',
},
vite: {
optimizeDeps: {

View file

@ -3,16 +3,14 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "nuxi dev",
"build": "nuxi build",
"generate": "nuxi generate",
"preview": "nuxi preview"
"dev": "nuxt dev",
"build": "nuxt build",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"dependencies": {
"theme-colors": "^0.1.0"
},
"devDependencies": {
"@nuxt-themes/docus": "^1.15.1",
"nuxt": "^3.17.7"
"better-sqlite3": "^12.4.1",
"docus": "^4.0.0",
"nuxt": "^4.1.2"
}
}

View file

@ -1,17 +0,0 @@
import { defineTheme } from 'pinceau'
import { getColors } from 'theme-colors'
const light = getColors('#995e1b')
const primary = Object
.entries(getColors('#d98018'))
.reduce((acc, [key, value]) => {
acc[key] = {
initial: light[key]!,
dark: value,
}
return acc
}, {} as Record<string | number, { initial: string, dark: string }>)
export default defineTheme({
color: { primary },
})

View file

@ -85,7 +85,7 @@
},
"app_desc_short": "منصة تواصل ماستودون رشيقة",
"app_logo": "Elk شعار",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "وصف",
"remove_label": "قم بإزالة المرفق"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Un client web àgil Mastodon",
"app_logo": "Logotip d'Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Descripció",
"remove_label": "Elimina el fitxer adjunt"

View file

@ -78,7 +78,7 @@
},
"app_desc_short": "ڕاژەخوازێکی وێبی ماستۆدۆن",
"app_logo": "Elk لۆگۆی",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "وەسف",
"remove_label": "لابردنی لکێندراو"

View file

@ -49,7 +49,7 @@
},
"app_desc_short": "Elk - hbitý webklient pro Mastodon.",
"app_logo": "logo Elk",
"app_name": "Elk",
"app_name": "Yolk",
"command": {
"activate": "Aktivovat",
"complete": "Dokončit",

View file

@ -70,7 +70,7 @@
},
"app_desc_short": "Cleient gwe ystwyth ar gyfer Mastodon",
"app_logo": "Logo Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Disgrifiad",
"remove_label": "Tunnu atodiad"

View file

@ -91,7 +91,7 @@
},
"app_desc_short": "Ein flinker Mastodon Web-Client",
"app_logo": "Elk Logo",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Beschreibung",
"remove_label": "Anhang entfernen"

View file

@ -86,7 +86,7 @@
},
"app_desc_short": "Ένας ελαφρώς προσαρμοσμένος πελάτης για το Mastodon",
"app_logo": "Λογότυπο Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Περιγραφή",
"remove_label": "Αφαίρεση επισύναψης"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "A nimble Mastodon web client",
"app_logo": "Elk Logo",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Description",
"remove_label": "Remove attachment"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Un cliente web ágil para Mastodon",
"app_logo": "Logotipo de Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Descripción",
"remove_label": "Eliminar fichero adjunto"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Mastodon web-bezero arin bat",
"app_logo": "Elk-en logoa",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Deskribapena",
"remove_label": "Kendu erantsitakoak"

View file

@ -85,7 +85,7 @@
},
"app_desc_short": "یک کارخواه وب چابک برای ماستودون",
"app_logo": "لوگوی نرم‌افزار",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "توضیحات",
"remove_label": "حذف پیوست"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Ketterä verkkosovellus Mastodonille",
"app_logo": "Elk-logo",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Kuvaus",
"remove_label": "Poista liite"

View file

@ -7,6 +7,8 @@
"route_loaded": "Page {0} chargée"
},
"account": {
"authorize": "Autoriser l'abonnement",
"authorized": "Vous avez autorisé la demande",
"avatar_description": "Avatar de {0}",
"blocked_by": "Ce compte vous a bloqué",
"blocked_domains": "Domaines bloqués",
@ -25,6 +27,7 @@
"follows_you": "@:account.follow_back",
"go_to_profile": "Aller à son profil",
"joined": "a rejoint",
"lock": "Verrouiller",
"moved_title": "a indiqué que son nouveau compte est désormais :",
"muted_users": "Comptes masqués",
"muting": "Masqué·e",
@ -37,7 +40,10 @@
"profile_description": "En-tête du profil de {0}",
"profile_personal_note": "Note personnelle",
"profile_unavailable": "Profil non accessible",
"reject": "Rejeter l'abonnement",
"rejected": "Vous avez rejeté la demande",
"request_follow": "Demander à suivre",
"requested": "{0} a demandé à vous suivre",
"unblock": "Débloquer",
"unfollow": "Ne plus suivre",
"unmute": "Réafficher",
@ -52,6 +58,7 @@
"boost": "Partager",
"boost_count": "{0}",
"boosted": "Partagé",
"clear": "Effacer",
"clear_publish_failed": "Effacer les erreurs de publication",
"clear_save_failed": "Effacer les erreurs de sauvegarde",
"clear_upload_failed": "Effacer les erreurs de téléversement de fichier",
@ -66,8 +73,10 @@
"favourited": "J'aime",
"more": "Plus",
"next": "Suivant",
"open_image_preview_dialog": "Ouvrir le dialogue d'aperçu de l'image",
"prev": "Précédent",
"publish": "Publier",
"publish_thread": "Publier le fil",
"reply": "Répondre",
"reply_count": "{0}",
"reset": "Réinitialiser",
@ -80,7 +89,7 @@
},
"app_desc_short": "Un client Mastodon fait avec 🧡",
"app_logo": "Logo Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Description",
"remove_label": "Retirer le fichier attaché"
@ -115,12 +124,14 @@
"block_account": {
"cancel": "Annuler",
"confirm": "Bloquer",
"description": "Voulez-vous vraiment bloquer {0} ?"
"description": "Voulez-vous vraiment bloquer {0} ?",
"title": "Bloquer le compte"
},
"block_domain": {
"cancel": "Annuler",
"confirm": "Bloquer",
"description": "Voulez-vous vraiment bloquer {0} ?"
"description": "Voulez-vous vraiment bloquer {0} ?",
"title": "Bloquer le domaine"
},
"common": {
"cancel": "Non",
@ -129,27 +140,37 @@
"delete_list": {
"cancel": "Annuler",
"confirm": "Supprimer",
"description": "Voulez-vous vraiment supprimer la liste \"{0}\" ?"
"description": "Voulez-vous vraiment supprimer la liste \"{0}\" ?",
"title": "Supprimer la liste"
},
"delete_posts": {
"cancel": "Annuler",
"confirm": "Supprimer",
"description": "Voulez-vous vraiment supprimer ce message ?"
"description": "Voulez-vous vraiment supprimer ce message ?",
"title": "Supprimer le message"
},
"mute_account": {
"cancel": "Annuler",
"confirm": "Mettre en sourdine",
"description": "Voulez-vous vraiment mettre en sourdine {0} ?"
"days": "jour|jour|jour",
"description": "Voulez-vous vraiment mettre en sourdine {0} ?",
"hours": "heures|heures|heures",
"minute": "minutes|minutes|minutes",
"notifications": "Mettre en sourdine les notifications",
"specify_duration": "Spécifier la durée de la mise en sourdine",
"title": "Mettre en sourdine le compte"
},
"show_reblogs": {
"cancel": "Annuler",
"confirm": "Afficher",
"description": "Voulez-vous vraiment afficher les partages de {0} ?"
"description": "Voulez-vous vraiment afficher les partages de {0} ?",
"title": "Afficher les partages"
},
"unfollow": {
"cancel": "Annuler",
"confirm": "Se désabonner",
"description": "Voulez-vous vraiment vous désabonner ?"
"description": "Voulez-vous vraiment vous désabonner ?",
"title": "Se désabonner"
}
},
"conversation": {
@ -202,9 +223,12 @@
"error": "Il y a eu une erreur lors de la création de la liste",
"error_prefix": "Erreur :",
"list_title_placeholder": "Nom de la liste",
"manage": "Gérer les listes",
"modify_account": "Modifier les listes de ce compte",
"remove_account": "Supprimer ce compte de listes",
"save": "Enregistrer les changements"
"save": "Enregistrer les changements",
"search_following_desc": "Chercher des personnes que vous suivez",
"search_following_placeholder": "Chercher parmi les personnes que vous suivez"
},
"magic_keys": {
"dialog_header": "Raccourcis clavier",
@ -214,14 +238,26 @@
"command_mode": "Mode commande",
"compose": "Composer",
"favourite": "J'aime",
"search": "Rechercher",
"show_new_items": "Afficher les nouveaux éléments",
"title": "Actions"
},
"media": {
"title": "Média"
},
"navigation": {
"go_to_bookmarks": "Signets",
"go_to_conversations": "Conversations",
"go_to_explore": "Explorer",
"go_to_favourites": "Favoris",
"go_to_federated": "Fédérés",
"go_to_home": "Accueil",
"go_to_lists": "Listes",
"go_to_local": "Local",
"go_to_notifications": "Notifications",
"go_to_profile": "Profil",
"go_to_search": "Rechercher",
"go_to_settings": "Paramètres",
"next_status": "Message suivant",
"previous_status": "Message précédent",
"shortcut_help": "Aide sur les raccourcis",
@ -276,13 +312,16 @@
"built_at": "Dernière compilation {0}",
"compose": "Composer",
"conversations": "Conversations",
"docs": "Documentation",
"explore": "Explorer",
"favourites": "Aimés",
"federated": "Fédérés",
"hashtags": "Hashtags",
"home": "Accueil",
"list": "Liste",
"lists": "Listes",
"local": "Local",
"more_menu": "Plus d'options",
"muted_users": "Comptes masqués",
"notifications": "Notifications",
"privacy": "Données privées",
@ -297,10 +336,12 @@
"zen_mode": "Mode Zen"
},
"notification": {
"and": "et",
"favourited_post": "a aimé votre message",
"followed_you": "vous suit",
"followed_you_count": "{0} personnes vous suivent|{0} personne vous suit|{0} personnes vous suivent",
"missing_type": "MISSING notification.type:",
"others": "{0} personnes|{0} personne|{0} personnes",
"reblogged_post": "a relayé votre message",
"reported": "{0} a signalé {1}",
"request_to_follow": "vous demande de le suivre",
@ -417,6 +458,8 @@
"label": "Paramètres de compte"
},
"interface": {
"bottom_nav": "Navigation inférieure",
"bottom_nav_instructions": "Choisissez jusqu'à cinq boutons de navigation inférieure favoris. Doit inclure le bouton \"Plus d'options\".",
"color_mode": "Couleur de thème",
"dark_mode": "Mode sombre",
"default": " (par défaut)",
@ -428,6 +471,7 @@
},
"language": {
"display_language": "Langue d'affichage",
"how_to_contribute": "Comment contribuer ?",
"label": "Langue",
"post_language": "Langue de publication",
"status": "État de la traduction : {0}/{1} ({2} %)",
@ -495,6 +539,8 @@
},
"notifications_settings": "Notifications",
"preferences": {
"embedded_media": "Lecteur multimédia intégré",
"embedded_media_description": "Affichez un lecteur intégré au lieu de la carte d'aperçu normale lors de l'expansion des liens de streaming de supports partagés.",
"enable_autoplay": "Activer la lecture automatique",
"enable_data_saving": "Activer l'économie de données",
"enable_data_saving_description": "Economise les données en évitant le chargement automatique des médias.",
@ -507,13 +553,16 @@
"hide_boost_count": "Masquer les compteurs de partages",
"hide_favorite_count": "Masquer les compteurs de favoris",
"hide_follower_count": "Masquer les compteurs d'abonné·e·s",
"hide_gif_indi_on_posts": "Masquer l'indicateur de gif sur les messages",
"hide_news": "Masquer les actualités",
"hide_reply_count": "Masquer les compteurs de réponses",
"hide_translation": "Masquer traduction",
"hide_username_emojis": "Masquer les emojis sur le nom d'utilisateur",
"hide_username_emojis_description": "Masque les emojis des noms d'utilisateur dans la timeline. \nLes emojis seront toujours visibles sur leurs profils.",
"label": "Préférences",
"optimize_for_low_performance_device": "Optimiser pour un dispositif à faible performance",
"title": "Fonctionnalités expérimentales",
"unmute_videos": "Son de vidéo par défaut",
"use_star_favorite_icon": "Utiliser l'icône de l'étoile en favoris",
"user_picker": "User Picker",
"user_picker_description": "Affiche tous les avatars des comptes connectés en bas à gauche afin que vous puissiez basculer rapidement entre eux.",
@ -556,7 +605,11 @@
},
"state": {
"attachments_exceed_server_limit": "Le nombre de pièces jointes a dépassé la limite par message.",
"attachments_limit_audio_error": "Taille maximum d'audio dépassée : {0}",
"attachments_limit_error": "Limite par publication dépassée",
"attachments_limit_image_error": "Taille maximum d'image dépassée : {0}",
"attachments_limit_unknown_error": "Taille maximum de fichier dépassée : {0}",
"attachments_limit_video_error": "Taille maximum de vidéo dépassée : {0}",
"edited": "(Édité)",
"editing": "Édition",
"loading": "Chargement...",
@ -573,15 +626,18 @@
},
"boosted_by": "Partagé par",
"edited": "Edité {0}",
"embedded_warning": "Lire ceci peut révéler votre adresse IP à d'autres.",
"favourited_by": "Aimé par",
"filter_hidden_phrase": "Filtré par",
"filter_show_anyway": "Montrer coûte que coûte",
"gif": "GIF",
"img_alt": {
"ALT": "ALT",
"desc": "Description",
"dismiss": "Fermer",
"read": "Lire la description de {0}"
},
"pinned": "Messages épinglés",
"poll": {
"count": "{0} votes",
"ends": "se clôt {0}",
@ -663,6 +719,7 @@
"year_past": "il y a 0 année|l'année dernière|il y a {n} années"
},
"timeline": {
"no_posts": "Pas de messages ici !",
"show_new_items": "Voir le nouveau message|Voir les {v} nouveaux messages",
"view_older_posts": "Les messages plus anciens d'autres instances peuvent ne pas être affichés."
},
@ -675,6 +732,7 @@
"add_emojis": "Ajouter des émoticônes",
"add_media": "Ajouter des images, une vidéo ou un fichier audio",
"add_publishable_content": "Ajouter du contenu à publier",
"add_thread_item": "Ajouter un message au fil",
"change_content_visibility": "Ajuster la confidentialité du message",
"change_language": "Changer la langue",
"emoji": "Emoji",
@ -684,6 +742,8 @@
"open_editor_tools": "Outils d'édition",
"pick_an_icon": "Choisir une icône",
"publish_failed": "Fermez les messages ayant échoué en haut de l'éditeur pour republier les messages",
"remove_thread_item": "Supprimer le message du fil",
"start_thread": "Commencer un fil",
"toggle_bold": "Appliquer/retirer le gras",
"toggle_code_block": "Ajouter un bloc de code",
"toggle_italic": "Appliquer/retirer l'italique"

View file

@ -85,7 +85,7 @@
},
"app_desc_short": "Un cliente web áxil para Mastodon",
"app_logo": "Logo de Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Descrición",
"remove_label": "Quitar o anexo"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Egy fürge Mastodon kliens",
"app_logo": "Elk Logó",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Leírás",
"remove_label": "Melléklet eltávolítása"

View file

@ -27,6 +27,7 @@
"follows_you": "Mengikutimu",
"go_to_profile": "Buka profil",
"joined": "Bergabung",
"lock": "Kunci",
"moved_title": "telah mengindikasikan bahwa akun baru mereka sekarang:",
"muted_users": "Pengguna dibisukan",
"muting": "Bisukan",
@ -57,6 +58,7 @@
"boost": "Dukung",
"boost_count": "{0}",
"boosted": "Didukung",
"clear": "Bersihkan",
"clear_publish_failed": "Hapus error publikasi",
"clear_save_failed": "Hapus kesalahan penyimpanan",
"clear_upload_failed": "Hapus kesalahan pengunggahan file",
@ -71,8 +73,10 @@
"favourited": "Difavoritkan",
"more": "Selengkapnya",
"next": "Selanjutnya",
"open_image_preview_dialog": "Buka dialog pratinjau gambar",
"prev": "Sebelumnya",
"publish": "Terbitkan",
"publish_thread": "Terbitkan thread",
"reply": "Balas",
"reply_count": "{0}",
"reset": "Atur ulang",
@ -85,7 +89,7 @@
},
"app_desc_short": "Klien web Mastodon yang gesit",
"app_logo": "Logo Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Deskripsi",
"remove_label": "Hapus lampiran"
@ -120,12 +124,14 @@
"block_account": {
"cancel": "Batalkan",
"confirm": "Blokir",
"description": "Yakin ingin memblokir {0}?"
"description": "Yakin ingin memblokir {0}?",
"title": "Blokir akun"
},
"block_domain": {
"cancel": "Batalkan",
"confirm": "Blokir",
"description": "Yakin ingin memblokir {0}?"
"description": "Yakin ingin memblokir {0}?",
"title": "Blokir domain"
},
"common": {
"cancel": "Tidak",
@ -134,27 +140,37 @@
"delete_list": {
"cancel": "Batalkan",
"confirm": "Hapus",
"description": "Apakah Anda yakin ingin menghapus daftar \"{0}\"?"
"description": "Apakah Anda yakin ingin menghapus daftar \"{0}\"?",
"title": "Hapus daftar"
},
"delete_posts": {
"cancel": "Batalkan",
"confirm": "Hapus",
"description": "Yakin ingin menghapus postingan ini?"
"description": "Yakin ingin menghapus postingan ini?",
"title": "Hapus postingan"
},
"mute_account": {
"cancel": "Batalkan",
"confirm": "Bisukan",
"description": "Yakin ingin membisukan {0}?"
"days": "hari|hari|hari",
"description": "Yakin ingin membisukan {0}?",
"hours": "jam|jam|jam",
"minute": "menit|menit|menit",
"notifications": "Bisukan notifikasi",
"specify_duration": "Tentukan durasi bisu",
"title": "Bisukan akun"
},
"show_reblogs": {
"cancel": "Batalkan",
"confirm": "Tampilkan",
"description": "Yakin ingin menampilkan dukungan dari {0}?"
"description": "Yakin ingin menampilkan dukungan dari {0}?",
"title": "Tampilkan dukungan"
},
"unfollow": {
"cancel": "Batalkan",
"confirm": "Berhenti mengikuti",
"description": "Anda yakin ingin berhenti mengikuti?"
"description": "Anda yakin ingin berhenti mengikuti?",
"title": "Berhenti mengikuti"
}
},
"conversation": {
@ -207,9 +223,12 @@
"error": "Terjadi kesalahan saat membuat daftar",
"error_prefix": "Kesalahan: ",
"list_title_placeholder": "Judul daftar",
"manage": "Kelola daftar",
"modify_account": "Ubah daftar dengan akun",
"remove_account": "Hapus akun dari daftar",
"save": "Simpan perubahan"
"save": "Simpan perubahan",
"search_following_desc": "Cari orang yang Anda ikuti",
"search_following_placeholder": "Cari di antara orang yang Anda ikuti"
},
"magic_keys": {
"dialog_header": "Pintasan keyboard",
@ -220,14 +239,25 @@
"compose": "Menyusun",
"favourite": "Favorit",
"search": "Cari",
"show_new_items": "Tampilkan item baru",
"title": "Tindakan"
},
"media": {
"title": "Media"
},
"navigation": {
"go_to_bookmarks": "Markah",
"go_to_conversations": "Percakapan",
"go_to_explore": "Jelajahi",
"go_to_favourites": "Favorit",
"go_to_federated": "Federasi",
"go_to_home": "Beranda",
"go_to_lists": "Daftar",
"go_to_local": "Lokal",
"go_to_notifications": "Notifikasi",
"go_to_profile": "Profil",
"go_to_search": "Cari",
"go_to_settings": "Pengaturan",
"next_status": "Posting selanjutnya",
"previous_status": "Posting sebelumnya",
"shortcut_help": "Bantuan jalan pintas",
@ -282,13 +312,16 @@
"built_at": "Diperbarui {0}",
"compose": "Menyusun",
"conversations": "Percakapan",
"docs": "Dokumentasi",
"explore": "Jelajahi",
"favourites": "Favorit",
"federated": "Federasi",
"hashtags": "Tagar",
"home": "Beranda",
"list": "Daftar",
"lists": "Daftar",
"local": "Lokal",
"more_menu": "Menu lainnya",
"muted_users": "Pengguna dibisukan",
"notifications": "Notifikasi",
"privacy": "Privasi",
@ -303,10 +336,12 @@
"zen_mode": "Mode Zen"
},
"notification": {
"and": "dan",
"favourited_post": "memfavoritkan pos Anda",
"followed_you": "mengikutimu",
"followed_you_count": "{0} orang mengikuti Anda|{0} orang mengikuti Anda|{0} orang mengikuti Anda",
"missing_type": "NOTIFIKASI HILANG.jenis:",
"others": "{0} lainnya|{0} lainnya|{0} lainnya",
"reblogged_post": "meningkatkan postingan Anda",
"reported": "{0} melaporkan {1}",
"request_to_follow": "meminta untuk mengikuti Anda",
@ -423,6 +458,8 @@
"label": "Pengaturan akun"
},
"interface": {
"bottom_nav": "Navigasi Bawah",
"bottom_nav_instructions": "Pilih tombol navigasi favorit Anda hingga lima untuk navigasi bawah. Harus menyertakan tombol \"Menu Lainnya\".",
"color_mode": "Tema",
"dark_mode": "Gelap",
"default": "(bawaan)",
@ -434,6 +471,7 @@
},
"language": {
"display_language": "Bahasa tampilan",
"how_to_contribute": "Bagaimana cara berkontribusi?",
"label": "Bahasa",
"post_language": "Bahasa Postingan",
"status": "Status terjemahan: {0}/{1} ({2}%)",
@ -515,13 +553,17 @@
"hide_boost_count": "Sembunyikan jumlah dukungan",
"hide_favorite_count": "Sembunyikan jumlah favorit",
"hide_follower_count": "Sembunyikan jumlah pengikut",
"hide_gif_indi_on_posts": "Sembunyikan indikator gif pada postingan",
"hide_news": "Sembunyikan berita",
"hide_reply_count": "Sembunyikan jumlah balasan",
"hide_tag_hover_card": "Sembunyikan tag kartu hover",
"hide_translation": "Sembunyikan terjemahan",
"hide_username_emojis": "Sembunyikan emoji di nama pengguna",
"hide_username_emojis_description": "Sembunyikan emoji dari nama pengguna dalam timeline. Emoji tetap akan terlihat di profil mereka.",
"label": "Preferensi",
"optimize_for_low_performance_device": "Optimalkan untuk perangkat berkinerja rendah",
"title": "Fitur Eksperimental",
"unmute_videos": "Suara video aktif secara default",
"use_star_favorite_icon": "Gunakan ikon bintang favorit",
"user_picker": "Pemilih Pengguna",
"user_picker_description": "Menampilkan semua avatar akun yang dicatat di kiri bawah sehingga Anda dapat beralih antar akun dengan cepat.",
@ -564,7 +606,11 @@
},
"state": {
"attachments_exceed_server_limit": "Jumlah lampiran melebihi batas per posting.",
"attachments_limit_audio_error": "Ukuran maksimal audio terlampaui: {0}",
"attachments_limit_error": "Batas per posting terlampaui",
"attachments_limit_image_error": "Ukuran maksimal gambar terlampaui: {0}",
"attachments_limit_unknown_error": "Ukuran file maksimal terlampaui: {0}",
"attachments_limit_video_error": "Ukuran maksimal video terlampaui: {0}",
"edited": "(Disunting)",
"editing": "Menyunting",
"loading": "Memuat...",
@ -585,24 +631,27 @@
"favourited_by": "Difavoritkan Oleh",
"filter_hidden_phrase": "Difilter oleh",
"filter_show_anyway": "Tetap tampilkan",
"gif": "GIF",
"img_alt": {
"ALT": "ALT",
"desc": "Keterangan",
"dismiss": "Batalkan",
"read": "Baca deskripsi {0}"
},
"pinned": "Postingan disematkan",
"poll": {
"count": "{0} suara|{0} suara|{0} suara",
"ends": "berakhir {0}",
"finished": "selesai {0}"
"finished": "selesai {0}",
"update": "Perbarui jajak pendapat"
},
"replying_to": "Membalas ke {0}",
"show_full_thread": "Tampilkan utas Penuh",
"show_full_thread": "Tampilkan thread Penuh",
"someone": "seseorang",
"spoiler_media_hidden": "Media disembunyikan",
"spoiler_show_less": "Tampilkan lebih sedikit",
"spoiler_show_more": "Menampilkan lebih banyak",
"thread": "Utas",
"thread": "Thread",
"try_original_site": "Coba situs asli"
},
"status_history": {
@ -671,6 +720,7 @@
"year_past": "0 tahun lalu|tahun lalu|{n} tahun lalu"
},
"timeline": {
"no_posts": "Tidak ada postingan di sini!",
"show_new_items": "Tampilkan {v} item baru|Tampilkan {v} item baru|Tampilkan {v} item baru",
"view_older_posts": "Posting lama dari contoh lain mungkin tidak ditampilkan."
},
@ -683,6 +733,7 @@
"add_emojis": "Tambahkan emoji",
"add_media": "Tambahkan gambar, video atau file audio",
"add_publishable_content": "Tambahkan konten untuk dipublikasikan",
"add_thread_item": "Tambahkan item ke thread",
"change_content_visibility": "Ubah visibilitas konten",
"change_language": "Ubah bahasa",
"emoji": "Emoji",
@ -692,6 +743,8 @@
"open_editor_tools": "Alat Editor",
"pick_an_icon": "Pilih ikon",
"publish_failed": "Tutup pesan yang gagal di bagian atas editor untuk mempublikasikan ulang postingan",
"remove_thread_item": "Hapus item dari thread",
"start_thread": "Mulai thread",
"toggle_bold": "Ubah tebal",
"toggle_code_block": "Tulis sebagai blok kode",
"toggle_italic": "Alihkan miring"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Un client web agile per Mastodon",
"app_logo": "Logo Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Descrizione",
"remove_label": "Rimuovi allegato"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "軽快なMastodonウェブクライアント",
"app_logo": "Elkのロゴ",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "説明文",
"remove_label": "添付を削除"

View file

@ -69,7 +69,7 @@
},
"app_desc_short": "민첩한 마스토돈 웹 클라이언트",
"app_logo": "Elk 로고",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "설명",
"remove_label": "첨부파일 삭제"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Een vlotte Mastodon web client",
"app_logo": "Elk Logo",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Omschrijving",
"remove_label": "Verwijder bijlage"

View file

@ -77,7 +77,7 @@
},
"app_desc_short": "Aplikacja webowa dla Mastodon",
"app_logo": "Elk Logo",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Opis",
"remove_label": "Usuń załącznik"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Uma ágil aplicação web para o Mastodon",
"app_logo": "Logo do Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Descrição",
"remove_label": "Remover anexo"

View file

@ -78,7 +78,7 @@
},
"app_desc_short": "Открытый веб-клиент для Mastodon",
"app_logo": "Логотип Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Описание",
"remove_label": "Удалить файл"

772
locales/sv.json Normal file
View file

@ -0,0 +1,772 @@
{
"a11y": {
"loading_page": "Laddar sidan, vänligen vänta",
"loading_titled_page": "Laddar sida {0}, vänligen vänta",
"locale_changed": "Språk ändrades till {0}",
"locale_changing": "Byter språk, vänligen vänta",
"route_loaded": "Sidan {0} laddades"
},
"account": {
"authorize": "Tillåt att följa",
"authorized": "Du har tillåtit förfrågan",
"avatar_description": "{0}'s profilbild",
"blocked_by": "Du är blockerad för denna användare.",
"blocked_domains": "Blockerade domäner",
"blocked_users": "Blockerade användare",
"blocking": "Blockerad",
"bot": "BOT",
"copy_account_name": "Kopiera kontonamn",
"favourites": "Favoriter",
"follow": "Följ",
"follow_back": "Följ tillbaka",
"follow_requested": "Begärd",
"followers": "Följare",
"followers_count": "{0} Följare|{0} Följare|{0} Följare",
"following": "Följer",
"following_count": "{0} Följer",
"follows_you": "Följer dig",
"go_to_profile": "Gå till profil",
"joined": "Gick med",
"lock": "Lås",
"moved_title": "har angett att deras konto är nu:",
"muted_users": "Tystade användare",
"muting": "Tystad",
"mutuals": "Ömsesidiga",
"notifications_on_post_disable": "Sluta notifiera mig när {username} gör inlägg",
"notifications_on_post_enable": "Notifiera mig när {username} gör inlägg",
"pinned": "Fäst",
"posts": "Inlägg",
"posts_count": "{0} Inlägg|{0} Inlägg|{0} Inlägg",
"profile_description": "{0}s profilrubrik",
"profile_personal_note": "Personlig anteckning",
"profile_unavailable": "Profil ej tillgänlig",
"reject": "Neka följare",
"rejected": "Du har nekat begäran",
"request_follow": "Begär att följa",
"requested": "{0} har begärt att följa dig",
"unblock": "Avblockera",
"unfollow": "Avfölj",
"unmute": "Avtysta",
"view_other_followers": "Följare från andra instanser kanske inte visas.",
"view_other_following": "Följande från andra instanser kanske inte visas.",
"withdraw_follow_request": "Återkalla följförfrågan"
},
"action": {
"apply": "Tillämpa",
"bookmark": "Bokmärk",
"bookmarked": "Bokmärkt",
"boost": "Boosta",
"boost_count": "{0}",
"boosted": "Boostad",
"clear": "Rensa",
"clear_publish_failed": "Rensa publiceringsfel",
"clear_save_failed": "Rensa sparfel",
"clear_upload_failed": "Rensa filuppladdningsfel",
"close": "Stäng",
"compose": "Komponera",
"confirm": "Bekräfta",
"done": "Klar",
"edit": "Redigera",
"enter_app": "Öppna appen",
"favourite": "Favoritmarkera",
"favourite_count": "{0}",
"favourited": "Favoritmarkerad",
"more": "Mer",
"next": "Nästa",
"open_image_preview_dialog": "Öppna bildförhandsgranskning",
"prev": "Föregående",
"publish": "Publicera",
"publish_thread": "Publicera tråd",
"reply": "Svara",
"reply_count": "{0}",
"reset": "Återställ",
"save": "Spara",
"save_changes": "Spara ändringar",
"sign_in": "Logga in",
"sign_in_to": "Logga in på {0}",
"switch_account": "Växla konto",
"vote": "Rösta"
},
"app_desc_short": "En smidig Mastodon-webbklient",
"app_logo": "Elk-logga",
"app_name": "Elk",
"attachment": {
"edit_title": "Beskrivning",
"remove_label": "Ta bort bilaga"
},
"command": {
"activate": "Aktivera",
"complete": "Färdigställ",
"compose_desc": "Skriv ett nytt inlägg",
"n_people_in_the_past_n_days": "{0} personer de senaste {1} dagarna",
"select_lang": "Välj språk",
"sign_in_desc": "Lägg till ett befintligt konto",
"switch_account": "Växla till {0}",
"switch_account_desc": "Växla till ett annat konto",
"toggle_dark_mode": "Växla mörkt läge",
"toggle_zen_mode": "Växla zen-läge"
},
"common": {
"end_of_list": "Slut på listan",
"error": "FEL",
"fetching": "Hämtar...",
"in": "i",
"no_bookmarks": "Inga bokmärkta inlägg än",
"no_favourites": "Inga favoritmarkerade inlägg än",
"not_found": "404 Ej hittad",
"offline_desc": "Verkar som att du är offline. Vänligen kontrollera din nätverksanslutning."
},
"compose": {
"draft_title": "Utkast {0}",
"drafts": "Utkast ({v})"
},
"confirm": {
"block_account": {
"cancel": "Avbryt",
"confirm": "Blockera",
"description": "Är du säker på att du vill blockera {0}?",
"title": "Blockera konto"
},
"block_domain": {
"cancel": "Avbryt",
"confirm": "Blockera",
"description": "Är du säker på att du vill blockera {0}?",
"title": "Blockera domän"
},
"common": {
"cancel": "Nej",
"confirm": "Ja"
},
"delete_list": {
"cancel": "Avbryt",
"confirm": "Radera",
"description": "Är du säker på att du vill radera \"{0}\"-listan?",
"title": "Radera lista"
},
"delete_posts": {
"cancel": "Avbryt",
"confirm": "Radera",
"description": "Är du säker på att du vill radera detta inlägg",
"title": "Radera inlägg"
},
"mute_account": {
"cancel": "Avbryt",
"confirm": "Tysta",
"days": "dagar|dag|dagar",
"description": "Är du säker på att du vill tysta {0}?",
"hours": "timmar|timme|timmar",
"minute": "minuter|minut|minuter",
"notifications": "Tysta notiser",
"specify_duration": "Specifiera tyst-varaktighet",
"title": "Tysta konto"
},
"show_reblogs": {
"cancel": "Avbryt",
"confirm": "Visa",
"description": "Är du säker på att du vill visa boosts från {0}?",
"title": "Visa boosts"
},
"unfollow": {
"cancel": "Avbryt",
"confirm": "Avfölj",
"description": "Är du säker på att du vill avfölja {0}?",
"title": "Avfölj"
}
},
"conversation": {
"with": "med"
},
"custom_cards": {
"stackblitz": {
"lines": "Rader {0}",
"open": "Öppna",
"snippet_from": "Urklipp från {0}"
}
},
"error": {
"account_not_found": "Konto {0} hittades ej",
"explore_list_empty": "Inget trendar just nu. Kolla igen senare!",
"file_size_cannot_exceed_n_mb": "Filstorleken kan ej överstiga {0}MB",
"sign_in_error": "Kan ej ansluta till servern.",
"status_not_found": "Inlägget hittades ej",
"unsupported_file_format": "Filformatet stödjs ej"
},
"help": {
"build_preview": {
"desc1": "Du använder just nu en förhandsversion av Elk från communityt - {0}.",
"desc2": "Den kan innehålla ogranskade eller till och med illvilliga ändringar.",
"desc3": "Logga inte in med ditt riktiga konto.",
"title": "Förhandsversion"
},
"desc_highlight": "Förvänta dig buggar här och där och funktioner som saknas.",
"desc_para1": "Elk är en smidig Mastodon-webbklient. Du kan logga in med ditt Mastodon-konto och använda det för att interagera med fediversumet.",
"desc_para2": "Elk är Open Source och vi förbättrar det aktivt som ett community-projekt. Gå med och låt oss bygga det tillsammans!",
"desc_para3": "För att accelerera utvecklingen så kan du sponsra teamet genom GitHub Sponsors. Vi hoppas du gillar Elk!",
"desc_para4": "Om du vill göra en buggrapport, hjälpa oss med testning, ge oss feedback eller bidra,",
"desc_para5": "nå ut till oss på GitHub",
"desc_para6": "och involvera dig.",
"footer_team": "Elk-teamet",
"title": "Välkommen till Elk!"
},
"language": {
"search": "Sök"
},
"list": {
"add_account": "Lägg till konto i listan",
"cancel_edit": "Avbryt redigering",
"clear_error": "Rensa fel",
"create": "Skapa",
"delete": "Radera denna lista",
"delete_error": "Ett fel uppstod när listan skulle raderas",
"edit": "Redigera denna lista",
"edit_error": "Ett fel uppstod när listan skulle uppdateras",
"error": "Ett fel uppstod när listan skulle skapas",
"error_prefix": "Fel: ",
"list_title_placeholder": "Listnamn",
"manage": "Hantera listor",
"modify_account": "Modifiera listor med konto",
"remove_account": "Radera konto från lista",
"save": "Spara ändringar",
"search_following_desc": "Sök efter personer du följer",
"search_following_placeholder": "Sök bland personer du följer"
},
"magic_keys": {
"dialog_header": "Kortkommandon",
"groups": {
"actions": {
"boost": "Boosta",
"command_mode": "Kommandoläge",
"compose": "Komponera",
"favourite": "Favoritmarkera",
"search": "Sök",
"show_new_items": "Visa nya objekt",
"title": "Åtgärder"
},
"media": {
"title": "Media"
},
"navigation": {
"go_to_bookmarks": "Bokmärken",
"go_to_conversations": "Konversationer",
"go_to_explore": "Upptäck",
"go_to_favourites": "Favoriter",
"go_to_federated": "Federerat",
"go_to_home": "Hem",
"go_to_lists": "Listor",
"go_to_local": "Lokalt",
"go_to_notifications": "Notiser",
"go_to_profile": "Profil",
"go_to_search": "Sök",
"go_to_settings": "Inställningar",
"next_status": "Nästa inlägg",
"previous_status": "Föregående inlägg",
"shortcut_help": "Kortkommandon",
"title": "Navigering"
}
},
"sequence_then": "then"
},
"menu": {
"add_personal_note": "Lägg till personlig anteckning för {0}",
"block_account": "Blockera {0}",
"block_domain": "Blockera domän {0}",
"copy_link_to_post": "Kopiera länk till detta inlägg",
"copy_original_link_to_post": "Kopiera originallänk till detta inlägg",
"delete": "Radera",
"delete_and_redraft": "Radera & gör om",
"direct_message_account": "Skicka meddelande till {0}",
"edit": "Redigera",
"hide_reblogs": "Göm boosts från {0}",
"mention_account": "Omnämn {0}",
"mute_account": "Tysta {0}",
"mute_conversation": "Tysta detta inlägg",
"open_in_original_site": "Öppna på originalsidan",
"pin_on_profile": "Fäst i profil",
"remove_personal_note": "Ta bort personlig anteckning från {0}",
"report_account": "Anmäl {0}",
"share_account": "Dela {0}",
"share_post": "Dela detta inlägg",
"show_favourited_and_boosted_by": "Visa vem som favoritmarkerade och boostade",
"show_reblogs": "Visa boosts från {0}",
"show_untranslated": "Visa oöversatta",
"toggle_theme": {
"dark": "Växla mörkt läge",
"light": "Växla ljust läge"
},
"translate_post": "Översätt inlägg",
"unblock_account": "Avblockera {0}",
"unblock_domain": "Avblockera domain {0}",
"unfollow_account": "Avfölj {0}",
"unmute_account": "Avtysta {0}",
"unmute_conversation": "Avtysta detta inlägg",
"unpin_on_profile": "Ångra fäst i profil"
},
"modals": {
"aria_label_close": "Stäng"
},
"nav": {
"back": "Gå tillbaka",
"blocked_domains": "Blockerade domäner",
"blocked_users": "Blockerade användare",
"bookmarks": "Bokmärken",
"built_at": "Byggd {0}",
"compose": "Komponera",
"conversations": "Konversationer",
"docs": "Dokumentation",
"explore": "Upptäck",
"favourites": "Favoriter",
"federated": "Federerat",
"hashtags": "Hashtaggar",
"home": "Hem",
"list": "Lista",
"lists": "Listor",
"local": "Lokalt",
"more_menu": "Mer",
"muted_users": "Tystade användare",
"notifications": "Notiser",
"privacy": "Integritet",
"profile": "Profil",
"search": "Sök",
"select_feature_flags": "Växla funktionsflaggor",
"select_font_size": "Teckenstorlek",
"select_language": "Språk för gränssnittet",
"settings": "Inställningar",
"show_intro": "Visa introduktion",
"toggle_theme": "Växla tema",
"zen_mode": "Zen-läge"
},
"notification": {
"and": "och",
"favourited_post": "favoritmarkerade ditt inlägg",
"followed_you": "följde dig",
"followed_you_count": "{0} följde dig|{0} följde dig|{0} följde dig",
"missing_type": "SAKNAS notification.type:",
"others": "{0} andra|{0} andra|{0} andra",
"reblogged_post": "boostade ditt inlägg",
"reported": "{0} anmälde {1}",
"request_to_follow": "begärde att följa dig",
"signed_up": "registrerade sig",
"update_status": "uppdaterade deras inlägg"
},
"placeholder": {
"content_warning": "Skriv din varning här",
"default_1": "Vad tänker du på?",
"reply_to_account": "Svara till {0}",
"replying": "Svarar"
},
"polls": {
"allow_multiple": "Tillåt flera val",
"cancel": "Avbryt",
"create": "Skapa omröstning",
"disallow_multiple": "Tillåt ej flera val",
"expiration": "Omröstning upphör",
"hide_votes": "Dölj totala röster till slutet",
"option_placeholder": "Omröstningsval {current}/{max}",
"remove_option": "Ta bort val",
"settings": "Omröstningsval",
"show_votes": "Visa alltid totalt antal röster"
},
"pwa": {
"dismiss": "Avvisa",
"install": "Installera",
"install_title": "Installera Elk",
"screenshots": {
"dark": "Skärmavbild av Elk i mörkt läge",
"light": "Skärmavbild av Elk i ljust läge"
},
"title": "Ny Elk-uppdatering tillgänglig!",
"update": "Uppdatera",
"update_available_short": "Uppdatera Elk",
"webmanifest": {
"canary": {
"description": "En smidig Mastodon-webbklient (canary)",
"name": "Elk (canary)",
"short_name": "Elk (canary)"
},
"dev": {
"description": "En smidig Mastodon-webbklient (dev)",
"name": "Elk (dev)",
"short_name": "Elk (dev)"
},
"preview": {
"description": "En smidig Mastodon-webbklient (preview)",
"name": "Elk (preview)",
"short_name": "Elk (preview)"
},
"release": {
"description": "En smidig Mastodon-webbklient",
"name": "Elk",
"short_name": "Elk"
}
}
},
"report": {
"additional_comments": "Övriga kommentarer",
"another_server": "Användaren du anmäler är från en annan server",
"anything_else": "Är det något annat vi borde veta om?",
"block_desc": "Du kommer inte att se inlägg från den här användaren i fortsättningen. De kommer inte kunna se dina inlägg eller följa dig. De kommer kunna se att de är blockerade.",
"dontlike": "Jag gillar det inte",
"dontlike_desc": "Det är inget jag vill se",
"forward": "Ja, vidarebefordra denna anmälan till {0}",
"forward_question": "Vill du även skicka en anonymiserad kopia av denna anmälan till servern?",
"further_actions": {
"limit": {
"description": "Här är dina alternativ för att kontrollera vad du ser:",
"title": "Vill du inte se det här?"
},
"report": {
"description": "Medan vi granskar detta, så kan du ta följande åtgärder:",
"title": "Tack för din anmälan, vi kommer ta en titt på detta."
}
},
"limiting": "Begränsar {0}",
"mute_desc": "Du kommer inte att se inlägg från den här användaren i fortsättningen. De kommer fortfarande kunna följa dig och se dina inlägg. De kommer inte veta att de är tystade.",
"other": "Det är något annat",
"other_desc": "Problemet passar inte i någon av kategorierna",
"reporting": "Anmäler {0}",
"select_many": "Välj allt som stämmer in:",
"select_one": "Välj den bästa matchningen:",
"select_posts": "Finns det några inlägg som styrker denna anmälan?",
"select_posts_other": "Finns det några andra inlägg som styrker denna anmälan?",
"spam": "Det är skräppost",
"spam_desc": "Skadliga länkar, klicklockeri eller upprepande svar",
"submit": "Skicka in anmälan",
"unfollow_desc": "Du kommer inte att se inlägg från den här användaren i ditt flöde i fortsättningen. Du kan dock komma att se inlägg från den här användaren på andra ställen.",
"violation": "Det bryter mot en eller flera av serverreglerna",
"whats_wrong_account": "Berätta vad som är fel med det här kontot",
"whats_wrong_post": "Berätta vad som är fel med det här inlägget"
},
"search": {
"search_desc": "Sök efter personer & hashtaggar",
"search_empty": "Kunde inte hitta något med dessa söktermer"
},
"settings": {
"about": {
"built_at": "Byggd",
"label": "Om",
"meet_the_team": "Möt teamet",
"sponsor_action": "Sponsra oss",
"sponsor_action_desc": "Sponsra teamet som utvecklar Elk",
"sponsors": "Sponsorer",
"sponsors_body_1": "Elk möjliggörs tack vare det generösa stödet och hjälpen från:",
"sponsors_body_2": "Samt alla företag och individer som stödjer Elk-teamet och dess medlemmar.",
"sponsors_body_3": "Om du tycker om appen, överväg då att sponsra oss:",
"version": "Version"
},
"account_settings": {
"description": "Ändra dina kontoinställningar i Mastodon-gränssnittet",
"label": "Kontoinställningar"
},
"interface": {
"bottom_nav": "Nedre navigeringsfält",
"bottom_nav_instructions": "Välj upp till fem stycken favoritknappar för det nedre navigeringsfältet. Måste innehålla \"Mer\"-knappen",
"color_mode": "Färgläge",
"dark_mode": "Mörkt",
"default": " (standard)",
"font_size": "Teckenstorlek",
"label": "Gränssnitt",
"light_mode": "Ljust",
"system_mode": "System",
"theme_color": "Färgtema"
},
"language": {
"display_language": "Språk för gränssnittet",
"how_to_contribute": "Hur kan du bidra?",
"label": "Språk",
"post_language": "Inläggsspråk",
"status": "Översättningsstatus: {0}/{1} ({2}%)",
"translations": {
"add": "Lägg till",
"choose_language": "Välj språk",
"heading": "Översättningar",
"hide_specific": "Dölj specifika översättningar",
"remove": "Ta bort"
}
},
"notifications": {
"label": "Notiser",
"notifications": {
"label": "Inställningar för notiser"
},
"push_notifications": {
"alerts": {
"favourite": "Favoriter",
"follow": "Nya följare",
"mention": "Omnämnanden",
"poll": "Omröstningar",
"reblog": "Boostar",
"title": "Vilka notiser vill jag ta emot?"
},
"description": "Ta emot notiser även när du inte använder Elk.",
"instructions": "Glöm inte att spara dina ändringar med @:settings.notifications.push_notifications.save_settings -knappen!",
"label": "Inställningar för push-notiser",
"policy": {
"all": "Från vem som helst",
"followed": "Från personer jag följer",
"follower": "Från personer som följer mig",
"none": "Från ingen",
"title": "Vem kan jag ta emot notiser från?"
},
"save_settings": "Spara inställningar",
"subscription_error": {
"clear_error": "Rensa fel",
"error_hint": "Du kan konsultera en lista med vanliga frågor för att lösa problem: {0}.",
"invalid_vapid_key": "Den allmänna nyckeln för VAPID verkar vara ogiltig.",
"permission_denied": "Åtkomst nekad: aktivera notiser i din webbläsare.",
"repo_link": "Elks förvar på GitHub",
"request_error": "Ett fel inträffade när prenumerationen förfrågades. Prova igen, och om felet återstår, vänligen anmäl felet till Elks förvar på Github.",
"title": "Kunde inte prenumerera på push-notiser",
"too_many_registrations": "På grund av begränsningar i webbläsaren, kan Elk inte använda tjänsten för push-notiser för flera konton på olika servrar. Du bör avprenumerera från push-notiser på ett annat konto och försök igen.",
"vapid_not_supported": "Din webbläsare stödjer Web Push Notifications, men verkar inte ha implementerat VAPID-protokollet."
},
"title": "Inställningar för push-notiser",
"undo_settings": "Ångra ändringar",
"unsubscribe": "Avaktivera push-notiser",
"unsupported": "Din webbläsare stödjer inte push-notiser.",
"warning": {
"enable_close": "Stäng",
"enable_description": "För att ta emot notiser när Elk inte är öppet, aktivera push-notiser. Du kan kontrollera precis vilka typer av interaktioner som ska generera push-notiser via \"@:settings.notifications.show_btn{'\"'} -knappen ovan när det är aktiverat.",
"enable_description_desktop": "För att ta emot notiser när Elk inte är öppet, aktivera push-notiser. Du kan kontrollera precis vilka typer av interaktioner som ska generera push-notiser via \"Inställningar > Notiser > Inställningar för push-notiser\" när det är aktiverat.",
"enable_description_mobile": "Du kan även komma åt inställningarna genom att använda navigationsmenyn \"Inställningar > Notiser > Inställningar för push-notiser\".",
"enable_description_settings": "För att ta emot notiser när Elk inte är öppet, aktivera push-notiser. Du kan kontrollera precis vilka typer av interaktioner som ska generera push-notiser på denna sida när du aktiverat dem.",
"enable_desktop": "Aktivera push-notiser",
"enable_title": "Missa aldrig något",
"re_auth": "Det ser ut som att din server inte stödjer push-notiser. Prova att logga ut och logga in igen, och om detta meddelande fortfarande dyker upp så kontakta din serveradministratör."
}
},
"show_btn": "Gå till inställningar för notiser",
"under_construction": "Under uppbyggnad"
},
"notifications_settings": "Notiser",
"preferences": {
"embedded_media": "Inbäddad mediaspelare",
"embedded_media_description": "Visa en inbäddad mediaspelare istället för den vanliga förhandsvisningskortet när delade medialänkar öppnas.",
"enable_autoplay": "Aktivera automatisk uppspelning",
"enable_data_saving": "Aktivera datasparläge",
"enable_data_saving_description": "Spara på din dataförbrukning genom att förhindra att bilagor laddas automatiskt.",
"enable_pinch_to_zoom": "Aktivera nyp för att zooma",
"github_cards": "GitHub-kort",
"github_cards_description": "När ett inlägg har en GitHub-länk, visas ett HTML-kort med hjälp av social graph-metadata istället för bilden.",
"grayscale_mode": "Gråskaleläge",
"hide_account_hover_card": "Dölj konto-hoverkort",
"hide_alt_indi_on_posts": "Dölj alt-indikator på inlägg",
"hide_boost_count": "Dölj boost-räknare",
"hide_favorite_count": "Dölj favoriträknare",
"hide_follower_count": "Dölj följer/följarräknare",
"hide_gif_indi_on_posts": "Dölj gif-indikator på inlägg",
"hide_news": "Dölj nyheter",
"hide_reply_count": "Dölj svarsräknare",
"hide_tag_hover_card": "Dölj tagg-hoverkort",
"hide_translation": "Dölj översättningar",
"hide_username_emojis": "Dölj användarnamn-emojis",
"hide_username_emojis_description": "Döljer emojis på användarnamn i tidslinjer. Emojis kommer fortfarande vara synliga i deras profiler.",
"label": "Inställningar",
"optimize_for_low_performance_device": "Optimera för enhet med låg prestanda",
"title": "Experimentella funktioner",
"unmute_videos": "Videoljud på som standard",
"use_star_favorite_icon": "Använd stjärna som favoritikon",
"user_picker": "Användarväljare",
"user_picker_description": "Visar alla profilbilder för inloggade konton i det nedre vänstra hörnet så att du snabbt kan växla mellan dem.",
"virtual_scroll": "Virtuell skrollning",
"virtual_scroll_description": "Använder en virtuell lista i tidslinjer så att ett större antal objekt kan renderas följsamt.",
"wellbeing": "Välbefinnande",
"zen_mode": "Zen-läge",
"zen_mode_description": "Döljer flikar så länge muspekaren inte är över dem. Döljer även vissa element från tidslinjen."
},
"profile": {
"appearance": {
"bio": "Biografi",
"description": "Ändra profilbild, användarnamn, profil, etc.",
"display_name": "Visningsnamn",
"label": "Utseende",
"profile_metadata": "Profil-metadata",
"profile_metadata_desc": "Du kan ha upp till {0} objekt för visning som en tabell på din profil",
"profile_metadata_label": "Etikett",
"profile_metadata_value": "Innehåll",
"title": "Redigera profil"
},
"featured_tags": {
"description": "Folk kan bläddra bland dina offentliga inlägg under dessa hashtaggar.",
"label": "Utvalda hashtaggar",
"under_construction": "Under uppbyggnad"
},
"label": "Profil"
},
"select_a_settings": "Välj en inställning",
"users": {
"export": "Exportera användarnycklar",
"import": "Importera användarnycklar",
"label": "Inloggade användare"
}
},
"share_target": {
"description": "Elk kan konfigureras så att du kan dela innehåll från andra applikationer. Installera Elk på din enhet eller dator och logga in.",
"hint": "För att kunna dela innehåll med Elk så måste Elk vara installerat och du måste vara inloggad.",
"title": "Dela med Elk"
},
"state": {
"attachments_exceed_server_limit": "Antalet bilagor överstiger gränsen per inlägg.",
"attachments_limit_audio_error": "Maximal ljudfilsstorlek har överskridits: {0}",
"attachments_limit_error": "Gränsen per inlägg har överstigits",
"attachments_limit_image_error": "Maximal bildstorlek har överskridits: {0}",
"attachments_limit_unknown_error": "Maximal filstorlek har överskridits: {0}",
"attachments_limit_video_error": "Maximal videostorlek har överskridits: {0}",
"edited": "(Redigerad)",
"editing": "Redigerar",
"loading": "Laddar...",
"publish_failed": "Publicering misslyckades",
"publishing": "Publicerar",
"save_failed": "Sparningen misslyckades",
"upload_failed": "Uppladdning misslyckades",
"uploading": "Laddar upp..."
},
"status": {
"account": {
"suspended_message": "Kontot för detta inlägg har stängts av.",
"suspended_show": "Visa innehållet ändå?"
},
"boosted_by": "Boostad av",
"edited": "Redigerad {0}",
"embedded_warning": "Om du spelar detta så kan det avslöja din IP-adress för andra.",
"favourited_by": "Favoritmarkerad av",
"filter_hidden_phrase": "Filtrerad av",
"filter_show_anyway": "Visa ändå",
"gif": "GIF",
"img_alt": {
"ALT": "ALT",
"desc": "Beskrivning",
"dismiss": "Stäng",
"read": "Läs {0} beskrivning"
},
"pinned": "Fästa inlägg",
"poll": {
"count": "{0} röster|{0} röst|{0} röster",
"ends": "slutar {0}",
"finished": "avslutad {0}",
"update": "Uppdatera omröstning"
},
"replying_to": "Svarar till {0}",
"show_full_thread": "Visa hela tråden",
"someone": "någon",
"spoiler_media_hidden": "Media dold",
"spoiler_show_less": "Visa mindre",
"spoiler_show_more": "Visa mer",
"thread": "Tråd",
"try_original_site": "Försök på originalsidan"
},
"status_history": {
"created": "skapades {0}",
"edited": "redigerades {0}"
},
"tab": {
"accounts": "Konton",
"for_you": "För dig",
"hashtags": "Hashtaggar",
"list": "Lista",
"media": "Media",
"news": "Nyheter",
"notifications_admin": {
"report": "Anmäl",
"sign_up": "Registrera"
},
"notifications_all": "Alla",
"notifications_favourite": "Favoritmarkering",
"notifications_follow": "Följ",
"notifications_follow_request": "Följförfrågan",
"notifications_mention": "Omnämnanden",
"notifications_more_tooltip": "Filtrera notiser efter typ",
"notifications_poll": "Omröstning",
"notifications_reblog": "Boost",
"notifications_status": "Status",
"notifications_update": "Uppdatering",
"posts": "Inlägg",
"posts_with_replies": "Inlägg & svar"
},
"tag": {
"follow": "Följ",
"follow_label": "Följ {0}-taggen",
"unfollow": "Avfölj",
"unfollow_label": "Avfölj {0}-taggen"
},
"time_ago_options": {
"day_future": "om 0 dagar|imorgon|om {n} dagar",
"day_past": "0 dagar sedan|igår|{n} dagar sedan",
"hour_future": "om 0 timmar|om 1 timme|om {n} timmar",
"hour_past": "0 timmar sedan|1 timme sedan|{n} timmar sedan",
"just_now": "just nu",
"minute_future": "om 0 minuter|om 1 minut|om {n} minuter",
"minute_past": "0 minuter sedan|1 minut sedan|{n} minuter sedan",
"month_future": "om 0 månader|nästa månad|om {n} månader",
"month_past": "0 månader sedan|förra månaden|{n} månader sedan",
"second_future": "just nu|om {n} sekund|om {n} sekunder",
"second_past": "just nu|{n} sekund sedan|{n} sekunder sedan",
"short_day_future": "om {n}d",
"short_day_past": "{n}d",
"short_hour_future": "om {n}h",
"short_hour_past": "{n}h",
"short_minute_future": "om {n}min",
"short_minute_past": "{n}min",
"short_month_future": "om {n}mån",
"short_month_past": "{n}mån",
"short_second_future": "om {n}s",
"short_second_past": "{n}s",
"short_week_future": "om {n}v",
"short_week_past": "{n}v",
"short_year_future": "om {n}år",
"short_year_past": "{n}år",
"week_future": "om 0 veckor|nästa vecka|om {n} veckor",
"week_past": "0 veckor sedan|förra veckan|{n} veckor sedan",
"year_future": "om 0 år|nästa år|om {n} år",
"year_past": "0 år sedan|förra året|{n} år sedan"
},
"timeline": {
"no_posts": "Inga inlägg här!",
"show_new_items": "Visa {v} nya inlägg|Visa {v} nytt inlägg|Visa {v} nya inlägg",
"view_older_posts": "Äldre inlägg från andra instanser kanske inte visas."
},
"title": {
"federated_timeline": "Federerad tidslinje",
"local_timeline": "Lokal tidslinje"
},
"tooltip": {
"add_content_warning": "Lägg till innehållsvarning",
"add_emojis": "Lägg till emojis",
"add_media": "Lägg till bilder, video eller en ljudfil",
"add_publishable_content": "Lägg till innehåll för publicering",
"add_thread_item": "Lägg till inlägg i tråden",
"change_content_visibility": "Ändra innehållets synlighet",
"change_language": "Byt språk",
"emoji": "Emoji",
"explore_links_intro": "Dessa nyheter pratas det om just nu på denna och andra servrar på det decentraliserade nätverket.",
"explore_posts_intro": "Dessa inlägg från denna och andra servrar på det decentraliserade nätverket har fått fäste på denna server just nu.",
"explore_tags_intro": "Dessa hashtaggar har fått fäste bland folk på denna och andra servrar på det decentraliserade nätverket",
"open_editor_tools": "Redigeringsverktyg",
"pick_an_icon": "Välj en ikon",
"publish_failed": "Stäng misslyckade meddelanden på toppen av redigeringsverktyget för att åter publicera inläggen",
"remove_thread_item": "Ta bort inlägg från tråden",
"start_thread": "Starta tråd",
"toggle_bold": "Fet",
"toggle_code_block": "Kodblock",
"toggle_italic": "Kursiv"
},
"user": {
"add_existing": "Lägg till ett befintligt konto",
"server_address_label": "Mastodon-serveradress",
"sign_in_desc": "Logga in för att följa profiler eller hashtaggar samt favoritmarkera, dela och svara på inlägg, eller interagera från ditt konto på en annan server.",
"sign_in_notice_title": "Visar {0} offentliga data",
"sign_out_account": "Logga ut {0}",
"single_instance_sign_in_desc": "Logga in för att följa profiler eller hashtaggar, favoritmarkera, dela och svara på inlägg",
"tip_no_account": "Om du inte har ett Mastodon-konto än, {0}.",
"tip_register_account": "välj server och registrera ett"
},
"visibility": {
"direct": "Privat omnämnande",
"direct_desc": "Synligt endast för omnämnda användare",
"private": "Endast följare",
"private_desc": "Synligt endast för följare",
"public": "Offentlig",
"public_desc": "Synligt för alla",
"unlisted": "Offentlig (begränsad)",
"unlisted_desc": "Synligt för alla, men bortvald från upptäcktsfunktioner"
}
}

View file

@ -77,7 +77,7 @@
},
"app_desc_short": "เว็บหน้าบ้านมาสโตดอนที่ปราดเปรียว",
"app_logo": "โลโก้ Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "อธิบาย",
"remove_label": "เอาสิ่งแนบออก"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Isang mabilis na web client ng Mastodon",
"app_logo": "Logo ng Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Paglalarawan",
"remove_label": "Tanggalin ang attachment"

View file

@ -77,7 +77,7 @@
},
"app_desc_short": "Hızlı bir Mastodon web istemcisi",
"app_logo": "Elk Logosu",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Açıklama",
"remove_label": "Eki kaldır"

View file

@ -87,7 +87,7 @@
},
"app_desc_short": "Спритний вебклієнт для Mastodon",
"app_logo": "Лоґо Elk",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Опис",
"remove_label": "Вилучити вкладення"

View file

@ -89,7 +89,7 @@
},
"app_desc_short": "Một ứng dụng web Mastodon nhanh nhẹn",
"app_logo": "Elk Logo",
"app_name": "Elk",
"app_name": "Yolk",
"attachment": {
"edit_title": "Mô tả",
"remove_label": "Xóa file đính kèm"

View file

@ -1,5 +1,5 @@
import type { BuildInfo } from '#shared/types'
import { createResolver, defineNuxtModule } from '@nuxt/kit'
import type { BuildInfo } from '../shared/types'
import { createResolver, defineNuxtModule } from 'nuxt/kit'
import { isCI } from 'std-env'
import { getEnv, version } from '../config/env'

View file

@ -1,5 +1,5 @@
import { lstat } from 'node:fs'
import { createResolver, defineNuxtModule } from '@nuxt/kit'
import { createResolver, defineNuxtModule } from 'nuxt/kit'
import { currentLocales } from '../config/i18n'
const virtual = 'virtual:emoji-mart-lang-importer'

View file

@ -1,5 +1,5 @@
import { addVitePlugin, defineNuxtModule } from '@nuxt/kit'
import MagicString from 'magic-string'
import { addVitePlugin, defineNuxtModule } from 'nuxt/kit'
export default defineNuxtModule({
meta: {

View file

@ -1,7 +1,7 @@
import type { ManifestOptions } from 'vite-plugin-pwa'
import { Buffer } from 'node:buffer'
import { readFile } from 'node:fs/promises'
import { createResolver } from '@nuxt/kit'
import { createResolver } from 'nuxt/kit'
import { THEME_COLORS } from '../../app/constants/index'
import { getEnv } from '../../config/env'
import { currentLocales } from '../../config/i18n'

View file

@ -6,7 +6,7 @@ import type { VitePWANuxtOptions } from './types'
import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'
import { addPlugin, createResolver, defineNuxtModule } from 'nuxt/kit'
import { join, resolve } from 'pathe'
import { VitePWA } from 'vite-plugin-pwa'
import { configurePWAOptions } from './config'

View file

@ -1,5 +1,4 @@
import type { Ref } from 'vue'
import type { UnwrapNestedRefs } from 'vue'
import type { Ref, UnwrapNestedRefs } from 'vue'
export interface PwaInjection {
isInstalled: boolean

View file

@ -1,68 +0,0 @@
import { rm } from 'node:fs/promises'
import { addImports, addImportsSources, addPlugin, createResolver, defineNuxtModule, useNuxt } from '@nuxt/kit'
import { resolveModulePath } from 'exsolve'
const mockProxy = resolveModulePath('mocked-exports/proxy', { from: import.meta.url })
export default defineNuxtModule({
meta: {
name: 'tauri',
},
setup() {
const nuxt = useNuxt()
const { resolve } = createResolver(import.meta.url)
if (!process.env.TAURI_PLATFORM)
return
if (nuxt.options.dev)
nuxt.options.ssr = false
nuxt.options.pwa.disable = true
nuxt.options.sourcemap.client = false
nuxt.options.alias = {
...nuxt.options.alias,
'unstorage/drivers/fs': mockProxy,
'unstorage/drivers/cloudflare-kv-http': mockProxy,
'#storage-config': resolve('./runtime/storage-config'),
'node:events': 'unenv/runtime/node/events/index',
'#build-info': resolve('./runtime/build-info'),
}
nuxt.hook('vite:extend', ({ config }) => {
config.build!.target = ['chrome100', 'safari15']
config.envPrefix = [...config.envPrefix || [], 'VITE_', 'TAURI_']
})
// prevent creation of server routes
nuxt.hook('nitro:config', (config) => {
config.srcDir = './_nonexistent'
config.scanDirs = []
})
addImportsSources({
from: 'h3',
imports: ['defineEventHandler', 'getQuery', 'getRouterParams', 'readBody', 'sendRedirect'] as Array<keyof typeof import('h3')>,
})
nuxt.options.imports.dirs = nuxt.options.imports.dirs || []
nuxt.options.imports.dirs.push(resolve('../../server/utils'))
addImports({ name: 'useStorage', from: resolve('./runtime/storage') })
addPlugin(resolve('./runtime/logging.client'))
addPlugin(resolve('./runtime/nitro.client'))
// cleanup files copied from the public folder that we don't need
nuxt.hook('close', async () => {
await rm('.output/public/_redirects')
await rm('.output/public/apple-touch-icon.png')
await rm('.output/public/elk-og.png')
await rm('.output/public/favicon.ico')
await rm('.output/public/pwa-192x192.png')
await rm('.output/public/pwa-512x512.png')
await rm('.output/public/robots.txt')
})
},
})

View file

@ -1 +0,0 @@
export const env = useAppConfig().env

View file

@ -1,18 +0,0 @@
import * as log from 'tauri-plugin-log-api'
// When running inside Tauri, catch all logs from 3rd party packages and direct them to the unified logging stream
export default defineNuxtPlugin(() => {
// eslint-disable-next-line no-global-assign
console = {
...console,
trace: log.trace,
debug: log.debug,
log: log.info,
warn: log.warn,
error: log.error,
}
window.addEventListener('unhandledrejection', err =>
log.error(err.reason))
window.addEventListener('error', err => log.error(err.error), true)
})

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