feat(pwa): add screenshots and orientation to webmanifest (#2109)
@ -136,9 +136,10 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
|||||||
let failedAttachments = $ref<MediaAttachmentUploadError[]>([])
|
let failedAttachments = $ref<MediaAttachmentUploadError[]>([])
|
||||||
const dropZoneRef = ref<HTMLDivElement>()
|
const dropZoneRef = ref<HTMLDivElement>()
|
||||||
|
|
||||||
const maxPixels
|
const maxPixels = $computed(() => {
|
||||||
= currentInstance.value!.configuration?.mediaAttachments?.imageMatrixLimit
|
return currentInstance.value?.configuration?.mediaAttachments?.imageMatrixLimit
|
||||||
?? 4096 ** 2
|
?? 4096 ** 2
|
||||||
|
})
|
||||||
|
|
||||||
const loadImage = (inputFile: Blob) => new Promise<HTMLImageElement>((resolve, reject) => {
|
const loadImage = (inputFile: Blob) => new Promise<HTMLImageElement>((resolve, reject) => {
|
||||||
const url = URL.createObjectURL(inputFile)
|
const url = URL.createObjectURL(inputFile)
|
||||||
|
@ -323,6 +323,10 @@
|
|||||||
"dismiss": "Dismiss",
|
"dismiss": "Dismiss",
|
||||||
"install": "Install",
|
"install": "Install",
|
||||||
"install_title": "Install Elk",
|
"install_title": "Install Elk",
|
||||||
|
"screenshots": {
|
||||||
|
"dark": "Screenshot of Elk running in dark mode",
|
||||||
|
"light": "Screenshot of Elk running in light mode"
|
||||||
|
},
|
||||||
"title": "New Elk update available!",
|
"title": "New Elk update available!",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"update_available_short": "Update Elk",
|
"update_available_short": "Update Elk",
|
||||||
|
@ -311,6 +311,10 @@
|
|||||||
"dismiss": "Descartar",
|
"dismiss": "Descartar",
|
||||||
"install": "Instalar",
|
"install": "Instalar",
|
||||||
"install_title": "Instalar Elk",
|
"install_title": "Instalar Elk",
|
||||||
|
"screenshots": {
|
||||||
|
"dark": "Captura de pantalla de Elk ejecutándose en modo oscuro",
|
||||||
|
"light": "Captura de pantalla de Elk ejecutándose en modo claro"
|
||||||
|
},
|
||||||
"title": "Nueva versión de Elk disponible",
|
"title": "Nueva versión de Elk disponible",
|
||||||
"update": "Actualizar",
|
"update": "Actualizar",
|
||||||
"update_available_short": "Actualiza Elk",
|
"update_available_short": "Actualiza Elk",
|
||||||
|
@ -6,48 +6,124 @@ import { getEnv } from '../../config/env'
|
|||||||
import { i18n } from '../../config/i18n'
|
import { i18n } from '../../config/i18n'
|
||||||
import type { LocaleObject } from '#i18n'
|
import type { LocaleObject } from '#i18n'
|
||||||
|
|
||||||
// We have to extend the ManifestOptions interface from 'vite-plugin-pwa'
|
export type LocalizedWebManifest = Record<string, Partial<ManifestOptions>>
|
||||||
// as that interface doesn't define the share_target field of Web App Manifests.
|
|
||||||
interface ExtendedManifestOptions extends ManifestOptions {
|
|
||||||
share_target: {
|
|
||||||
action: string
|
|
||||||
method: string
|
|
||||||
enctype: string
|
|
||||||
params: {
|
|
||||||
title: string
|
|
||||||
text: string
|
|
||||||
url: string
|
|
||||||
files: [{
|
|
||||||
name: string
|
|
||||||
accept: string[]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type LocalizedWebManifest = Record<string, Partial<ExtendedManifestOptions>>
|
|
||||||
|
|
||||||
export const pwaLocales = i18n.locales as LocaleObject[]
|
export const pwaLocales = i18n.locales as LocaleObject[]
|
||||||
|
|
||||||
type WebManifestEntry = Pick<ExtendedManifestOptions, 'name' | 'short_name' | 'description'>
|
type WebManifestEntry = Pick<ManifestOptions, 'name' | 'short_name' | 'description' | 'screenshots' | 'shortcuts'>
|
||||||
type RequiredWebManifestEntry = Required<WebManifestEntry & Pick<ExtendedManifestOptions, 'dir' | 'lang'>>
|
type RequiredWebManifestEntry = Required<WebManifestEntry & Pick<ManifestOptions, 'dir' | 'lang' | 'screenshots' | 'shortcuts'>>
|
||||||
|
|
||||||
export async function createI18n(): Promise<LocalizedWebManifest> {
|
export async function createI18n(): Promise<LocalizedWebManifest> {
|
||||||
const { env } = await getEnv()
|
const { env } = await getEnv()
|
||||||
const envName = `${env === 'release' ? '' : `(${env})`}`
|
const envName = `${env === 'release' ? '' : `(${env})`}`
|
||||||
const { pwa } = await readI18nFile('en.json')
|
const { action, nav, pwa } = await readI18nFile('en.json')
|
||||||
|
|
||||||
const defaultManifest: Required<WebManifestEntry> = pwa.webmanifest[env]
|
const defaultManifest: Required<WebManifestEntry> = pwa.webmanifest[env]
|
||||||
|
|
||||||
|
const defaultShortcuts: ManifestOptions['shortcuts'] = [{
|
||||||
|
name: nav.home,
|
||||||
|
url: '/home',
|
||||||
|
icons: [
|
||||||
|
{ src: 'shortcuts/home-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||||
|
{ src: 'shortcuts/home.png', sizes: '192x192', type: 'image/png' },
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
name: nav.local,
|
||||||
|
url: '/',
|
||||||
|
icons: [
|
||||||
|
{ src: 'shortcuts/local-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||||
|
{ src: 'shortcuts/local.png', sizes: '192x192', type: 'image/png' },
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
name: nav.notifications,
|
||||||
|
url: '/notifications',
|
||||||
|
icons: [
|
||||||
|
{ src: 'shortcuts/notifications-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||||
|
{ src: 'shortcuts/notifications.png', sizes: '192x192', type: 'image/png' },
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
name: action.compose,
|
||||||
|
url: '/compose',
|
||||||
|
icons: [
|
||||||
|
{ src: 'shortcuts/compose-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||||
|
{ src: 'shortcuts/compose.png', sizes: '192x192', type: 'image/png' },
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
name: nav.settings,
|
||||||
|
url: '/settings',
|
||||||
|
icons: [
|
||||||
|
{ src: 'shortcuts/settings-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||||
|
{ src: 'shortcuts/settings.png', sizes: '192x192', type: 'image/png' },
|
||||||
|
],
|
||||||
|
}]
|
||||||
|
|
||||||
|
const defaultScreenshots: ManifestOptions['screenshots'] = [{
|
||||||
|
src: 'screenshots/dark-1.webp',
|
||||||
|
sizes: '3840x2400',
|
||||||
|
type: 'image/webp',
|
||||||
|
label: pwa.screenshots.dark,
|
||||||
|
}, {
|
||||||
|
src: 'screenshots/light-1.webp',
|
||||||
|
sizes: '3840x2400',
|
||||||
|
type: 'image/webp',
|
||||||
|
label: pwa.screenshots.light,
|
||||||
|
}]
|
||||||
|
|
||||||
|
const manifestEntries: Partial<ManifestOptions> = {
|
||||||
|
scope: '/',
|
||||||
|
id: '/',
|
||||||
|
start_url: '/',
|
||||||
|
orientation: 'natural',
|
||||||
|
display: 'standalone',
|
||||||
|
display_override: ['window-controls-overlay'],
|
||||||
|
categories: ['social', 'social networking'],
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: 'pwa-192x192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'pwa-512x512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'any',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'maskable-icon.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'maskable',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
share_target: {
|
||||||
|
action: '/web-share-target',
|
||||||
|
method: 'POST',
|
||||||
|
enctype: 'multipart/form-data',
|
||||||
|
params: {
|
||||||
|
title: 'title',
|
||||||
|
text: 'text',
|
||||||
|
url: 'url',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
name: 'files',
|
||||||
|
accept: ['image/*', 'video/*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const locales: RequiredWebManifestEntry[] = await Promise.all(
|
const locales: RequiredWebManifestEntry[] = await Promise.all(
|
||||||
pwaLocales
|
pwaLocales
|
||||||
.filter(l => l.code !== 'en-US')
|
.filter(l => l.code !== 'en-US')
|
||||||
.map(async ({ code, dir = 'ltr', file, files }) => {
|
.map(async ({ code, dir = 'ltr', file, files }) => {
|
||||||
// read locale file or files
|
// read locale file or files
|
||||||
const { pwa, app_name, app_desc_short } = file
|
const { action, app_desc_short, app_name, nav, pwa } = file
|
||||||
? await readI18nFile(file)
|
? await readI18nFile(file)
|
||||||
: await findBestWebManifestData(files, env)
|
: await findBestWebManifestData(files, env)
|
||||||
const entry: WebManifestEntry = pwa?.webmanifest?.[env] ?? {}
|
const entry = pwa?.webmanifest?.[env] ?? {}
|
||||||
|
|
||||||
if (!entry.name && app_name)
|
if (!entry.name && app_name)
|
||||||
entry.name = dir === 'rtl' ? `${envName} ${app_name}` : `${app_name} ${envName}`
|
entry.name = dir === 'rtl' ? `${envName} ${app_name}` : `${app_name} ${envName}`
|
||||||
|
|
||||||
@ -57,11 +133,45 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
|
|||||||
if (!entry.description && app_desc_short)
|
if (!entry.description && app_desc_short)
|
||||||
entry.description = app_desc_short
|
entry.description = app_desc_short
|
||||||
|
|
||||||
|
// clone default screenshots and shortcuts
|
||||||
|
const useScreenshots = [...defaultScreenshots.map(screenshot => ({ ...screenshot }))]
|
||||||
|
const useShortcuts = [...defaultShortcuts.map(shortcut => ({ ...shortcut }))]
|
||||||
|
|
||||||
|
const pwaScreenshots = pwa?.screenshots
|
||||||
|
if (pwaScreenshots) {
|
||||||
|
useScreenshots.forEach((screenshot, idx) => {
|
||||||
|
if (idx === 0 && pwaScreenshots?.dark)
|
||||||
|
screenshot.label = pwaScreenshots.dark
|
||||||
|
|
||||||
|
if (idx === 1 && pwaScreenshots?.light)
|
||||||
|
screenshot.label = pwaScreenshots.light
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useShortcuts.forEach((shortcut, idx) => {
|
||||||
|
if (idx === 0 && nav?.home)
|
||||||
|
shortcut.name = nav.home
|
||||||
|
|
||||||
|
if (idx === 1 && nav?.local)
|
||||||
|
shortcut.name = nav.local
|
||||||
|
|
||||||
|
if (idx === 2 && nav?.notifications)
|
||||||
|
shortcut.name = nav.notifications
|
||||||
|
|
||||||
|
if (idx === 3 && action?.compose)
|
||||||
|
shortcut.name = action?.compose
|
||||||
|
|
||||||
|
if (idx === 4 && nav?.settings)
|
||||||
|
shortcut.name = nav.settings
|
||||||
|
})
|
||||||
|
|
||||||
return <RequiredWebManifestEntry>{
|
return <RequiredWebManifestEntry>{
|
||||||
...defaultManifest,
|
...defaultManifest,
|
||||||
...entry,
|
...entry,
|
||||||
lang: code,
|
lang: code,
|
||||||
dir,
|
dir,
|
||||||
|
screenshots: useScreenshots,
|
||||||
|
shortcuts: useShortcuts,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -69,13 +179,19 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
|
|||||||
...defaultManifest,
|
...defaultManifest,
|
||||||
lang: 'en-US',
|
lang: 'en-US',
|
||||||
dir: 'ltr',
|
dir: 'ltr',
|
||||||
|
screenshots: defaultScreenshots,
|
||||||
|
shortcuts: defaultShortcuts,
|
||||||
})
|
})
|
||||||
return locales.reduce((acc, { lang, dir, name, short_name, description }) => {
|
return locales.reduce((acc, {
|
||||||
|
lang,
|
||||||
|
dir,
|
||||||
|
name,
|
||||||
|
short_name,
|
||||||
|
description,
|
||||||
|
shortcuts,
|
||||||
|
screenshots,
|
||||||
|
}) => {
|
||||||
acc[lang] = {
|
acc[lang] = {
|
||||||
scope: '/',
|
|
||||||
id: '/',
|
|
||||||
start_url: '/',
|
|
||||||
display: 'standalone',
|
|
||||||
lang,
|
lang,
|
||||||
name,
|
name,
|
||||||
short_name,
|
short_name,
|
||||||
@ -83,47 +199,11 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
|
|||||||
dir,
|
dir,
|
||||||
background_color: '#ffffff',
|
background_color: '#ffffff',
|
||||||
theme_color: '#ffffff',
|
theme_color: '#ffffff',
|
||||||
icons: [
|
...manifestEntries,
|
||||||
{
|
shortcuts,
|
||||||
src: 'pwa-192x192.png',
|
screenshots,
|
||||||
sizes: '192x192',
|
|
||||||
type: 'image/png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: 'pwa-512x512.png',
|
|
||||||
sizes: '512x512',
|
|
||||||
type: 'image/png',
|
|
||||||
purpose: 'any',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: 'maskable-icon.png',
|
|
||||||
sizes: '512x512',
|
|
||||||
type: 'image/png',
|
|
||||||
purpose: 'maskable',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
share_target: {
|
|
||||||
action: '/web-share-target',
|
|
||||||
method: 'POST',
|
|
||||||
enctype: 'multipart/form-data',
|
|
||||||
params: {
|
|
||||||
title: 'title',
|
|
||||||
text: 'text',
|
|
||||||
url: 'url',
|
|
||||||
files: [
|
|
||||||
{
|
|
||||||
name: 'files',
|
|
||||||
accept: ['image/*', 'video/*'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
acc[`${lang}-dark`] = {
|
acc[`${lang}-dark`] = {
|
||||||
scope: '/',
|
|
||||||
id: '/',
|
|
||||||
start_url: '/',
|
|
||||||
display: 'standalone',
|
|
||||||
lang,
|
lang,
|
||||||
name,
|
name,
|
||||||
short_name,
|
short_name,
|
||||||
@ -131,41 +211,9 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
|
|||||||
dir,
|
dir,
|
||||||
background_color: '#111111',
|
background_color: '#111111',
|
||||||
theme_color: '#111111',
|
theme_color: '#111111',
|
||||||
icons: [
|
...manifestEntries,
|
||||||
{
|
shortcuts,
|
||||||
src: 'pwa-192x192.png',
|
screenshots,
|
||||||
sizes: '192x192',
|
|
||||||
type: 'image/png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: 'pwa-512x512.png',
|
|
||||||
sizes: '512x512',
|
|
||||||
type: 'image/png',
|
|
||||||
purpose: 'any',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: 'maskable-icon.png',
|
|
||||||
sizes: '512x512',
|
|
||||||
type: 'image/png',
|
|
||||||
purpose: 'maskable',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
share_target: {
|
|
||||||
action: '/web-share-target',
|
|
||||||
method: 'POST',
|
|
||||||
enctype: 'multipart/form-data',
|
|
||||||
params: {
|
|
||||||
title: 'title',
|
|
||||||
text: 'text',
|
|
||||||
url: 'url',
|
|
||||||
files: [
|
|
||||||
{
|
|
||||||
name: 'files',
|
|
||||||
accept: ['image/*', 'video/*'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
@ -185,23 +233,30 @@ interface PWAEntry {
|
|||||||
short_name?: string
|
short_name?: string
|
||||||
description?: string
|
description?: string
|
||||||
}>
|
}>
|
||||||
|
screenshots?: Record<string, string>
|
||||||
|
shortcuts?: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JsonEntry {
|
interface JsonEntry {
|
||||||
pwa?: PWAEntry
|
pwa?: PWAEntry
|
||||||
app_name?: string
|
app_name?: string
|
||||||
app_desc_short?: string
|
app_desc_short?: string
|
||||||
|
action?: Record<string, any>
|
||||||
|
nav?: Record<string, any>
|
||||||
|
screenshots?: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findBestWebManifestData(files: string[], env: string) {
|
async function findBestWebManifestData(files: string[], env: string) {
|
||||||
const entries: JsonEntry[] = await Promise.all(files.map(async (file) => {
|
const entries: JsonEntry[] = await Promise.all(files.map(async (file) => {
|
||||||
const { pwa, app_name, app_desc_short } = await readI18nFile(file)
|
const { action, app_name, app_desc_short, nav, pwa } = await readI18nFile(file)
|
||||||
return { pwa, app_name, app_desc_short }
|
return { action, app_name, app_desc_short, nav, pwa }
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let pwa: PWAEntry | undefined
|
let pwa: PWAEntry | undefined
|
||||||
let app_name: string | undefined
|
let app_name: string | undefined
|
||||||
let app_desc_short: string | undefined
|
let app_desc_short: string | undefined
|
||||||
|
const action: Record<string, any> = {}
|
||||||
|
const nav: Record<string, any> = {}
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const webmanifest = entry?.pwa?.webmanifest?.[env]
|
const webmanifest = entry?.pwa?.webmanifest?.[env]
|
||||||
@ -226,7 +281,28 @@ async function findBestWebManifestData(files: string[], env: string) {
|
|||||||
|
|
||||||
if (entry.app_desc_short)
|
if (entry.app_desc_short)
|
||||||
app_desc_short = entry.app_desc_short
|
app_desc_short = entry.app_desc_short
|
||||||
|
|
||||||
|
if (entry.nav) {
|
||||||
|
['home', 'local', 'notifications', 'settings'].forEach((key) => {
|
||||||
|
const value = entry.nav![key]
|
||||||
|
if (value)
|
||||||
|
nav[key] = value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.action?.compose)
|
||||||
|
action.compose = entry.action.compose
|
||||||
|
|
||||||
|
if (entry.pwa?.screenshots) {
|
||||||
|
if (!pwa)
|
||||||
|
pwa = {}
|
||||||
|
|
||||||
|
pwa.screenshots = pwa.screenshots ?? {}
|
||||||
|
Object
|
||||||
|
.entries(entry.pwa.screenshots)
|
||||||
|
.forEach(([key, value]) => pwa!.screenshots![key] = value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { pwa, app_name, app_desc_short }
|
return { action, app_desc_short, app_name, nav, pwa }
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
"ufo": "^1.1.1",
|
"ufo": "^1.1.1",
|
||||||
"ultrahtml": "^1.2.0",
|
"ultrahtml": "^1.2.0",
|
||||||
"unimport": "^3.0.6",
|
"unimport": "^3.0.6",
|
||||||
"vite-plugin-pwa": "^0.14.7",
|
"vite-plugin-pwa": "^0.15.0",
|
||||||
"vue-advanced-cropper": "^2.8.8",
|
"vue-advanced-cropper": "^2.8.8",
|
||||||
"vue-virtual-scroller": "2.0.0-beta.8",
|
"vue-virtual-scroller": "2.0.0-beta.8",
|
||||||
"workbox-build": "^6.5.4",
|
"workbox-build": "^6.5.4",
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
middleware: 'auth',
|
||||||
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
useHydratedHead({
|
useHydratedHead({
|
||||||
title: () => t('nav.compose'),
|
title: () => t('nav.compose'),
|
||||||
})
|
})
|
||||||
|
@ -185,10 +185,10 @@ importers:
|
|||||||
version: 5.0.1
|
version: 5.0.1
|
||||||
tauri-plugin-log-api:
|
tauri-plugin-log-api:
|
||||||
specifier: github:tauri-apps/tauri-plugin-log
|
specifier: github:tauri-apps/tauri-plugin-log
|
||||||
version: github.com/tauri-apps/tauri-plugin-log/be811332676ff73e1e891f31ce3de8d447fdf07e
|
version: github.com/tauri-apps/tauri-plugin-log/5e14c2cad7335a4284a6caad81d8cf37dd675a27
|
||||||
tauri-plugin-store-api:
|
tauri-plugin-store-api:
|
||||||
specifier: github:tauri-apps/tauri-plugin-store
|
specifier: github:tauri-apps/tauri-plugin-store
|
||||||
version: github.com/tauri-apps/tauri-plugin-store/f4ef29684e4a32eddf51befaae98a5e498df8574
|
version: github.com/tauri-apps/tauri-plugin-store/0558a1f6c869ae0afdb0181dfa1ea31be8cf4893
|
||||||
theme-vitesse:
|
theme-vitesse:
|
||||||
specifier: ^0.6.4
|
specifier: ^0.6.4
|
||||||
version: 0.6.4
|
version: 0.6.4
|
||||||
@ -208,8 +208,8 @@ importers:
|
|||||||
specifier: ^3.0.6
|
specifier: ^3.0.6
|
||||||
version: 3.0.6(rollup@2.79.1)
|
version: 3.0.6(rollup@2.79.1)
|
||||||
vite-plugin-pwa:
|
vite-plugin-pwa:
|
||||||
specifier: ^0.14.7
|
specifier: ^0.15.0
|
||||||
version: 0.14.7(vite@4.3.4)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
version: 0.15.0(vite@4.3.4)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
||||||
vue-advanced-cropper:
|
vue-advanced-cropper:
|
||||||
specifier: ^2.8.8
|
specifier: ^2.8.8
|
||||||
version: 2.8.8(vue@3.2.45)
|
version: 2.8.8(vue@3.2.45)
|
||||||
@ -13387,18 +13387,16 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vite-plugin-pwa@0.14.7(vite@4.3.4)(workbox-build@6.5.4)(workbox-window@6.5.4):
|
/vite-plugin-pwa@0.15.0(vite@4.3.4)(workbox-build@6.5.4)(workbox-window@6.5.4):
|
||||||
resolution: {integrity: sha512-dNJaf0fYOWncmjxv9HiSa2xrSjipjff7IkYE5oIUJ2x5HKu3cXgA8LRgzOwTc5MhwyFYRSU0xyN0Phbx3NsQYw==}
|
resolution: {integrity: sha512-gpmx3BeubsRIXRBkjPToOTJbo8fknNmZFQs24i0TPZyaNVa0n27YHDo0Y72amnO70WvHKGE3e1fn8SYUP7e8SA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^3.1.0 || ^4.0.0
|
vite: ^3.1.0 || ^4.0.0
|
||||||
workbox-build: ^6.5.4
|
workbox-build: ^6.5.4
|
||||||
workbox-window: ^6.5.4
|
workbox-window: ^6.5.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rollup/plugin-replace': 5.0.2(rollup@3.21.3)
|
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
fast-glob: 3.2.12
|
fast-glob: 3.2.12
|
||||||
pretty-bytes: 6.1.0
|
pretty-bytes: 6.1.0
|
||||||
rollup: 3.21.3
|
|
||||||
vite: 4.3.4(@types/node@18.16.3)
|
vite: 4.3.4(@types/node@18.16.3)
|
||||||
workbox-build: 6.5.4
|
workbox-build: 6.5.4
|
||||||
workbox-window: 6.5.4
|
workbox-window: 6.5.4
|
||||||
@ -14276,16 +14274,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
github.com/tauri-apps/tauri-plugin-log/be811332676ff73e1e891f31ce3de8d447fdf07e:
|
github.com/tauri-apps/tauri-plugin-log/5e14c2cad7335a4284a6caad81d8cf37dd675a27:
|
||||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/be811332676ff73e1e891f31ce3de8d447fdf07e}
|
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/5e14c2cad7335a4284a6caad81d8cf37dd675a27}
|
||||||
name: tauri-plugin-log-api
|
name: tauri-plugin-log-api
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 1.2.0
|
'@tauri-apps/api': 1.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
github.com/tauri-apps/tauri-plugin-store/f4ef29684e4a32eddf51befaae98a5e498df8574:
|
github.com/tauri-apps/tauri-plugin-store/0558a1f6c869ae0afdb0181dfa1ea31be8cf4893:
|
||||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/f4ef29684e4a32eddf51befaae98a5e498df8574}
|
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/0558a1f6c869ae0afdb0181dfa1ea31be8cf4893}
|
||||||
name: tauri-plugin-store-api
|
name: tauri-plugin-store-api
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
|
BIN
public/screenshots/dark-1.webp
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
public/screenshots/light-1.webp
Normal file
After Width: | Height: | Size: 162 KiB |
BIN
public/shortcuts/compose-96x96.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/shortcuts/compose.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
public/shortcuts/home-96x96.png
Normal file
After Width: | Height: | Size: 781 B |
BIN
public/shortcuts/home.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
public/shortcuts/local-96x96.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
public/shortcuts/local.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
public/shortcuts/notifications-96x96.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
public/shortcuts/notifications.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
public/shortcuts/settings-96x96.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
public/shortcuts/settings.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
@ -32,8 +32,23 @@ if (import.meta.env.DEV)
|
|||||||
|
|
||||||
// deny api and server page calls
|
// deny api and server page calls
|
||||||
let denylist: undefined | RegExp[]
|
let denylist: undefined | RegExp[]
|
||||||
if (import.meta.env.PROD)
|
if (import.meta.env.PROD) {
|
||||||
denylist = [/^\/api\//, /^\/login\//, /^\/oauth\//, /^\/signin\//, /^\/web-share-target\//]
|
denylist = [
|
||||||
|
/^\/api\//,
|
||||||
|
/^\/login\//,
|
||||||
|
/^\/oauth\//,
|
||||||
|
/^\/signin\//,
|
||||||
|
/^\/web-share-target\//,
|
||||||
|
// exclude shiki: has its own cache
|
||||||
|
/^\/shiki\//,
|
||||||
|
// exclude shiki: has its own cache
|
||||||
|
/^\/emojis\//,
|
||||||
|
// exclude sw: if the user navigates to it, fallback to index.html
|
||||||
|
/^\/sw.js$/,
|
||||||
|
// exclude webmanifest: has its own cache
|
||||||
|
/^\/manifest-(.*).webmanifest$/,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
// only cache pages and external assets on local build + start or in production
|
// only cache pages and external assets on local build + start or in production
|
||||||
if (import.meta.env.PROD) {
|
if (import.meta.env.PROD) {
|
||||||
@ -59,7 +74,7 @@ if (import.meta.env.PROD) {
|
|||||||
plugins: [
|
plugins: [
|
||||||
new CacheableResponsePlugin({ statuses: [200] }),
|
new CacheableResponsePlugin({ statuses: [200] }),
|
||||||
// 365 days max
|
// 365 days max
|
||||||
new ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 365 }),
|
new ExpirationPlugin({ purgeOnQuotaError: true, maxAgeSeconds: 60 * 60 * 24 * 365 }),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -74,7 +89,7 @@ if (import.meta.env.PROD) {
|
|||||||
plugins: [
|
plugins: [
|
||||||
new CacheableResponsePlugin({ statuses: [200] }),
|
new CacheableResponsePlugin({ statuses: [200] }),
|
||||||
// 15 days max
|
// 15 days max
|
||||||
new ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 15 }),
|
new ExpirationPlugin({ purgeOnQuotaError: true, maxAgeSeconds: 60 * 60 * 24 * 15 }),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|