+
diff --git a/app/components/modal/DurationPicker.vue b/app/components/modal/DurationPicker.vue
index 897dcd02..90614ae9 100644
--- a/app/components/modal/DurationPicker.vue
+++ b/app/components/modal/DurationPicker.vue
@@ -13,9 +13,9 @@ watchEffect(() => {
}
const duration
- = days.value * 24 * 60 * 60
- + hours.value * 60 * 60
- + minutes.value * 60
+ = days.value * 24 * 60 * 60
+ + hours.value * 60 * 60
+ + minutes.value * 60
if (duration <= 0) {
isValid.value = false
diff --git a/app/components/modal/ModalContainer.vue b/app/components/modal/ModalContainer.vue
index 28f43b62..a8fb7896 100644
--- a/app/components/modal/ModalContainer.vue
+++ b/app/components/modal/ModalContainer.vue
@@ -82,7 +82,7 @@ function handleFavouritedBoostedByClose() {
>
-
+
diff --git a/app/components/modal/ModalDialog.vue b/app/components/modal/ModalDialog.vue
index d6ae88d2..d88836a9 100644
--- a/app/components/modal/ModalDialog.vue
+++ b/app/components/modal/ModalDialog.vue
@@ -10,6 +10,7 @@ const {
closeByMask = true,
useVIf = true,
keepAlive = false,
+ focusFirstElement = true,
} = defineProps<{
// level of depth
zIndex?: number
@@ -21,6 +22,8 @@ const {
keepAlive?: boolean
// The aria-labelledby id for the dialog.
dialogLabelledBy?: string
+ // Whether to focus on the first element when the modal opens.
+ focusFirstElement?: boolean
}>()
const emit = defineEmits<{
@@ -45,6 +48,7 @@ const { activate } = useFocusTrap(elDialogRoot, {
escapeDeactivates: true,
preventScroll: true,
returnFocusOnDeactivate: true,
+ initialFocus: focusFirstElement ? undefined : false,
})
defineExpose({
diff --git a/app/components/modal/ModalMediaPreviewCarousel.vue b/app/components/modal/ModalMediaPreviewCarousel.vue
index 43eb6f7d..2e56e608 100644
--- a/app/components/modal/ModalMediaPreviewCarousel.vue
+++ b/app/components/modal/ModalMediaPreviewCarousel.vue
@@ -159,14 +159,16 @@ function handleTap([positionX, positionY]: Vector2) {
goToFocusedSlide()
}
else {
- const focusedSlideBounding = slide.value[modelValue.value].getBoundingClientRect()
- const slideCenterX = focusedSlideBounding.left + focusedSlideBounding.width / 2
- const slideCenterY = focusedSlideBounding.top + focusedSlideBounding.height / 2
+ const focusedSlideBounding = slide.value[modelValue.value]?.getBoundingClientRect()
+ if (focusedSlideBounding) {
+ const slideCenterX = focusedSlideBounding.left + focusedSlideBounding.width / 2
+ const slideCenterY = focusedSlideBounding.top + focusedSlideBounding.height / 2
- scale.value = 3
- x.value += positionX - slideCenterX
- y.value += positionY - slideCenterY
- restrictShiftToInsideSlide()
+ scale.value = 3
+ x.value += positionX - slideCenterX
+ y.value += positionY - slideCenterY
+ restrictShiftToInsideSlide()
+ }
}
}
diff --git a/app/components/nav/NavLogo.vue b/app/components/nav/NavLogo.vue
index 6fc5549d..5bb827a0 100644
--- a/app/components/nav/NavLogo.vue
+++ b/app/components/nav/NavLogo.vue
@@ -2,47 +2,13 @@
-
+
+
+
diff --git a/app/components/nav/NavTitle.vue b/app/components/nav/NavTitle.vue
index 630f425c..40306b7a 100644
--- a/app/components/nav/NavTitle.vue
+++ b/app/components/nav/NavTitle.vue
@@ -18,7 +18,7 @@ router.afterEach(() => {
-
+
{
@click.prevent="onClickLogo"
>
-
- {{ $t('app_name') }}
{{ env === 'release' ? 'alpha' : env }}
+
+ {{ $t('app_name') }}
+ {{ env === 'release' ? 'alpha' : env }}
diff --git a/app/components/timeline/TimelineHome.vue b/app/components/timeline/TimelineHome.vue
index 2c35fcf8..a0ed7cdf 100644
--- a/app/components/timeline/TimelineHome.vue
+++ b/app/components/timeline/TimelineHome.vue
@@ -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
+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 ?? []
}
diff --git a/app/components/timeline/TimelineNotifications.vue b/app/components/timeline/TimelineNotifications.vue
index b966b7d4..035446e9 100644
--- a/app/components/timeline/TimelineNotifications.vue
+++ b/app/components/timeline/TimelineNotifications.vue
@@ -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
+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\/?/, '')
diff --git a/app/components/timeline/TimelinePublic.vue b/app/components/timeline/TimelinePublic.vue
index 1ee571b2..73da7b74 100644
--- a/app/components/timeline/TimelinePublic.vue
+++ b/app/components/timeline/TimelinePublic.vue
@@ -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
+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 ?? []
}
diff --git a/app/components/timeline/TimelinePublicLocal.vue b/app/components/timeline/TimelinePublicLocal.vue
index 8a1312f4..c7de4c64 100644
--- a/app/components/timeline/TimelinePublicLocal.vue
+++ b/app/components/timeline/TimelinePublicLocal.vue
@@ -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
+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 ?? []
}
diff --git a/app/composables/cache.ts b/app/composables/cache.ts
index 83d25c05..27f8f9de 100644
--- a/app/composables/cache.ts
+++ b/app/composables/cache.ts
@@ -74,7 +74,7 @@ export async function fetchAccountByHandle(acct: string): Promise {
const fuse = (lastScope === scope && lastFuse)
? lastFuse
: new Fuse(cmds, {
- keys: ['scope', 'name', 'description'],
- includeScore: true,
- })
+ keys: ['scope', 'name', 'description'],
+ includeScore: true,
+ })
lastScope = scope
lastFuse = fuse
diff --git a/app/composables/content-parse.ts b/app/composables/content-parse.ts
index 3f749e74..385ded04 100644
--- a/app/composables/content-parse.ts
+++ b/app/composables/content-parse.ts
@@ -67,6 +67,11 @@ const sanitizer = sanitize({
li: {
value: keep,
},
+ // Hollo supports 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, '>')
.replace(/`/g, '`')
+ .replace(/\*/g, '*')
const classes = lang ? ` class="language-${lang}"` : ''
return `>${code} `
})
.replace(/`([^`\n]*)`/g, (_1, raw) => {
- return raw ? `${htmlToText(raw).replace(//g, '>')}` : ''
+ return raw ? `${htmlToText(raw).replace(//g, '>').replace(/\*/g, '*')}` : ''
})
}
diff --git a/app/composables/masto/masto.ts b/app/composables/masto/masto.ts
index 30dfd987..3a217519 100644
--- a/app/composables/masto/masto.ts
+++ b/app/composables/masto/masto.ts
@@ -26,54 +26,59 @@ export function mastoLogin(masto: ElkMasto, user: Pick {
- return streamingApiUrl ? createStreamingAPIClient({ streamingApiUrl, accessToken, implementation: globalThis.WebSocket }) : undefined
- }
+ let createStreamingClient: (streamingApiUrl: string | undefined) => mastodon.streaming.Client | 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
- masto.client.value.v2.instance.fetch().catch(error => new Promise((resolve, reject) => {
- if (error instanceof MastoHttpError && error.statusCode === 404) {
- return masto.client.value.v1.instance.fetch().then((newInstance) => {
- console.warn(`Instance ${server} on version ${newInstance.version} does not support "GET /api/v2/instance" API, try converting to v2 instance... expect some errors`)
- const v2Instance = {
- ...newInstance,
- domain: newInstance.uri,
- sourceUrl: '',
- usage: {
- users: {
- activeMonth: 0,
- },
- },
- icon: [],
- apiVersions: {
- mastodon: newInstance.version,
- },
- contact: {
- email: newInstance.email,
- },
- configuration: {
- ...(newInstance.configuration ?? {}),
- urls: {
- streaming: newInstance.urls.streamingApi,
- },
- },
- } as unknown as mastodon.v2.Instance
- return resolve(v2Instance)
- }).catch(reject)
+ if (currentUser.value !== undefined) {
+ createStreamingClient = (streamingApiUrl: string | undefined) => {
+ return streamingApiUrl ? createStreamingAPIClient({ streamingApiUrl, accessToken, implementation: globalThis.WebSocket }) : undefined
}
- return reject(error)
- })).then((newInstance) => {
- Object.assign(instance, newInstance)
- if (newInstance.configuration.urls.streaming !== streamingApiUrl)
- masto.streamingClient.value = createStreamingClient(newInstance.configuration.urls.streaming)
+ const streamingApiUrl = instance?.configuration?.urls?.streaming
+ masto.streamingClient.value = createStreamingClient(streamingApiUrl)
- instanceStorage.value[server] = newInstance
- })
+ // Refetch instance info in the background on login
+ masto.client.value.v2.instance.fetch().catch(error => new Promise((resolve, reject) => {
+ if (error instanceof MastoHttpError && error.statusCode === 404) {
+ return masto.client.value.v1.instance.fetch().then((newInstance) => {
+ console.warn(`Instance ${server} on version ${newInstance.version} does not support "GET /api/v2/instance" API, try converting to v2 instance... expect some errors`)
+ const v2Instance = {
+ ...newInstance,
+ domain: newInstance.uri,
+ sourceUrl: '',
+ usage: {
+ users: {
+ activeMonth: 0,
+ },
+ },
+ icon: [],
+ apiVersions: {
+ mastodon: newInstance.version,
+ },
+ contact: {
+ email: newInstance.email,
+ },
+ configuration: {
+ ...(newInstance.configuration ?? {}),
+ urls: {
+ streaming: newInstance.urls.streamingApi,
+ },
+ },
+ } as unknown as mastodon.v2.Instance
+ return resolve(v2Instance)
+ }).catch(reject)
+ }
+
+ return reject(error)
+ })).then((newInstance) => {
+ Object.assign(instance, newInstance)
+ if (newInstance.configuration.urls.streaming !== streamingApiUrl)
+ masto.streamingClient.value = createStreamingClient(newInstance.configuration.urls.streaming)
+
+ instanceStorage.value[server] = newInstance
+ })
+ }
return instance
}
diff --git a/app/composables/masto/notification.ts b/app/composables/masto/notification.ts
index 20939bec..6b474b30 100644
--- a/app/composables/masto/notification.ts
+++ b/app/composables/masto/notification.ts
@@ -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)
diff --git a/app/composables/masto/relationship.ts b/app/composables/masto/relationship.ts
index 4e5349f1..4d9279dd 100644
--- a/app/composables/masto/relationship.ts
+++ b/app/composables/masto/relationship.ts
@@ -95,9 +95,9 @@ export async function toggleMuteAccount(relationship: mastodon.v1.Relationship,
relationship!.muting = !relationship!.muting
relationship = relationship!.muting
? await client.value.v1.accounts.$select(account.id).mute({
- duration,
- notifications,
- })
+ duration,
+ notifications,
+ })
: await client.value.v1.accounts.$select(account.id).unmute()
}
diff --git a/app/composables/masto/search.ts b/app/composables/masto/search.ts
index e6215ea8..c0ff40f9 100644
--- a/app/composables/masto/search.ts
+++ b/app/composables/masto/search.ts
@@ -77,7 +77,7 @@ export function useSearch(query: MaybeRefOrGetter, 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, options: UseSearchOpt
return
loading.value = true
- const nextResults = await paginator.next()
+ const nextResults = await paginator.values().next()
loading.value = false
done.value = !!nextResults.done
diff --git a/app/composables/paginator.ts b/app/composables/paginator.ts
index 93aeed1f..674193e9 100644
--- a/app/composables/paginator.ts
+++ b/app/composables/paginator.ts
@@ -3,7 +3,7 @@ import type { mastodon } from 'masto'
import type { Ref } from 'vue'
export function usePaginator(
- _paginator: mastodon.Paginator,
+ paginator: mastodon.Paginator,
stream: Ref,
eventType: 'update' | 'notification' = 'update',
preprocess: (items: (T | U)[]) => U[] = items => items as unknown as U[],
@@ -12,8 +12,8 @@ export function usePaginator(
// 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(isHydrated.value ? 'idle' : 'loading')
const items = ref([])
const nextItems = ref([])
@@ -75,7 +75,7 @@ export function usePaginator(
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)[])
diff --git a/app/composables/tiptap/shiki-parser.ts b/app/composables/tiptap/shiki-parser.ts
index 00c23a13..47daaac5 100644
--- a/app/composables/tiptap/shiki-parser.ts
+++ b/app/composables/tiptap/shiki-parser.ts
@@ -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)
diff --git a/app/composables/tiptap/suggestion.ts b/app/composables/tiptap/suggestion.ts
index fdaa24cd..36e42662 100644
--- a/app/composables/tiptap/suggestion.ts
+++ b/app/composables/tiptap/suggestion.ts
@@ -28,7 +28,7 @@ export const TiptapMentionSuggestion: Partial = 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 = {
resolve: false,
excludeUnreviewed: true,
})
- return (await paginator.next()).value?.hashtags ?? []
+ return (await paginator.values().next()).value?.hashtags ?? []
},
render: createSuggestionRenderer(TiptapHashtagList),
}
diff --git a/app/constants/index.ts b/app/constants/index.ts
index 9a82b5e3..455c72d4 100644
--- a/app/constants/index.ts
+++ b/app/constants/index.ts
@@ -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'
diff --git a/app/layouts/default.vue b/app/layouts/default.vue
index 4aa326eb..5f0e6dfd 100644
--- a/app/layouts/default.vue
+++ b/app/layouts/default.vue
@@ -16,9 +16,9 @@ const instance = instanceStorage.value[currentServer.value]
-
-
-
+
+
+
@@ -60,7 +60,7 @@ const instance = instanceStorage.value[currentServer.value]
-
+
diff --git a/app/middleware/1.permalink.global.ts b/app/middleware/1.permalink.global.ts
index 754d0815..7aa2740a 100644
--- a/app/middleware/1.permalink.global.ts
+++ b/app/middleware/1.permalink.global.ts
@@ -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])
diff --git a/app/pages/[[server]]/index.vue b/app/pages/[[server]]/index.vue
index 3f16bbdd..35ff0e3a 100644
--- a/app/pages/[[server]]/index.vue
+++ b/app/pages/[[server]]/index.vue
@@ -10,6 +10,6 @@ catch (err) {
-
+
diff --git a/app/pages/[[server]]/list/[list]/index/index.vue b/app/pages/[[server]]/list/[list]/index/index.vue
index 8dc5a697..6d991fab 100644
--- a/app/pages/[[server]]/list/[list]/index/index.vue
+++ b/app/pages/[[server]]/list/[list]/index/index.vue
@@ -1,4 +1,6 @@
diff --git a/app/pages/[[server]]/tags/[tag].vue b/app/pages/[[server]]/tags/[tag].vue
index 913ccc27..4ce3494f 100644
--- a/app/pages/[[server]]/tags/[tag].vue
+++ b/app/pages/[[server]]/tags/[tag].vue
@@ -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
+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 ?? []
}
diff --git a/app/plugins/magic-keys.client.ts b/app/plugins/magic-keys.client.ts
index 84128344..afc7f934 100644
--- a/app/plugins/magic-keys.client.ts
+++ b/app/plugins/magic-keys.client.ts
@@ -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
diff --git a/app/plugins/setup-i18n.ts b/app/plugins/setup-i18n.ts
index 9c292159..18c3fbd9 100644
--- a/app/plugins/setup-i18n.ts
+++ b/app/plugins/setup-i18n.ts
@@ -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 })
}
diff --git a/config/env.ts b/config/env.ts
index d2d31d2d..af31f1e9 100644
--- a/config/env.ts
+++ b/config/env.ts
@@ -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`.
*/
diff --git a/config/i18n.ts b/config/i18n.ts
index 91d7dc1f..f7bb7a07 100644
--- a/config/i18n.ts
+++ b/config/i18n.ts
@@ -75,13 +75,11 @@ export const countryLocaleVariants: Record[])
+ }, [] 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
-}, {})
+}, {} 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
-}, {})
+}, {} 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
-}, {})
+}, {} as PluralizationRules)
diff --git a/docker-compose.yaml b/docker-compose.yaml
index f8f35794..138ba32f 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -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
diff --git a/docs/app.config.ts b/docs/app.config.ts
index 1c7583dd..723909dd 100644
--- a/docs/app.config.ts
+++ b/docs/app.config.ts
@@ -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',
- },
+ description: 'A nimble Mastodon web client with modern features and elegant design.',
+ },
+ header: {
+ title: 'Elk',
+ logo: {
+ alt: 'Elk',
+ light: '/logo.svg',
+ dark: '/logo.svg',
},
- aside: {
- level: 0,
- exclude: [],
- },
- header: {
- logo: true,
- showLinkIcon: true,
- exclude: [],
- },
- 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',
},
],
},
diff --git a/docs/app.vue b/docs/app.vue
deleted file mode 100644
index e19315ee..00000000
--- a/docs/app.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/docs/app/assets/css/main.css b/docs/app/assets/css/main.css
new file mode 100644
index 00000000..53994ce2
--- /dev/null
+++ b/docs/app/assets/css/main.css
@@ -0,0 +1,4 @@
+/* Elk brand colors for light and dark modes */
+:root {
+ --ui-primary: #f0943c;
+}
diff --git a/docs/components/global/ClipboardIcon.vue b/docs/app/components/content/ClipboardIcon.vue
similarity index 100%
rename from docs/components/global/ClipboardIcon.vue
rename to docs/app/components/content/ClipboardIcon.vue
diff --git a/docs/components/global/IconMastodon.vue b/docs/app/components/content/IconMastodon.vue
similarity index 100%
rename from docs/components/global/IconMastodon.vue
rename to docs/app/components/content/IconMastodon.vue
diff --git a/docs/components/global/Logo.vue b/docs/app/components/content/Logo.vue
similarity index 100%
rename from docs/components/global/Logo.vue
rename to docs/app/components/content/Logo.vue
diff --git a/docs/components/global/ToggleIcon.vue b/docs/app/components/content/ToggleIcon.vue
similarity index 100%
rename from docs/components/global/ToggleIcon.vue
rename to docs/app/components/content/ToggleIcon.vue
diff --git a/docs/components/global/TranslationState.vue b/docs/app/components/content/TranslationState.vue
similarity index 97%
rename from docs/components/global/TranslationState.vue
rename to docs/app/components/content/TranslationState.vue
index 0dd06865..63980fcf 100644
--- a/docs/components/global/TranslationState.vue
+++ b/docs/app/components/content/TranslationState.vue
@@ -1,9 +1,9 @@