You've already forked AstralRinth
forked from didirus/AstralRinth
feat: doc templating & cleanup of routes (#4411)
* feat: clean up route structure * feat: install html-pdf-node-ts * fea * feat: use @ceereals/vue-pdf (react-pdf) * feat: remove pdf * feat: hide cc * feat: shared template * feat: payment statement document & redirect for emails * feat: layout tweaks * fix: lint issues * fix: robots.txt * feat: remove letterhead * Delete .claude/settings.local.json Signed-off-by: Calum H. <contact@cal.engineer> --------- Signed-off-by: Calum H. <contact@cal.engineer>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -64,3 +64,4 @@ generated
|
|||||||
app-playground-data/*
|
app-playground-data/*
|
||||||
|
|
||||||
.astro
|
.astro
|
||||||
|
.claude
|
||||||
|
|||||||
@@ -114,13 +114,19 @@ export default defineNuxtConfig({
|
|||||||
hooks: {
|
hooks: {
|
||||||
async 'nitro:config'(nitroConfig) {
|
async 'nitro:config'(nitroConfig) {
|
||||||
const emailTemplates = Object.keys(
|
const emailTemplates = Object.keys(
|
||||||
await import('./src/emails/index.ts').then((m) => m.default),
|
await import('./src/templates/emails/index.ts').then((m) => m.default),
|
||||||
|
)
|
||||||
|
const docTemplates = Object.keys(
|
||||||
|
await import('./src/templates/docs/index.ts').then((m) => m.default),
|
||||||
)
|
)
|
||||||
|
|
||||||
nitroConfig.prerender = nitroConfig.prerender || {}
|
nitroConfig.prerender = nitroConfig.prerender || {}
|
||||||
nitroConfig.prerender.routes = nitroConfig.prerender.routes || []
|
nitroConfig.prerender.routes = nitroConfig.prerender.routes || []
|
||||||
for (const template of emailTemplates) {
|
for (const template of emailTemplates) {
|
||||||
nitroConfig.prerender.routes.push(`/email/${template}`)
|
nitroConfig.prerender.routes.push(`/_internal/templates/email/${template}`)
|
||||||
|
}
|
||||||
|
for (const template of docTemplates) {
|
||||||
|
nitroConfig.prerender.routes.push(`/_internal/templates/doc/${template}`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async 'build:before'() {
|
async 'build:before'() {
|
||||||
@@ -470,6 +476,16 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
'/email/**': {
|
'/email/**': {
|
||||||
|
redirect: '/_internal/templates/email/**',
|
||||||
|
},
|
||||||
|
'/_internal/templates/email/**': {
|
||||||
|
prerender: true,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/html',
|
||||||
|
'Cache-Control': 'public, max-age=3600',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'/_internal/templates/doc/**': {
|
||||||
prerender: true,
|
prerender: true,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/html',
|
'Content-Type': 'text/html',
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import type { Component } from 'vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
// Account
|
|
||||||
'auth-method-added': () => import('./templates/account/AuthenticationMethodAdded.vue'),
|
|
||||||
'auth-method-removed': () => import('./templates/account/AuthenticationMethodRemoved.vue'),
|
|
||||||
'email-changed': () => import('./templates/account/EmailChanged.vue'),
|
|
||||||
'password-changed': () => import('./templates/account/PasswordChanged.vue'),
|
|
||||||
'password-removed': () => import('./templates/account/PasswordRemoved.vue'),
|
|
||||||
'payment-failed': () => import('./templates/account/PaymentFailed.vue'),
|
|
||||||
'reset-password': () => import('./templates/account/ResetPassword.vue'),
|
|
||||||
'two-factor-added': () => import('./templates/account/TwoFactorAdded.vue'),
|
|
||||||
'two-factor-removed': () => import('./templates/account/TwoFactorRemoved.vue'),
|
|
||||||
'verify-email': () => import('./templates/account/VerifyEmail.vue'),
|
|
||||||
'login-new-device': () => import('./templates/account/LoginNewDevice.vue'),
|
|
||||||
'payout-available': () => import('./templates/account/PayoutAvailable.vue'),
|
|
||||||
'personal-access-token-created': () => import('./templates/account/PATCreated.vue'),
|
|
||||||
|
|
||||||
// Subscriptions
|
|
||||||
'subscription-tax-change': () => import('./templates/account/SubscriptionTaxChange.vue'),
|
|
||||||
|
|
||||||
// Moderation
|
|
||||||
'report-submitted': () => import('./templates/moderation/ReportSubmitted.vue'),
|
|
||||||
'report-status-updated': () => import('./templates/moderation/ReportStatusUpdated.vue'),
|
|
||||||
'moderation-thread-message-received': () =>
|
|
||||||
import('./templates/moderation/ModerationThreadMessageReceived.vue'),
|
|
||||||
|
|
||||||
// Project
|
|
||||||
'project-status-updated-neutral': () =>
|
|
||||||
import('./templates/project/ProjectStatusUpdatedNeutral.vue'),
|
|
||||||
'project-status-approved': () => import('./templates/project/ProjectStatusApproved.vue'),
|
|
||||||
'project-invited': () => import('./templates/project/ProjectInvited.vue'),
|
|
||||||
'project-transferred': () => import('./templates/project/ProjectTransferred.vue'),
|
|
||||||
|
|
||||||
// Organization
|
|
||||||
'organization-invited': () => import('./templates/organization/OrganizationInvited.vue'),
|
|
||||||
} as Record<string, () => Promise<{ default: Component }>>
|
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import {
|
|
||||||
Body,
|
|
||||||
Column,
|
|
||||||
Container,
|
|
||||||
Head,
|
|
||||||
Html,
|
|
||||||
Img,
|
|
||||||
Link as VLink,
|
|
||||||
Row,
|
|
||||||
Section,
|
|
||||||
Style,
|
|
||||||
Tailwind,
|
|
||||||
Text,
|
|
||||||
} from '@vue-email/components'
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
title?: string
|
|
||||||
manualLinks?: { link: string; label?: string }[]
|
|
||||||
supportInfo?: string[]
|
|
||||||
}>()
|
|
||||||
|
|
||||||
interface SocialLink {
|
|
||||||
href: string
|
|
||||||
alt: string
|
|
||||||
src: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const socialLinks = Object.freeze<readonly SocialLink[]>([
|
|
||||||
{
|
|
||||||
href: 'https://discord.modrinth.com',
|
|
||||||
alt: 'Discord',
|
|
||||||
src: 'https://cdn-raw.modrinth.com/email/discord.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: 'https://bsky.app/profile/modrinth.com',
|
|
||||||
alt: 'Bluesky',
|
|
||||||
src: 'https://cdn-raw.modrinth.com/email/bluesky.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: 'https://floss.social/@modrinth',
|
|
||||||
alt: 'Mastodon',
|
|
||||||
src: 'https://cdn-raw.modrinth.com/email/mastodon.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: 'https://x.com/modrinth',
|
|
||||||
alt: 'X (Twitter)',
|
|
||||||
src: 'https://cdn-raw.modrinth.com/email/x.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: 'https://www.instagram.com/modrinth/',
|
|
||||||
alt: 'Instagram',
|
|
||||||
src: 'https://cdn-raw.modrinth.com/email/instagram.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: 'https://www.youtube.com/@modrinth',
|
|
||||||
alt: 'YouTube',
|
|
||||||
src: 'https://cdn-raw.modrinth.com/email/youtube.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: 'https://github.com/modrinth',
|
|
||||||
alt: 'GitHub',
|
|
||||||
src: 'https://cdn-raw.modrinth.com/email/github.png',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Html lang="en">
|
|
||||||
<Head>
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css?family=Inter:700,400"
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
/>
|
|
||||||
<Style>
|
|
||||||
/* Outlook.com centering + line-height fixes */ .ExternalClass { width:100%; }
|
|
||||||
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td {
|
|
||||||
line-height:100%; } table { border-collapse:separate; } a, a:link, a:visited {
|
|
||||||
text-decoration:none; color:#1f68c0; } a:hover { text-decoration:underline; }
|
|
||||||
h1,h2,h3,h4,h5,h6 { color:#000 !important; margin:0; mso-line-height-rule:exactly; }
|
|
||||||
</Style>
|
|
||||||
</Head>
|
|
||||||
|
|
||||||
<Body>
|
|
||||||
<Tailwind
|
|
||||||
:config="{
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
bg: { DEFAULT: '#ebebeb', raised: '#ffffff', super: '#e9e9e9' },
|
|
||||||
divider: { DEFAULT: '#babfc5', dark: '#c8cdd3' },
|
|
||||||
base: '#2c2e31',
|
|
||||||
secondary: '#484d54',
|
|
||||||
contrast: '#1a202c',
|
|
||||||
accentContrast: '#ffffff',
|
|
||||||
red: '#cb2245',
|
|
||||||
orange: '#e08325',
|
|
||||||
green: '#00af5c',
|
|
||||||
blue: '#1f68c0',
|
|
||||||
purple: '#8e32f3',
|
|
||||||
gray: '#595b61',
|
|
||||||
brand: {
|
|
||||||
DEFAULT: '#00af5c',
|
|
||||||
highlight: 'rgba(0, 175, 92, 0.25)',
|
|
||||||
shadow: 'rgba(0, 175, 92, 0.7)',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
red: 'rgba(203, 34, 69, 0.25)',
|
|
||||||
orange: 'rgba(224, 131, 37, 0.25)',
|
|
||||||
green: 'rgba(0, 175, 92, 0.25)',
|
|
||||||
blue: 'rgba(31, 104, 192, 0.25)',
|
|
||||||
purple: 'rgba(142, 50, 243, 0.25)',
|
|
||||||
gray: 'rgba(89, 91, 97, 0.25)',
|
|
||||||
},
|
|
||||||
tint: {
|
|
||||||
red: 'rgba(203, 34, 69, 0.1)',
|
|
||||||
orange: 'rgba(224, 131, 37, 0.1)',
|
|
||||||
green: 'rgba(0, 175, 92, 0.1)',
|
|
||||||
blue: 'rgba(31, 104, 192, 0.1)',
|
|
||||||
purple: 'rgba(142, 50, 243, 0.1)',
|
|
||||||
},
|
|
||||||
link: { DEFAULT: '#1f68c0', hover: '#1f68c0', active: '#1f68c0' },
|
|
||||||
muted: '#777777',
|
|
||||||
footerText: '#4d4d4d',
|
|
||||||
},
|
|
||||||
fontSize: {
|
|
||||||
base: ['16px', { lineHeight: '24px' }],
|
|
||||||
'2xl': ['28px', { lineHeight: '32px' }],
|
|
||||||
xs: ['13px', { lineHeight: '16px' }],
|
|
||||||
'2xs': ['11px', { lineHeight: '16px' }],
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
sans: ['Inter', 'Arial', 'sans-serif'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<Section class="m-0 pb-0 pl-0 pr-0 pt-0 font-sans">
|
|
||||||
<Section class="bg-bg pb-4 pl-4 pr-4 pt-4">
|
|
||||||
<Container class="max-w-[600px]">
|
|
||||||
<Row>
|
|
||||||
<Column>
|
|
||||||
<Img
|
|
||||||
src="https://cdn.modrinth.com/email/f740e2decee8764a4629bff677a284f9.png"
|
|
||||||
width="29"
|
|
||||||
alt=""
|
|
||||||
class="block h-auto"
|
|
||||||
/>
|
|
||||||
</Column>
|
|
||||||
</Row>
|
|
||||||
</Container>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section class="bg-white pb-8 pl-8 pr-8 pt-8">
|
|
||||||
<Container class="max-w-[600px]">
|
|
||||||
<slot />
|
|
||||||
</Container>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section class="mb-4 bg-bg pb-4 pl-4 pr-4 pt-4">
|
|
||||||
<Container class="max-w-[600px]">
|
|
||||||
<Row>
|
|
||||||
<Column class="align-middle">
|
|
||||||
<VLink href="https://modrinth.com" aria-label="Modrinth">
|
|
||||||
<Img
|
|
||||||
src="https://cdn.modrinth.com/email/bd3357dfae4b1d266250372db3a0988f.png"
|
|
||||||
width="175"
|
|
||||||
alt="modrinth logo"
|
|
||||||
class="block h-auto"
|
|
||||||
/>
|
|
||||||
</VLink>
|
|
||||||
|
|
||||||
<Row class="text-right align-middle">
|
|
||||||
<Section class="m-0 inline-block pb-0 pl-0 pr-0 pt-0">
|
|
||||||
<template v-for="(item, index) in socialLinks" :key="item.href">
|
|
||||||
<VLink
|
|
||||||
:href="item.href"
|
|
||||||
:class="['inline-block', index !== socialLinks.length - 1 ? 'mr-4' : '']"
|
|
||||||
>
|
|
||||||
<Img width="20" height="20" :alt="item.alt" :src="item.src" />
|
|
||||||
</VLink>
|
|
||||||
</template>
|
|
||||||
</Section>
|
|
||||||
</Row>
|
|
||||||
|
|
||||||
<Text class="mb-0 mt-2 text-xs" :style="{ color: '#4d4d4d' }"> Rinth, Inc. </Text>
|
|
||||||
<Section class="m-0 pb-0 pl-0 pr-0 pt-0">
|
|
||||||
<Text class="m-0 text-xs text-secondary">800 N King St</Text>
|
|
||||||
<Text class="m-0 text-xs text-secondary">Suite 304 #3133</Text>
|
|
||||||
<Text class="m-0 text-xs text-secondary">Wilmington, DE 19801</Text>
|
|
||||||
</Section>
|
|
||||||
</Column>
|
|
||||||
</Row>
|
|
||||||
</Container>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<!-- <Text
|
|
||||||
class="text-footerText text-2xs mb-4 mt-0 pb-0 pl-4 pr-4 pt-0 text-center font-sans"
|
|
||||||
>
|
|
||||||
This email was sent to you as a registered user of Modrinth. You can customize the
|
|
||||||
emails you recieve in your
|
|
||||||
<VLink href="https://modrinth.com/settings/notifications" class="text-green underline"
|
|
||||||
>notification settings</VLink
|
|
||||||
>. Some emails are required to keep your account secure and cannot be disabled.
|
|
||||||
</Text> -->
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<Section v-if="supportInfo && supportInfo.length" class="mb-0 pb-0 pl-4 pr-4 pt-0">
|
|
||||||
<Text
|
|
||||||
v-for="(line, index) in supportInfo"
|
|
||||||
:key="index"
|
|
||||||
class="text-footerText text-2xs font-sans"
|
|
||||||
>
|
|
||||||
{{ line }}
|
|
||||||
</Text>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section
|
|
||||||
v-if="manualLinks && manualLinks.length"
|
|
||||||
class="text-footerText text-2xs mb-4 pb-0 pl-4 pr-4 pt-0 font-sans"
|
|
||||||
>
|
|
||||||
<small class="text-muted text-2xs"
|
|
||||||
>If you're having trouble with the links above, copy and paste these URLs into your
|
|
||||||
web browser:</small
|
|
||||||
>
|
|
||||||
<Text class="text-2xs text-muted mt-0">
|
|
||||||
<span v-for="(item, index) in manualLinks" :key="index" class="block break-words">
|
|
||||||
<span v-if="item.label">
|
|
||||||
<b>{{ item.label }}:</b><br />
|
|
||||||
</span>
|
|
||||||
{{ item.link }}
|
|
||||||
</span>
|
|
||||||
<!-- <span class="block break-words">
|
|
||||||
<span> <b>Notification settings:</b><br /> </span>
|
|
||||||
https://modrinth.com/settings/notifications
|
|
||||||
</span> -->
|
|
||||||
</Text>
|
|
||||||
</Section>
|
|
||||||
</Section>
|
|
||||||
</Tailwind>
|
|
||||||
</Body>
|
|
||||||
</Html>
|
|
||||||
</template>
|
|
||||||
131
apps/frontend/src/pages/admin/docs.vue
Normal file
131
apps/frontend/src/pages/admin/docs.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { CopyIcon, LibraryIcon, PlayIcon, SearchIcon } from '@modrinth/assets'
|
||||||
|
import { ButtonStyled, Card } from '@modrinth/ui'
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
import docs from '~/templates/docs'
|
||||||
|
|
||||||
|
const allTemplates = Object.keys(docs).sort()
|
||||||
|
const query = ref('')
|
||||||
|
const filtered = computed(() =>
|
||||||
|
allTemplates.filter((t) => t.toLowerCase().includes(query.value.toLowerCase().trim())),
|
||||||
|
)
|
||||||
|
|
||||||
|
function openAll() {
|
||||||
|
let offset = 0
|
||||||
|
for (const id of filtered.value) {
|
||||||
|
openPreview(id, offset)
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy(id: string) {
|
||||||
|
navigator.clipboard?.writeText(`/_internal/templates/doc/${id}`).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPreview(id: string, offset = 0) {
|
||||||
|
const width = 800
|
||||||
|
const height = 1000
|
||||||
|
const left = window.screenX + (window.outerWidth - width) / 2 + ((offset * 28) % 320)
|
||||||
|
const top = window.screenY + (window.outerHeight - height) / 2 + ((offset * 28) % 320)
|
||||||
|
window.open(
|
||||||
|
`/_internal/templates/doc/${id}`,
|
||||||
|
`doc-${id}`,
|
||||||
|
`popup=yes,width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,menubar=no,toolbar=no,location=no,status=no`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const counts = computed(() => ({
|
||||||
|
total: allTemplates.length,
|
||||||
|
shown: filtered.value.length,
|
||||||
|
}))
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.getElementById('doc-search')?.focus()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="normal-page no-sidebar">
|
||||||
|
<h1 class="mb-4 text-3xl font-extrabold text-heading">Document templates</h1>
|
||||||
|
<div class="normal-page__content">
|
||||||
|
<Card class="mb-6 flex flex-col gap-4">
|
||||||
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
|
<div class="relative">
|
||||||
|
<SearchIcon
|
||||||
|
class="pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-secondary"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
id="doc-search"
|
||||||
|
v-model="query"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search templates..."
|
||||||
|
class="w-72 rounded-lg border border-divider bg-bg px-7 py-2 text-sm text-primary placeholder-secondary focus:border-green focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ButtonStyled color="brand">
|
||||||
|
<button :disabled="filtered.length === 0" @click="openAll">
|
||||||
|
<LibraryIcon class="h-4 w-4" aria-hidden="true" />
|
||||||
|
Open all ({{ counts.shown }})
|
||||||
|
</button>
|
||||||
|
</ButtonStyled>
|
||||||
|
|
||||||
|
<span class="text-sm text-secondary">
|
||||||
|
Showing <span class="font-medium text-contrast">{{ counts.shown }}</span> of
|
||||||
|
<span class="font-medium text-contrast">{{ counts.total }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="filtered.length === 0"
|
||||||
|
class="rounded-lg border border-dashed border-divider px-6 py-10 text-center text-sm text-secondary"
|
||||||
|
>
|
||||||
|
No templates match your search.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul v-else class="grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
|
<li
|
||||||
|
v-for="id in filtered"
|
||||||
|
:key="id"
|
||||||
|
class="hover:border-green/70 group flex flex-col justify-between rounded-lg border border-divider bg-button-bg p-4 shadow-sm transition hover:shadow"
|
||||||
|
>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="font-mono text-sm font-semibold tracking-tight text-contrast">
|
||||||
|
{{ id }}
|
||||||
|
</div>
|
||||||
|
<div class="mt-1 truncate text-xs text-secondary">
|
||||||
|
/_internal/templates/doc/{{ id }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-auto flex gap-2">
|
||||||
|
<ButtonStyled color="brand" class="flex-1">
|
||||||
|
<button class="w-full justify-center" @click="openPreview(id)">
|
||||||
|
<PlayIcon class="h-4 w-4" aria-hidden="true" />
|
||||||
|
Preview
|
||||||
|
</button>
|
||||||
|
</ButtonStyled>
|
||||||
|
|
||||||
|
<ButtonStyled>
|
||||||
|
<button class="justify-center" title="Copy preview URL" @click="copy(id)">
|
||||||
|
<CopyIcon class="h-4 w-4" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</ButtonStyled>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<p class="mt-2 text-xs text-secondary">
|
||||||
|
All templates come from
|
||||||
|
<code class="rounded bg-code-bg px-1 py-0.5 text-[11px] text-code-text"
|
||||||
|
>src/templates/docs/index.ts</code
|
||||||
|
>. Popouts render via
|
||||||
|
<code class="rounded bg-code-bg px-1 py-0.5 text-[11px] text-code-text"
|
||||||
|
>/_internal/templates/doc/[template]</code
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -3,7 +3,7 @@ import { CopyIcon, LibraryIcon, PlayIcon, SearchIcon } from '@modrinth/assets'
|
|||||||
import { ButtonStyled, Card } from '@modrinth/ui'
|
import { ButtonStyled, Card } from '@modrinth/ui'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
import emails from '@/emails'
|
import emails from '~/templates/emails'
|
||||||
|
|
||||||
const allTemplates = Object.keys(emails).sort()
|
const allTemplates = Object.keys(emails).sort()
|
||||||
const query = ref('')
|
const query = ref('')
|
||||||
@@ -20,7 +20,7 @@ function openAll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function copy(id: string) {
|
function copy(id: string) {
|
||||||
navigator.clipboard?.writeText(`/email/${id}`).catch(() => {})
|
navigator.clipboard?.writeText(`/_internal/templates/email/${id}`).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
function openPreview(id: string, offset = 0) {
|
function openPreview(id: string, offset = 0) {
|
||||||
@@ -29,7 +29,7 @@ function openPreview(id: string, offset = 0) {
|
|||||||
const left = window.screenX + (window.outerWidth - width) / 2 + ((offset * 28) % 320)
|
const left = window.screenX + (window.outerWidth - width) / 2 + ((offset * 28) % 320)
|
||||||
const top = window.screenY + (window.outerHeight - height) / 2 + ((offset * 28) % 320)
|
const top = window.screenY + (window.outerHeight - height) / 2 + ((offset * 28) % 320)
|
||||||
window.open(
|
window.open(
|
||||||
`/email/${id}`,
|
`/_internal/templates/email/${id}`,
|
||||||
`email-${id}`,
|
`email-${id}`,
|
||||||
`popup=yes,width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,menubar=no,toolbar=no,location=no,status=no`,
|
`popup=yes,width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,menubar=no,toolbar=no,location=no,status=no`,
|
||||||
)
|
)
|
||||||
@@ -94,7 +94,9 @@ onMounted(() => {
|
|||||||
<div class="font-mono text-sm font-semibold tracking-tight text-contrast">
|
<div class="font-mono text-sm font-semibold tracking-tight text-contrast">
|
||||||
{{ id }}
|
{{ id }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 truncate text-xs text-secondary">/email/{{ id }}</div>
|
<div class="mt-1 truncate text-xs text-secondary">
|
||||||
|
/_internal/templates/email/{{ id }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-auto flex gap-2">
|
<div class="mt-auto flex gap-2">
|
||||||
@@ -121,7 +123,7 @@ onMounted(() => {
|
|||||||
>src/emails/index.ts</code
|
>src/emails/index.ts</code
|
||||||
>. Popouts render via
|
>. Popouts render via
|
||||||
<code class="rounded bg-code-bg px-1 py-0.5 text-[11px] text-code-text"
|
<code class="rounded bg-code-bg px-1 py-0.5 text-[11px] text-code-text"
|
||||||
>/email/[template]</code
|
>/_internal/templates/email/[template]</code
|
||||||
>.
|
>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow: /email/
|
Disallow: /_internal/
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { render } from '@vue-email/render'
|
||||||
|
import type { Component } from 'vue'
|
||||||
|
|
||||||
|
import docs from '~/templates/docs'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const template = event.context.params?.template as string
|
||||||
|
try {
|
||||||
|
const component = (await docs[template]()).default as Component | undefined
|
||||||
|
|
||||||
|
if (!component) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 404,
|
||||||
|
message: 'Document template not found',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = await render(component, {})
|
||||||
|
|
||||||
|
return html
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error rendering document template ${template}:`, error)
|
||||||
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
message: 'Failed to render document template',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { render } from '@vue-email/render'
|
import { render } from '@vue-email/render'
|
||||||
import type { Component } from 'vue'
|
import type { Component } from 'vue'
|
||||||
|
|
||||||
import emails from '~/emails'
|
import emails from '~/templates/emails'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const template = event.context.params?.template as string
|
const template = event.context.params?.template as string
|
||||||
107
apps/frontend/src/templates/docs/finance/PaymentStatement.vue
Normal file
107
apps/frontend/src/templates/docs/finance/PaymentStatement.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Link as VLink, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
|
import StyledDoc from '../shared/StyledDoc.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<StyledDoc title="Payment Statement">
|
||||||
|
<Section class="mb-8">
|
||||||
|
<div class="flex items-start justify-between">
|
||||||
|
<div class="flex-1">
|
||||||
|
<Text class="m-0 mb-2 text-2xl text-base font-bold">Payment Statement</Text>
|
||||||
|
<Text class="m-0 text-sm text-secondary">Payment ID: { statement.payment_id }</Text>
|
||||||
|
<Text class="m-0 text-sm text-secondary">Date: { statement.payment_date }</Text>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 text-right">
|
||||||
|
<Text class="m-0 text-3xl font-bold text-brand">{ statement.net_amount } </Text>
|
||||||
|
<Text class="m-0 mt-1 text-sm text-secondary">Net Amount Paid</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section class="mb-8">
|
||||||
|
<div class="flex items-start gap-8">
|
||||||
|
<div class="flex-1">
|
||||||
|
<Text class="m-0 mb-3 border-b border-divider pb-2 text-base text-sm font-semibold">
|
||||||
|
From
|
||||||
|
</Text>
|
||||||
|
<Text class="m-0 mb-1 text-base text-sm font-semibold">Rinth, Inc.</Text>
|
||||||
|
<Text class="m-0 text-sm leading-tight text-secondary">800 N King St</Text>
|
||||||
|
<Text class="m-0 text-sm leading-tight text-secondary">Suite 304 #3133</Text>
|
||||||
|
<Text class="m-0 text-sm leading-tight text-secondary">Wilmington, DE 19801</Text>
|
||||||
|
<Text class="m-0 mt-2 text-sm text-secondary">
|
||||||
|
<VLink href="mailto:support@modrinth.com" class="text-blue no-underline">
|
||||||
|
support@modrinth.com
|
||||||
|
</VLink>
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1">
|
||||||
|
<Text class="m-0 mb-3 border-b border-divider pb-2 text-base text-sm font-semibold">
|
||||||
|
To
|
||||||
|
</Text>
|
||||||
|
<Text class="m-0 mb-1 text-base text-sm font-semibold">
|
||||||
|
{ statement.recipient_name }
|
||||||
|
</Text>
|
||||||
|
<Text class="m-0 text-sm leading-tight text-secondary">
|
||||||
|
{ statement.recipient_address_line_1 }
|
||||||
|
</Text>
|
||||||
|
<Text class="m-0 text-sm leading-tight text-secondary">
|
||||||
|
{ statement.recipient_address_line_2 }
|
||||||
|
</Text>
|
||||||
|
<Text class="m-0 text-sm leading-tight text-secondary">
|
||||||
|
{ statement.recipient_address_line_3 }
|
||||||
|
</Text>
|
||||||
|
<Text class="m-0 mt-2 text-sm text-secondary">
|
||||||
|
<VLink href="mailto:{ statement.recipient_email }" class="text-blue no-underline">
|
||||||
|
{ statement.recipient_email }
|
||||||
|
</VLink>
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section class="mb-8">
|
||||||
|
<Text class="m-0 mb-4 border-b border-divider pb-2 text-base text-lg font-semibold">
|
||||||
|
Payment Details
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<div class="mb-3 flex items-center justify-between">
|
||||||
|
<Text class="m-0 text-sm text-secondary">Gross Amount</Text>
|
||||||
|
<Text class="m-0 text-base text-sm">{ statement.gross_amount }</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3 flex items-center justify-between">
|
||||||
|
<Text class="m-0 text-sm text-secondary">Processing Fees</Text>
|
||||||
|
<Text class="m-0 text-base text-sm">{ statement.fees }</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4 border-divider" />
|
||||||
|
|
||||||
|
<div class="mb-6 flex items-center justify-between">
|
||||||
|
<Text class="m-0 text-base font-semibold">Net Amount</Text>
|
||||||
|
<Text class="m-0 text-base font-semibold">{ statement.net_amount }</Text>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section class="mb-8">
|
||||||
|
<Text class="m-0 mb-4 border-b border-divider pb-2 text-base text-lg font-semibold">
|
||||||
|
Purpose of Payment
|
||||||
|
</Text>
|
||||||
|
<Text class="m-0 text-sm leading-relaxed text-secondary">
|
||||||
|
This payout reflects revenue earned by the creator through their activity on the Modrinth
|
||||||
|
platform. Earnings are based on advertising revenue, subscriptions, and/or affiliate
|
||||||
|
commissions tied to the creator's published projects, in accordance with the Rewards Program
|
||||||
|
Terms.
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section class="mt-12 border-t border-divider pt-6">
|
||||||
|
<Text class="text-muted m-0 text-center text-xs">
|
||||||
|
This statement records a payout issued by Rinth, Inc. to the creator and may be retained as
|
||||||
|
proof of payment.
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
</StyledDoc>
|
||||||
|
</template>
|
||||||
6
apps/frontend/src/templates/docs/index.ts
Normal file
6
apps/frontend/src/templates/docs/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { Component } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Finance
|
||||||
|
'payment-statement': () => import('./finance/PaymentStatement.vue'),
|
||||||
|
} as Record<string, () => Promise<{ default: Component }>>
|
||||||
54
apps/frontend/src/templates/docs/shared/StyledDoc.vue
Normal file
54
apps/frontend/src/templates/docs/shared/StyledDoc.vue
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Column, Container, Img, Link as VLink, Row, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
|
import StyledTemplate from '../../shared/StyledTemplate.vue'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
title?: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<StyledTemplate :title="title">
|
||||||
|
<Section class="bg-white pb-8 pl-8 pr-8 pt-8">
|
||||||
|
<Container class="max-w-[700px]">
|
||||||
|
<Row class="mb-8">
|
||||||
|
<Column>
|
||||||
|
<Row class="mb-4">
|
||||||
|
<Column class="w-1/3">
|
||||||
|
<Img
|
||||||
|
src="https://cdn.modrinth.com/email/bd3357dfae4b1d266250372db3a0988f.png"
|
||||||
|
width="120"
|
||||||
|
alt="Modrinth Logo"
|
||||||
|
class="block h-auto"
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
<Column class="w-2/3 text-right align-top">
|
||||||
|
<Text class="m-0 mb-2 text-2xl text-base font-bold"> Rinth, Inc. </Text>
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Section class="border-t-2 border-brand pt-6">
|
||||||
|
<slot />
|
||||||
|
</Section>
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section class="border-t border-divider bg-bg pb-4 pl-8 pr-8 pt-4">
|
||||||
|
<Container class="max-w-[700px]">
|
||||||
|
<Row>
|
||||||
|
<Column class="text-center">
|
||||||
|
<Text class="text-muted m-0 text-xs">
|
||||||
|
This document was generated by Modrinth. For questions, contact
|
||||||
|
<VLink href="mailto:support@modrinth.com" class="text-blue"
|
||||||
|
>support@modrinth.com</VLink
|
||||||
|
>
|
||||||
|
</Text>
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
</StyledTemplate>
|
||||||
|
</template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Column, Heading, Img, Link as VLink, Row, Section, Text } from '@vue-email/components'
|
import { Column, Heading, Img, Link as VLink, Row, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Button, Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Link as VLink, Section, Text } from '@vue-email/components'
|
import { Button, Heading, Link as VLink, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Button, Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Button, Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
36
apps/frontend/src/templates/emails/index.ts
Normal file
36
apps/frontend/src/templates/emails/index.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import type { Component } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Account
|
||||||
|
'auth-method-added': () => import('./account/AuthenticationMethodAdded.vue'),
|
||||||
|
'auth-method-removed': () => import('./account/AuthenticationMethodRemoved.vue'),
|
||||||
|
'email-changed': () => import('./account/EmailChanged.vue'),
|
||||||
|
'password-changed': () => import('./account/PasswordChanged.vue'),
|
||||||
|
'password-removed': () => import('./account/PasswordRemoved.vue'),
|
||||||
|
'payment-failed': () => import('./account/PaymentFailed.vue'),
|
||||||
|
'reset-password': () => import('./account/ResetPassword.vue'),
|
||||||
|
'two-factor-added': () => import('./account/TwoFactorAdded.vue'),
|
||||||
|
'two-factor-removed': () => import('./account/TwoFactorRemoved.vue'),
|
||||||
|
'verify-email': () => import('./account/VerifyEmail.vue'),
|
||||||
|
'login-new-device': () => import('./account/LoginNewDevice.vue'),
|
||||||
|
'payout-available': () => import('./account/PayoutAvailable.vue'),
|
||||||
|
'personal-access-token-created': () => import('./account/PATCreated.vue'),
|
||||||
|
|
||||||
|
// Subscriptions
|
||||||
|
'subscription-tax-change': () => import('./account/SubscriptionTaxChange.vue'),
|
||||||
|
|
||||||
|
// Moderation
|
||||||
|
'report-submitted': () => import('./moderation/ReportSubmitted.vue'),
|
||||||
|
'report-status-updated': () => import('./moderation/ReportStatusUpdated.vue'),
|
||||||
|
'moderation-thread-message-received': () =>
|
||||||
|
import('./moderation/ModerationThreadMessageReceived.vue'),
|
||||||
|
|
||||||
|
// Project
|
||||||
|
'project-status-updated-neutral': () => import('./project/ProjectStatusUpdatedNeutral.vue'),
|
||||||
|
'project-status-approved': () => import('./project/ProjectStatusApproved.vue'),
|
||||||
|
'project-invited': () => import('./project/ProjectInvited.vue'),
|
||||||
|
'project-transferred': () => import('./project/ProjectTransferred.vue'),
|
||||||
|
|
||||||
|
// Organization
|
||||||
|
'organization-invited': () => import('./organization/OrganizationInvited.vue'),
|
||||||
|
} as Record<string, () => Promise<{ default: Component }>>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
import { Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
import { Heading, Link as VLink, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
import { Button, Heading, Img, Link as VLink, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
import StyledEmail from '@/emails/shared/StyledEmail.vue'
|
import StyledEmail from '../shared/StyledEmail.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
161
apps/frontend/src/templates/emails/shared/StyledEmail.vue
Normal file
161
apps/frontend/src/templates/emails/shared/StyledEmail.vue
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Column, Container, Img, Link as VLink, Row, Section, Text } from '@vue-email/components'
|
||||||
|
|
||||||
|
import StyledTemplate from '../../shared/StyledTemplate.vue'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
title?: string
|
||||||
|
manualLinks?: { link: string; label?: string }[]
|
||||||
|
supportInfo?: string[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
interface SocialLink {
|
||||||
|
href: string
|
||||||
|
alt: string
|
||||||
|
src: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const socialLinks = Object.freeze<readonly SocialLink[]>([
|
||||||
|
{
|
||||||
|
href: 'https://discord.modrinth.com',
|
||||||
|
alt: 'Discord',
|
||||||
|
src: 'https://cdn-raw.modrinth.com/email/discord.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://bsky.app/profile/modrinth.com',
|
||||||
|
alt: 'Bluesky',
|
||||||
|
src: 'https://cdn-raw.modrinth.com/email/bluesky.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://floss.social/@modrinth',
|
||||||
|
alt: 'Mastodon',
|
||||||
|
src: 'https://cdn-raw.modrinth.com/email/mastodon.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://x.com/modrinth',
|
||||||
|
alt: 'X (Twitter)',
|
||||||
|
src: 'https://cdn-raw.modrinth.com/email/x.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://www.instagram.com/modrinth/',
|
||||||
|
alt: 'Instagram',
|
||||||
|
src: 'https://cdn-raw.modrinth.com/email/instagram.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://www.youtube.com/@modrinth',
|
||||||
|
alt: 'YouTube',
|
||||||
|
src: 'https://cdn-raw.modrinth.com/email/youtube.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://github.com/modrinth',
|
||||||
|
alt: 'GitHub',
|
||||||
|
src: 'https://cdn-raw.modrinth.com/email/github.png',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<StyledTemplate :title="title">
|
||||||
|
<Section class="bg-bg pb-4 pl-4 pr-4 pt-4">
|
||||||
|
<Container class="max-w-[600px]">
|
||||||
|
<Row>
|
||||||
|
<Column>
|
||||||
|
<Img
|
||||||
|
src="https://cdn.modrinth.com/email/f740e2decee8764a4629bff677a284f9.png"
|
||||||
|
width="29"
|
||||||
|
alt=""
|
||||||
|
class="block h-auto"
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section class="bg-white pb-8 pl-8 pr-8 pt-8">
|
||||||
|
<Container class="max-w-[600px]">
|
||||||
|
<slot />
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section class="mb-4 bg-bg pb-4 pl-4 pr-4 pt-4">
|
||||||
|
<Container class="max-w-[600px]">
|
||||||
|
<Row>
|
||||||
|
<Column class="align-middle">
|
||||||
|
<VLink href="https://modrinth.com" aria-label="Modrinth">
|
||||||
|
<Img
|
||||||
|
src="https://cdn.modrinth.com/email/bd3357dfae4b1d266250372db3a0988f.png"
|
||||||
|
width="175"
|
||||||
|
alt="modrinth logo"
|
||||||
|
class="block h-auto"
|
||||||
|
/>
|
||||||
|
</VLink>
|
||||||
|
|
||||||
|
<Row class="text-right align-middle">
|
||||||
|
<Section class="m-0 inline-block pb-0 pl-0 pr-0 pt-0">
|
||||||
|
<template v-for="(item, index) in socialLinks" :key="item.href">
|
||||||
|
<VLink
|
||||||
|
:href="item.href"
|
||||||
|
:class="['inline-block', index !== socialLinks.length - 1 ? 'mr-4' : '']"
|
||||||
|
>
|
||||||
|
<Img width="20" height="20" :alt="item.alt" :src="item.src" />
|
||||||
|
</VLink>
|
||||||
|
</template>
|
||||||
|
</Section>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Text class="mb-0 mt-2 text-xs" :style="{ color: '#4d4d4d' }"> Rinth, Inc. </Text>
|
||||||
|
<Section class="m-0 pb-0 pl-0 pr-0 pt-0">
|
||||||
|
<Text class="m-0 text-xs text-secondary">800 N King St</Text>
|
||||||
|
<Text class="m-0 text-xs text-secondary">Suite 304 #3133</Text>
|
||||||
|
<Text class="m-0 text-xs text-secondary">Wilmington, DE 19801</Text>
|
||||||
|
</Section>
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<!-- <Text
|
||||||
|
class="text-footerText text-2xs mb-4 mt-0 pb-0 pl-4 pr-4 pt-0 text-center font-sans"
|
||||||
|
>
|
||||||
|
This email was sent to you as a registered user of Modrinth. You can customize the
|
||||||
|
emails you recieve in your
|
||||||
|
<VLink href="https://modrinth.com/settings/notifications" class="text-green underline"
|
||||||
|
>notification settings</VLink
|
||||||
|
>. Some emails are required to keep your account secure and cannot be disabled.
|
||||||
|
</Text> -->
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<Section v-if="supportInfo && supportInfo.length" class="mb-0 pb-0 pl-4 pr-4 pt-0">
|
||||||
|
<Text
|
||||||
|
v-for="(line, index) in supportInfo"
|
||||||
|
:key="index"
|
||||||
|
class="text-footerText text-2xs font-sans"
|
||||||
|
>
|
||||||
|
{{ line }}
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section
|
||||||
|
v-if="manualLinks && manualLinks.length"
|
||||||
|
class="text-footerText text-2xs mb-4 pb-0 pl-4 pr-4 pt-0 font-sans"
|
||||||
|
>
|
||||||
|
<small class="text-muted text-2xs"
|
||||||
|
>If you're having trouble with the links above, copy and paste these URLs into your web
|
||||||
|
browser:</small
|
||||||
|
>
|
||||||
|
<Text class="text-2xs text-muted mt-0">
|
||||||
|
<span v-for="(item, index) in manualLinks" :key="index" class="block break-words">
|
||||||
|
<span v-if="item.label">
|
||||||
|
<b>{{ item.label }}:</b><br />
|
||||||
|
</span>
|
||||||
|
{{ item.link }}
|
||||||
|
</span>
|
||||||
|
<!-- <span class="block break-words">
|
||||||
|
<span> <b>Notification settings:</b><br /> </span>
|
||||||
|
https://modrinth.com/settings/notifications
|
||||||
|
</span> -->
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
</StyledTemplate>
|
||||||
|
</template>
|
||||||
92
apps/frontend/src/templates/shared/StyledTemplate.vue
Normal file
92
apps/frontend/src/templates/shared/StyledTemplate.vue
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Body, Head, Html, Section, Style, Tailwind } from '@vue-email/components'
|
||||||
|
import type { Config } from 'tailwindcss'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
title?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const tailwindConfig = {
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
bg: { DEFAULT: '#ebebeb', raised: '#ffffff', super: '#e9e9e9' },
|
||||||
|
divider: { DEFAULT: '#babfc5', dark: '#c8cdd3' },
|
||||||
|
base: '#2c2e31',
|
||||||
|
secondary: '#484d54',
|
||||||
|
contrast: '#1a202c',
|
||||||
|
accentContrast: '#ffffff',
|
||||||
|
red: '#cb2245',
|
||||||
|
orange: '#e08325',
|
||||||
|
green: '#00af5c',
|
||||||
|
blue: '#1f68c0',
|
||||||
|
purple: '#8e32f3',
|
||||||
|
gray: '#595b61',
|
||||||
|
brand: {
|
||||||
|
DEFAULT: '#00af5c',
|
||||||
|
highlight: 'rgba(0, 175, 92, 0.25)',
|
||||||
|
shadow: 'rgba(0, 175, 92, 0.7)',
|
||||||
|
},
|
||||||
|
highlight: {
|
||||||
|
red: 'rgba(203, 34, 69, 0.25)',
|
||||||
|
orange: 'rgba(224, 131, 37, 0.25)',
|
||||||
|
green: 'rgba(0, 175, 92, 0.25)',
|
||||||
|
blue: 'rgba(31, 104, 192, 0.25)',
|
||||||
|
purple: 'rgba(142, 50, 243, 0.25)',
|
||||||
|
gray: 'rgba(89, 91, 97, 0.25)',
|
||||||
|
},
|
||||||
|
tint: {
|
||||||
|
red: 'rgba(203, 34, 69, 0.1)',
|
||||||
|
orange: 'rgba(224, 131, 37, 0.1)',
|
||||||
|
green: 'rgba(0, 175, 92, 0.1)',
|
||||||
|
blue: 'rgba(31, 104, 192, 0.1)',
|
||||||
|
purple: 'rgba(142, 50, 243, 0.1)',
|
||||||
|
},
|
||||||
|
link: { DEFAULT: '#1f68c0', hover: '#1f68c0', active: '#1f68c0' },
|
||||||
|
muted: '#777777',
|
||||||
|
footerText: '#4d4d4d',
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
base: ['16px', { lineHeight: '24px' }],
|
||||||
|
'2xl': ['28px', { lineHeight: '32px' }],
|
||||||
|
xs: ['13px', { lineHeight: '16px' }],
|
||||||
|
'2xs': ['11px', { lineHeight: '16px' }],
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Inter', 'Arial', 'sans-serif'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as Partial<Config>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Html lang="en">
|
||||||
|
<Head>
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Inter:700,400"
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
|
<Style>
|
||||||
|
/* Outlook.com centering + line-height fixes */ .ExternalClass { width:100%; }
|
||||||
|
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td {
|
||||||
|
line-height:100%; } table { border-collapse:separate; } a, a:link, a:visited {
|
||||||
|
text-decoration:none; color:#1f68c0; } a:hover { text-decoration:underline; }
|
||||||
|
h1,h2,h3,h4,h5,h6 { color:#000 !important; margin:0; mso-line-height-rule:exactly; }
|
||||||
|
</Style>
|
||||||
|
</Head>
|
||||||
|
|
||||||
|
<Body>
|
||||||
|
<Tailwind :config="tailwindConfig">
|
||||||
|
<Section class="m-0 pb-0 pl-0 pr-0 pt-0 font-sans">
|
||||||
|
<slot />
|
||||||
|
</Section>
|
||||||
|
</Tailwind>
|
||||||
|
</Body>
|
||||||
|
</Html>
|
||||||
|
</template>
|
||||||
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
@@ -533,7 +533,7 @@ importers:
|
|||||||
version: link:../utils
|
version: link:../utils
|
||||||
'@tresjs/cientos':
|
'@tresjs/cientos':
|
||||||
specifier: ^4.3.0
|
specifier: ^4.3.0
|
||||||
version: 4.3.1(@tresjs/core@4.3.6(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4)))(@types/three@0.172.0)(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))
|
version: 4.3.1(@tresjs/core@4.3.6(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4)))(@types/three@0.172.0)(react@19.1.1)(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))
|
||||||
'@tresjs/core':
|
'@tresjs/core':
|
||||||
specifier: ^4.3.4
|
specifier: ^4.3.4
|
||||||
version: 4.3.6(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))
|
version: 4.3.6(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))
|
||||||
@@ -6708,6 +6708,10 @@ packages:
|
|||||||
rc9@2.1.2:
|
rc9@2.1.2:
|
||||||
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
|
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
|
||||||
|
|
||||||
|
react@19.1.1:
|
||||||
|
resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
read-cache@1.0.0:
|
read-cache@1.0.0:
|
||||||
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
||||||
|
|
||||||
@@ -10569,7 +10573,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.5.0
|
'@tauri-apps/api': 2.5.0
|
||||||
|
|
||||||
'@tresjs/cientos@4.3.1(@tresjs/core@4.3.6(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4)))(@types/three@0.172.0)(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))':
|
'@tresjs/cientos@4.3.1(@tresjs/core@4.3.6(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4)))(@types/three@0.172.0)(react@19.1.1)(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tresjs/core': 4.3.6(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))
|
'@tresjs/core': 4.3.6(three@0.172.0)(typescript@5.5.4)(vue@3.5.13(typescript@5.5.4))
|
||||||
'@vueuse/core': 12.8.2(typescript@5.5.4)
|
'@vueuse/core': 12.8.2(typescript@5.5.4)
|
||||||
@@ -10577,7 +10581,7 @@ snapshots:
|
|||||||
stats-gl: 2.4.2(@types/three@0.172.0)(three@0.172.0)
|
stats-gl: 2.4.2(@types/three@0.172.0)(three@0.172.0)
|
||||||
stats.js: 0.17.0
|
stats.js: 0.17.0
|
||||||
three: 0.172.0
|
three: 0.172.0
|
||||||
three-custom-shader-material: 5.4.0(three@0.172.0)
|
three-custom-shader-material: 5.4.0(react@19.1.1)(three@0.172.0)
|
||||||
three-stdlib: 2.36.0(three@0.172.0)
|
three-stdlib: 2.36.0(three@0.172.0)
|
||||||
vue: 3.5.13(typescript@5.5.4)
|
vue: 3.5.13(typescript@5.5.4)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -12398,7 +12402,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es-define-property: 1.0.0
|
es-define-property: 1.0.0
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
gopd: 1.0.1
|
gopd: 1.2.0
|
||||||
|
|
||||||
define-lazy-prop@2.0.0: {}
|
define-lazy-prop@2.0.0: {}
|
||||||
|
|
||||||
@@ -13420,7 +13424,7 @@ snapshots:
|
|||||||
|
|
||||||
gopd@1.0.1:
|
gopd@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.3.0
|
||||||
|
|
||||||
gopd@1.2.0: {}
|
gopd@1.2.0: {}
|
||||||
|
|
||||||
@@ -15730,6 +15734,9 @@ snapshots:
|
|||||||
defu: 6.1.4
|
defu: 6.1.4
|
||||||
destr: 2.0.3
|
destr: 2.0.3
|
||||||
|
|
||||||
|
react@19.1.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
read-cache@1.0.0:
|
read-cache@1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
pify: 2.3.0
|
pify: 2.3.0
|
||||||
@@ -16672,13 +16679,15 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
any-promise: 1.3.0
|
any-promise: 1.3.0
|
||||||
|
|
||||||
three-custom-shader-material@5.4.0(three@0.172.0):
|
three-custom-shader-material@5.4.0(react@19.1.1)(three@0.172.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
glsl-token-functions: 1.0.1
|
glsl-token-functions: 1.0.1
|
||||||
glsl-token-string: 1.0.1
|
glsl-token-string: 1.0.1
|
||||||
glsl-tokenizer: 2.1.5
|
glsl-tokenizer: 2.1.5
|
||||||
object-hash: 3.0.0
|
object-hash: 3.0.0
|
||||||
three: 0.172.0
|
three: 0.172.0
|
||||||
|
optionalDependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
|
||||||
three-stdlib@2.36.0(three@0.172.0):
|
three-stdlib@2.36.0(three@0.172.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -16794,7 +16803,7 @@ snapshots:
|
|||||||
|
|
||||||
typescript-auto-import-cache@0.3.3:
|
typescript-auto-import-cache@0.3.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.7.1
|
semver: 7.7.2
|
||||||
|
|
||||||
typescript-eslint@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3):
|
typescript-eslint@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user