[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(i18n,ui): add privacy policy page (#900)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Roe <daniel@roe.dev>

authored by

Joaquín Sánchez
autofix-ci[bot]
Daniel Roe
and committed by
GitHub
d74812e4 65212b1b

+629 -2
+6
app/components/AppFooter.vue
··· 18 18 <NuxtLink to="/about" class="link-subtle font-mono text-xs flex items-center"> 19 19 {{ $t('footer.about') }} 20 20 </NuxtLink> 21 + <NuxtLink 22 + to="/privacy" 23 + class="link-subtle font-mono text-xs min-h-11 flex items-center gap-1 lowercase" 24 + > 25 + {{ $t('privacy_policy.title') }} 26 + </NuxtLink> 21 27 <a 22 28 href="https://docs.npmx.dev" 23 29 target="_blank"
+9
app/components/Header/MobileMenu.client.vue
··· 113 113 </NuxtLink> 114 114 115 115 <NuxtLink 116 + to="/privacy" 117 + class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200" 118 + @click="closeMenu" 119 + > 120 + <span class="i-carbon:security w-5 h-5 text-fg-muted" aria-hidden="true" /> 121 + {{ $t('privacy_policy.title') }} 122 + </NuxtLink> 123 + 124 + <NuxtLink 116 125 to="/compare" 117 126 class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200" 118 127 @click="closeMenu"
+354
app/pages/privacy.vue
··· 1 + <script setup lang="ts"> 2 + definePageMeta({ 3 + name: 'privacy', 4 + }) 5 + 6 + useSeoMeta({ 7 + title: () => `${$t('privacy_policy.title')} - npmx`, 8 + description: () => $t('privacy_policy.welcome', { app: 'npmx' }), 9 + }) 10 + 11 + defineOgImageComponent('Default', { 12 + title: () => $t('privacy_policy.title'), 13 + description: () => $t('privacy_policy.welcome', { app: 'npmx' }), 14 + }) 15 + 16 + const router = useRouter() 17 + const buildInfo = useAppConfig().buildInfo 18 + const { locale } = useI18n() 19 + </script> 20 + 21 + <template> 22 + <main class="container flex-1 py-12 sm:py-16 overflow-x-hidden"> 23 + <article class="max-w-2xl mx-auto"> 24 + <header class="mb-12"> 25 + <div class="flex items-baseline justify-between gap-4 mb-4"> 26 + <h1 class="font-mono text-3xl sm:text-4xl font-medium"> 27 + {{ $t('privacy_policy.title') }} 28 + </h1> 29 + <button 30 + type="button" 31 + :title="$t('nav.back')" 32 + class="inline-flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors duration-200 rounded focus-visible:outline-accent/70 shrink-0" 33 + @click="router.back()" 34 + > 35 + <span class="i-carbon:arrow-left rtl-flip w-4 h-4" aria-hidden="true" /> 36 + <span class="hidden sm:inline">{{ $t('nav.back') }}</span> 37 + </button> 38 + </div> 39 + <i18n-t 40 + keypath="privacy_policy.last_updated" 41 + tag="p" 42 + scope="global" 43 + class="text-fg-muted text-lg" 44 + > 45 + <template #date> 46 + <NuxtTime 47 + :locale 48 + :datetime="buildInfo.privacyPolicyDate" 49 + date-style="long" 50 + time-style="medium" 51 + /> 52 + </template> 53 + </i18n-t> 54 + </header> 55 + 56 + <section class="prose prose-invert max-w-none space-y-8"> 57 + <p class="text-fg-muted leading-relaxed"> 58 + <i18n-t keypath="privacy_policy.welcome" tag="span" scope="global"> 59 + <template #app> 60 + <strong class="text-fg">npmx</strong> 61 + </template> 62 + </i18n-t> 63 + </p> 64 + 65 + <!-- What are cookies --> 66 + <div> 67 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 68 + {{ $t('privacy_policy.cookies.what_are.title') }} 69 + </h2> 70 + <p class="text-fg-muted leading-relaxed"> 71 + {{ $t('privacy_policy.cookies.what_are.p1') }} 72 + </p> 73 + </div> 74 + 75 + <!-- What cookies do we use --> 76 + <div> 77 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 78 + {{ $t('privacy_policy.cookies.types.title') }} 79 + </h2> 80 + <p class="text-fg-muted leading-relaxed mb-4"> 81 + <i18n-t keypath="privacy_policy.cookies.types.p1" tag="span" scope="global"> 82 + <template #bold> 83 + <strong class="text-fg">{{ $t('privacy_policy.cookies.types.bold') }}</strong> 84 + </template> 85 + </i18n-t> 86 + </p> 87 + <ul class="space-y-3 text-fg-muted list-none p-0"> 88 + <li class="flex items-start gap-3"> 89 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 90 + <span> 91 + <i18n-t keypath="privacy_policy.cookies.types.li1" tag="span"> 92 + <template #li11> 93 + <strong class="text-fg font-mono text-sm"> 94 + <bdi>{{ $t('privacy_policy.cookies.types.cookie_vdpl') }}</bdi> 95 + </strong> 96 + </template> 97 + <template #separator> 98 + <bdi>{{ $t('privacy_policy.cookies.types.separator') }}</bdi> 99 + </template> 100 + <template #li12> 101 + <bdi>{{ $t('privacy_policy.cookies.types.cookie_vdpl_desc') }}</bdi> 102 + </template> 103 + </i18n-t> 104 + </span> 105 + </li> 106 + <li class="flex items-start gap-3"> 107 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 108 + <span> 109 + <i18n-t keypath="privacy_policy.cookies.types.li2" tag="span"> 110 + <template #li21> 111 + <strong class="text-fg font-mono text-sm"> 112 + <bdi>{{ $t('privacy_policy.cookies.types.cookie_h3') }}</bdi> 113 + </strong> 114 + </template> 115 + <template #separator> 116 + <bdi>{{ $t('privacy_policy.cookies.types.separator') }}</bdi> 117 + </template> 118 + <template #li22> 119 + <bdi>{{ $t('privacy_policy.cookies.types.cookie_h3_desc') }}</bdi> 120 + </template> 121 + </i18n-t> 122 + </span> 123 + </li> 124 + </ul> 125 + </div> 126 + 127 + <!-- Local storage --> 128 + <div> 129 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 130 + {{ $t('privacy_policy.cookies.local_storage.title') }} 131 + </h2> 132 + <p class="text-fg-muted leading-relaxed mb-4"> 133 + <i18n-t keypath="privacy_policy.cookies.local_storage.p1" tag="span" scope="global"> 134 + <template #bold> 135 + <strong class="text-fg">{{ 136 + $t('privacy_policy.cookies.local_storage.bold') 137 + }}</strong> 138 + </template> 139 + <template #settings> 140 + <NuxtLink 141 + to="/settings" 142 + class="text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg" 143 + > 144 + {{ $t('privacy_policy.cookies.local_storage.settings') }} 145 + </NuxtLink> 146 + </template> 147 + </i18n-t> 148 + </p> 149 + <p class="text-fg-muted leading-relaxed"> 150 + <i18n-t keypath="privacy_policy.cookies.local_storage.p2" tag="span" scope="global"> 151 + <template #bold2> 152 + <strong class="text-fg">{{ 153 + $t('privacy_policy.cookies.local_storage.bold2') 154 + }}</strong> 155 + </template> 156 + </i18n-t> 157 + </p> 158 + </div> 159 + 160 + <!-- Managing cookies --> 161 + <div> 162 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 163 + {{ $t('privacy_policy.cookies.management.title') }} 164 + </h2> 165 + <p class="text-fg-muted leading-relaxed mb-4"> 166 + <i18n-t keypath="privacy_policy.cookies.management.p1" tag="span" scope="global"> 167 + <template #bold> 168 + <strong class="text-fg">{{ $t('privacy_policy.cookies.management.bold') }}</strong> 169 + </template> 170 + </i18n-t> 171 + </p> 172 + <p class="text-fg-muted leading-relaxed mb-4"> 173 + {{ $t('privacy_policy.cookies.management.p2') }} 174 + </p> 175 + <ul class="space-y-3 text-fg-muted list-none p-0"> 176 + <li class="flex items-start gap-3"> 177 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 178 + <a 179 + href="https://support.google.com/chrome/answer/95647?hl=en" 180 + target="_blank" 181 + rel="noopener noreferrer" 182 + class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg" 183 + > 184 + {{ $t('privacy_policy.cookies.management.chrome') }} 185 + <span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" /> 186 + </a> 187 + </li> 188 + <li class="flex items-start gap-3"> 189 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 190 + <a 191 + href="https://support.mozilla.org/en-US/kb/clear-cookies-and-site-data-firefox" 192 + target="_blank" 193 + rel="noopener noreferrer" 194 + class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg" 195 + > 196 + {{ $t('privacy_policy.cookies.management.firefox') }} 197 + <span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" /> 198 + </a> 199 + </li> 200 + <li class="flex items-start gap-3"> 201 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 202 + <a 203 + href="https://support.microsoft.com/en-us/windows/manage-cookies-in-microsoft-edge-view-allow-block-delete-and-use-168dab11-0753-043d-7c16-ede5947fc64d" 204 + target="_blank" 205 + rel="noopener noreferrer" 206 + class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg" 207 + > 208 + {{ $t('privacy_policy.cookies.management.edge') }} 209 + <span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" /> 210 + </a> 211 + </li> 212 + </ul> 213 + </div> 214 + 215 + <!-- Analytics --> 216 + <div> 217 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 218 + {{ $t('privacy_policy.analytics.title') }} 219 + </h2> 220 + <p class="text-fg-muted leading-relaxed mb-4"> 221 + <i18n-t keypath="privacy_policy.analytics.p1" tag="span" scope="global"> 222 + <template #bold> 223 + <strong class="text-fg">{{ $t('privacy_policy.analytics.bold') }}</strong> 224 + </template> 225 + </i18n-t> 226 + </p> 227 + <p class="text-fg-muted leading-relaxed mb-4"> 228 + {{ $t('privacy_policy.analytics.p2') }} 229 + </p> 230 + <ul class="space-y-3 text-fg-muted list-none p-0 mb-4"> 231 + <li class="flex items-start gap-3"> 232 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 233 + <span>{{ $t('privacy_policy.analytics.li1') }}</span> 234 + </li> 235 + <li class="flex items-start gap-3"> 236 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 237 + <span>{{ $t('privacy_policy.analytics.li2') }}</span> 238 + </li> 239 + <li class="flex items-start gap-3"> 240 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 241 + <span>{{ $t('privacy_policy.analytics.li3') }}</span> 242 + </li> 243 + <li class="flex items-start gap-3"> 244 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 245 + <span>{{ $t('privacy_policy.analytics.li4') }}</span> 246 + </li> 247 + </ul> 248 + <p class="text-fg-muted leading-relaxed"> 249 + {{ $t('privacy_policy.analytics.p3') }} 250 + </p> 251 + </div> 252 + 253 + <!-- Authenticated Users --> 254 + <div> 255 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 256 + {{ $t('privacy_policy.authenticated.title') }} 257 + </h2> 258 + <p class="text-fg-muted leading-relaxed mb-4"> 259 + <i18n-t keypath="privacy_policy.authenticated.p1" tag="span" scope="global"> 260 + <template #bold> 261 + <strong class="text-fg">{{ $t('privacy_policy.authenticated.bold') }}</strong> 262 + </template> 263 + </i18n-t> 264 + </p> 265 + <p class="text-fg-muted leading-relaxed"> 266 + <i18n-t keypath="privacy_policy.authenticated.p2" tag="span" scope="global"> 267 + <template #settings> 268 + <NuxtLink 269 + to="/settings" 270 + class="text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg" 271 + > 272 + {{ $t('privacy_policy.authenticated.settings') }} 273 + </NuxtLink> 274 + </template> 275 + </i18n-t> 276 + </p> 277 + </div> 278 + 279 + <!-- Data Retention --> 280 + <div> 281 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 282 + {{ $t('privacy_policy.data_retention.title') }} 283 + </h2> 284 + <p class="text-fg-muted leading-relaxed"> 285 + {{ $t('privacy_policy.data_retention.p1') }} 286 + </p> 287 + </div> 288 + 289 + <!-- Your Rights --> 290 + <div> 291 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 292 + {{ $t('privacy_policy.your_rights.title') }} 293 + </h2> 294 + <p class="text-fg-muted leading-relaxed mb-4"> 295 + {{ $t('privacy_policy.your_rights.p1') }} 296 + </p> 297 + <ul class="space-y-3 text-fg-muted list-none p-0 mb-4"> 298 + <li class="flex items-start gap-3"> 299 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 300 + <span>{{ $t('privacy_policy.your_rights.li1') }}</span> 301 + </li> 302 + <li class="flex items-start gap-3"> 303 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 304 + <span>{{ $t('privacy_policy.your_rights.li2') }}</span> 305 + </li> 306 + <li class="flex items-start gap-3"> 307 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 308 + <span>{{ $t('privacy_policy.your_rights.li3') }}</span> 309 + </li> 310 + <li class="flex items-start gap-3"> 311 + <span class="text-fg-subtle shrink-0 mt-1">&mdash;</span> 312 + <span>{{ $t('privacy_policy.your_rights.li4') }}</span> 313 + </li> 314 + </ul> 315 + <p class="text-fg-muted leading-relaxed"> 316 + {{ $t('privacy_policy.your_rights.p2') }} 317 + </p> 318 + </div> 319 + 320 + <!-- Contact --> 321 + <div> 322 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 323 + {{ $t('privacy_policy.contact.title') }} 324 + </h2> 325 + <p class="text-fg-muted leading-relaxed"> 326 + <i18n-t keypath="privacy_policy.contact.p1" tag="span" scope="global"> 327 + <template #link> 328 + <a 329 + href="https://github.com/npmx-dev/npmx.dev/issues" 330 + target="_blank" 331 + rel="noopener noreferrer" 332 + class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg" 333 + > 334 + {{ $t('privacy_policy.contact.link') }} 335 + <span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" /> 336 + </a> 337 + </template> 338 + </i18n-t> 339 + </p> 340 + </div> 341 + 342 + <!-- Changes --> 343 + <div> 344 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 345 + {{ $t('privacy_policy.changes.title') }} 346 + </h2> 347 + <p class="text-fg-muted leading-relaxed"> 348 + {{ $t('privacy_policy.changes.p1') }} 349 + </p> 350 + </div> 351 + </section> 352 + </article> 353 + </main> 354 + </template>
+10
config/env.ts
··· 73 73 return { branch, commit, shortCommit } 74 74 } 75 75 76 + export async function getFileLastUpdated(path: string) { 77 + try { 78 + // Get ISO date of last commit for file 79 + const date = await git.log(['-1', '--format=%cI', '--', path]) 80 + return date.latest?.date || new Date().toISOString() 81 + } catch { 82 + return new Date().toISOString() 83 + } 84 + } 85 + 76 86 export async function getEnv(isDevelopment: boolean) { 77 87 const { commit, shortCommit, branch } = await getGitInfo() 78 88 const env = isDevelopment
+81
i18n/locales/en.json
··· 971 971 "up_to_you": "Up to you!" 972 972 } 973 973 } 974 + }, 975 + "privacy_policy": { 976 + "title": "privacy policy", 977 + "last_updated": "Last updated: {date}", 978 + "welcome": "Welcome to {app}. We are committed to protecting your privacy. This policy explains what data we collect, how we use it, and your rights regarding your information.", 979 + "cookies": { 980 + "title": "Cookies", 981 + "what_are": { 982 + "title": "What are cookies?", 983 + "p1": "Cookies are small text files stored on your device when you visit a website. Their purpose is to enhance your browsing experience by remembering certain preferences and settings." 984 + }, 985 + "types": { 986 + "title": "What cookies do we use?", 987 + "p1": "We only use {bold} for purposes strictly necessary for the site's functionality. We do not use third-party or advertising cookies.", 988 + "bold": "essential technical cookies", 989 + "li1": "{li11}{separator} {li12}", 990 + "li2": "{li21}{separator} {li22}", 991 + "separator": ":", 992 + "cookie_vdpl": "__vdpl", 993 + "cookie_vdpl_desc": "This cookie is used by our hosting provider (Vercel) for skew protection. It ensures you fetch assets from the correct deployment version if a new update is released while you are browsing. It does not track you.", 994 + "cookie_h3": "h3", 995 + "cookie_h3_desc": "This is our secure session cookie. It stores the OAuth access token when you connect your Atmosphere account. It is essential for maintaining your authenticated session." 996 + }, 997 + "local_storage": { 998 + "title": "Local storage", 999 + "p1": "In addition to session cookies, we use your browser's {bold} to save your display preferences. This allows us to remember the theme (light/dark) and some other {settings} you have selected, so you don't have to reconfigure them on each visit.", 1000 + "bold": "Local Storage", 1001 + "p2": "This information is purely functional, stored only on your device, and {bold2}. We use it exclusively to improve your experience on our website.", 1002 + "bold2": "contains no personal data nor is it used to track you", 1003 + "settings": "settings" 1004 + }, 1005 + "management": { 1006 + "title": "Managing cookies", 1007 + "p1": "You can configure your browser to accept, reject, or delete cookies according to your preferences. However, please note that {bold}.", 1008 + "bold": "rejecting essential cookies may prevent full access to the application", 1009 + "p2": "Below are links with instructions for cookie management in the most commonly used browsers:", 1010 + "chrome": "Google Chrome (opens in a new window)", 1011 + "firefox": "Mozilla Firefox (opens in a new window)", 1012 + "edge": "Microsoft Edge (opens in a new window)" 1013 + } 1014 + }, 1015 + "analytics": { 1016 + "title": "Analytics", 1017 + "p1": "We use {bold} to understand how visitors use our website. This helps us improve the user experience and identify issues.", 1018 + "bold": "Vercel Web Analytics", 1019 + "p2": "Vercel Analytics is designed with privacy in mind:", 1020 + "li1": "It does not use cookies", 1021 + "li2": "It does not collect personal identifiers", 1022 + "li3": "It does not track users across websites", 1023 + "li4": "All data is aggregated and anonymised", 1024 + "p3": "The only information collected includes: page URLs, referrer, country/region, device type, browser, and operating system. This data cannot be used to identify individual users." 1025 + }, 1026 + "authenticated": { 1027 + "title": "Authenticated users", 1028 + "p1": "When you connect your {bold} account to npmx, we store your OAuth access token in a secure, HTTP-only session cookie. This token is used solely to authenticate requests on your behalf.", 1029 + "bold": "Atmosphere", 1030 + "p2": "We do not store your credentials, and we do not access any data beyond what is necessary to provide the features you use. You can disconnect your account at any time from the {settings} page.", 1031 + "settings": "settings" 1032 + }, 1033 + "data_retention": { 1034 + "title": "Data retention", 1035 + "p1": "Session cookies are automatically deleted when you close your browser or after a period of inactivity. Local storage preferences remain on your device until you clear your browser data. Analytics data is retained in aggregate form and cannot be linked to individual users." 1036 + }, 1037 + "your_rights": { 1038 + "title": "Your rights", 1039 + "p1": "You have the right to:", 1040 + "li1": "Access information about what data we collect", 1041 + "li2": "Clear your local storage and cookies at any time", 1042 + "li3": "Disconnect your authenticated session", 1043 + "li4": "Request information about our data practices", 1044 + "p2": "Since we do not collect personal data, there is typically no personal information to delete or export." 1045 + }, 1046 + "contact": { 1047 + "title": "Contact us", 1048 + "p1": "For any questions or concerns about this privacy policy, you can contact us by opening an issue on our {link}.", 1049 + "link": "GitHub repository" 1050 + }, 1051 + "changes": { 1052 + "title": "Changes to this policy", 1053 + "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." 1054 + } 974 1055 } 975 1056 }
+81
lunaria/files/en-GB.json
··· 971 971 "up_to_you": "Up to you!" 972 972 } 973 973 } 974 + }, 975 + "privacy_policy": { 976 + "title": "privacy policy", 977 + "last_updated": "Last updated: {date}", 978 + "welcome": "Welcome to {app}. We are committed to protecting your privacy. This policy explains what data we collect, how we use it, and your rights regarding your information.", 979 + "cookies": { 980 + "title": "Cookies", 981 + "what_are": { 982 + "title": "What are cookies?", 983 + "p1": "Cookies are small text files stored on your device when you visit a website. Their purpose is to enhance your browsing experience by remembering certain preferences and settings." 984 + }, 985 + "types": { 986 + "title": "What cookies do we use?", 987 + "p1": "We only use {bold} for purposes strictly necessary for the site's functionality. We do not use third-party or advertising cookies.", 988 + "bold": "essential technical cookies", 989 + "li1": "{li11}{separator} {li12}", 990 + "li2": "{li21}{separator} {li22}", 991 + "separator": ":", 992 + "cookie_vdpl": "__vdpl", 993 + "cookie_vdpl_desc": "This cookie is used by our hosting provider (Vercel) for skew protection. It ensures you fetch assets from the correct deployment version if a new update is released while you are browsing. It does not track you.", 994 + "cookie_h3": "h3", 995 + "cookie_h3_desc": "This is our secure session cookie. It stores the OAuth access token when you connect your Atmosphere account. It is essential for maintaining your authenticated session." 996 + }, 997 + "local_storage": { 998 + "title": "Local storage", 999 + "p1": "In addition to session cookies, we use your browser's {bold} to save your display preferences. This allows us to remember the theme (light/dark) and some other {settings} you have selected, so you don't have to reconfigure them on each visit.", 1000 + "bold": "Local Storage", 1001 + "p2": "This information is purely functional, stored only on your device, and {bold2}. We use it exclusively to improve your experience on our website.", 1002 + "bold2": "contains no personal data nor is it used to track you", 1003 + "settings": "settings" 1004 + }, 1005 + "management": { 1006 + "title": "Managing cookies", 1007 + "p1": "You can configure your browser to accept, reject, or delete cookies according to your preferences. However, please note that {bold}.", 1008 + "bold": "rejecting essential cookies may prevent full access to the application", 1009 + "p2": "Below are links with instructions for cookie management in the most commonly used browsers:", 1010 + "chrome": "Google Chrome (opens in a new window)", 1011 + "firefox": "Mozilla Firefox (opens in a new window)", 1012 + "edge": "Microsoft Edge (opens in a new window)" 1013 + } 1014 + }, 1015 + "analytics": { 1016 + "title": "Analytics", 1017 + "p1": "We use {bold} to understand how visitors use our website. This helps us improve the user experience and identify issues.", 1018 + "bold": "Vercel Web Analytics", 1019 + "p2": "Vercel Analytics is designed with privacy in mind:", 1020 + "li1": "It does not use cookies", 1021 + "li2": "It does not collect personal identifiers", 1022 + "li3": "It does not track users across websites", 1023 + "li4": "All data is aggregated and anonymised", 1024 + "p3": "The only information collected includes: page URLs, referrer, country/region, device type, browser, and operating system. This data cannot be used to identify individual users." 1025 + }, 1026 + "authenticated": { 1027 + "title": "Authenticated users", 1028 + "p1": "When you connect your {bold} account to npmx, we store your OAuth access token in a secure, HTTP-only session cookie. This token is used solely to authenticate requests on your behalf.", 1029 + "bold": "Atmosphere", 1030 + "p2": "We do not store your credentials, and we do not access any data beyond what is necessary to provide the features you use. You can disconnect your account at any time from the {settings} page.", 1031 + "settings": "settings" 1032 + }, 1033 + "data_retention": { 1034 + "title": "Data retention", 1035 + "p1": "Session cookies are automatically deleted when you close your browser or after a period of inactivity. Local storage preferences remain on your device until you clear your browser data. Analytics data is retained in aggregate form and cannot be linked to individual users." 1036 + }, 1037 + "your_rights": { 1038 + "title": "Your rights", 1039 + "p1": "You have the right to:", 1040 + "li1": "Access information about what data we collect", 1041 + "li2": "Clear your local storage and cookies at any time", 1042 + "li3": "Disconnect your authenticated session", 1043 + "li4": "Request information about our data practices", 1044 + "p2": "Since we do not collect personal data, there is typically no personal information to delete or export." 1045 + }, 1046 + "contact": { 1047 + "title": "Contact us", 1048 + "p1": "For any questions or concerns about this privacy policy, you can contact us by opening an issue on our {link}.", 1049 + "link": "GitHub repository" 1050 + }, 1051 + "changes": { 1052 + "title": "Changes to this policy", 1053 + "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." 1054 + } 974 1055 } 975 1056 }
+81
lunaria/files/en-US.json
··· 971 971 "up_to_you": "Up to you!" 972 972 } 973 973 } 974 + }, 975 + "privacy_policy": { 976 + "title": "privacy policy", 977 + "last_updated": "Last updated: {date}", 978 + "welcome": "Welcome to {app}. We are committed to protecting your privacy. This policy explains what data we collect, how we use it, and your rights regarding your information.", 979 + "cookies": { 980 + "title": "Cookies", 981 + "what_are": { 982 + "title": "What are cookies?", 983 + "p1": "Cookies are small text files stored on your device when you visit a website. Their purpose is to enhance your browsing experience by remembering certain preferences and settings." 984 + }, 985 + "types": { 986 + "title": "What cookies do we use?", 987 + "p1": "We only use {bold} for purposes strictly necessary for the site's functionality. We do not use third-party or advertising cookies.", 988 + "bold": "essential technical cookies", 989 + "li1": "{li11}{separator} {li12}", 990 + "li2": "{li21}{separator} {li22}", 991 + "separator": ":", 992 + "cookie_vdpl": "__vdpl", 993 + "cookie_vdpl_desc": "This cookie is used by our hosting provider (Vercel) for skew protection. It ensures you fetch assets from the correct deployment version if a new update is released while you are browsing. It does not track you.", 994 + "cookie_h3": "h3", 995 + "cookie_h3_desc": "This is our secure session cookie. It stores the OAuth access token when you connect your Atmosphere account. It is essential for maintaining your authenticated session." 996 + }, 997 + "local_storage": { 998 + "title": "Local storage", 999 + "p1": "In addition to session cookies, we use your browser's {bold} to save your display preferences. This allows us to remember the theme (light/dark) and some other {settings} you have selected, so you don't have to reconfigure them on each visit.", 1000 + "bold": "Local Storage", 1001 + "p2": "This information is purely functional, stored only on your device, and {bold2}. We use it exclusively to improve your experience on our website.", 1002 + "bold2": "contains no personal data nor is it used to track you", 1003 + "settings": "settings" 1004 + }, 1005 + "management": { 1006 + "title": "Managing cookies", 1007 + "p1": "You can configure your browser to accept, reject, or delete cookies according to your preferences. However, please note that {bold}.", 1008 + "bold": "rejecting essential cookies may prevent full access to the application", 1009 + "p2": "Below are links with instructions for cookie management in the most commonly used browsers:", 1010 + "chrome": "Google Chrome (opens in a new window)", 1011 + "firefox": "Mozilla Firefox (opens in a new window)", 1012 + "edge": "Microsoft Edge (opens in a new window)" 1013 + } 1014 + }, 1015 + "analytics": { 1016 + "title": "Analytics", 1017 + "p1": "We use {bold} to understand how visitors use our website. This helps us improve the user experience and identify issues.", 1018 + "bold": "Vercel Web Analytics", 1019 + "p2": "Vercel Analytics is designed with privacy in mind:", 1020 + "li1": "It does not use cookies", 1021 + "li2": "It does not collect personal identifiers", 1022 + "li3": "It does not track users across websites", 1023 + "li4": "All data is aggregated and anonymised", 1024 + "p3": "The only information collected includes: page URLs, referrer, country/region, device type, browser, and operating system. This data cannot be used to identify individual users." 1025 + }, 1026 + "authenticated": { 1027 + "title": "Authenticated users", 1028 + "p1": "When you connect your {bold} account to npmx, we store your OAuth access token in a secure, HTTP-only session cookie. This token is used solely to authenticate requests on your behalf.", 1029 + "bold": "Atmosphere", 1030 + "p2": "We do not store your credentials, and we do not access any data beyond what is necessary to provide the features you use. You can disconnect your account at any time from the {settings} page.", 1031 + "settings": "settings" 1032 + }, 1033 + "data_retention": { 1034 + "title": "Data retention", 1035 + "p1": "Session cookies are automatically deleted when you close your browser or after a period of inactivity. Local storage preferences remain on your device until you clear your browser data. Analytics data is retained in aggregate form and cannot be linked to individual users." 1036 + }, 1037 + "your_rights": { 1038 + "title": "Your rights", 1039 + "p1": "You have the right to:", 1040 + "li1": "Access information about what data we collect", 1041 + "li2": "Clear your local storage and cookies at any time", 1042 + "li3": "Disconnect your authenticated session", 1043 + "li4": "Request information about our data practices", 1044 + "p2": "Since we do not collect personal data, there is typically no personal information to delete or export." 1045 + }, 1046 + "contact": { 1047 + "title": "Contact us", 1048 + "p1": "For any questions or concerns about this privacy policy, you can contact us by opening an issue on our {link}.", 1049 + "link": "GitHub repository" 1050 + }, 1051 + "changes": { 1052 + "title": "Changes to this policy", 1053 + "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." 1054 + } 974 1055 } 975 1056 }
+6 -2
modules/build-env.ts
··· 1 1 import type { BuildInfo } from '../shared/types' 2 2 import { createResolver, defineNuxtModule } from 'nuxt/kit' 3 3 import { isCI } from 'std-env' 4 - import { getEnv, version } from '../config/env' 4 + import { getEnv, getFileLastUpdated, version } from '../config/env' 5 5 6 6 const { resolve } = createResolver(import.meta.url) 7 7 ··· 10 10 name: 'npmx:build-env', 11 11 }, 12 12 async setup(_options, nuxt) { 13 - const { env, commit, shortCommit, branch } = await getEnv(nuxt.options.dev) 13 + const [{ env, commit, shortCommit, branch }, privacyPolicyDate] = await Promise.all([ 14 + getEnv(nuxt.options.dev), 15 + getFileLastUpdated('app/pages/privacy.vue'), 16 + ]) 14 17 15 18 nuxt.options.appConfig = nuxt.options.appConfig || {} 16 19 nuxt.options.appConfig.env = env ··· 21 24 shortCommit, 22 25 branch, 23 26 env, 27 + privacyPolicyDate, 24 28 } satisfies BuildInfo 25 29 26 30 nuxt.options.nitro.publicAssets = nuxt.options.nitro.publicAssets || []
+1
shared/types/env.ts
··· 5 5 time: number 6 6 branch: string 7 7 env: 'preview' | 'canary' | 'dev' | 'release' 8 + privacyPolicyDate: string 8 9 }