diff --git a/frontend/.storybook/decorators/with-rtl.js b/frontend/.storybook/decorators/with-rtl.js
index 7c67557fcff..a5498b9fd49 100644
--- a/frontend/.storybook/decorators/with-rtl.js
+++ b/frontend/.storybook/decorators/with-rtl.js
@@ -1,5 +1,4 @@
import { useI18n } from "#imports"
-
import { ref, watch, onMounted, reactive, h } from "vue"
import { useEffect } from "@storybook/preview-api"
diff --git a/frontend/.storybook/decorators/with-theme.ts b/frontend/.storybook/decorators/with-theme.ts
index b8945605f17..8cab2bb9daf 100644
--- a/frontend/.storybook/decorators/with-theme.ts
+++ b/frontend/.storybook/decorators/with-theme.ts
@@ -1,10 +1,10 @@
import { watch, onMounted, reactive, h } from "vue"
-import { useEffect, useGlobals } from "@storybook/preview-api"
-import { EffectiveColorMode } from "~/types/ui"
+import { useEffect, useGlobals } from "@storybook/preview-api"
-import { useDarkMode } from "~/composables/use-dark-mode"
+import { EffectiveColorMode } from "#shared/types/ui"
import { useUiStore } from "~/stores/ui"
+import { useDarkMode } from "~/composables/use-dark-mode"
import VThemeSelect from "~/components/VThemeSelect/VThemeSelect.vue"
diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts
index d906ee79e1a..da31d0b480a 100644
--- a/frontend/.storybook/preview.ts
+++ b/frontend/.storybook/preview.ts
@@ -1,5 +1,4 @@
-import { VIEWPORTS } from "~/constants/screens"
-
+import { VIEWPORTS } from "#shared/constants/screens"
import { WithUiStore } from "~~/.storybook/decorators/with-ui-store"
import { WithRTL } from "~~/.storybook/decorators/with-rtl"
import { WithTheme } from "~~/.storybook/decorators/with-theme"
diff --git a/frontend/bin/get-custom-event-names.js b/frontend/bin/get-custom-event-names.js
index 1f630bddbf2..e2b6e64f36a 100644
--- a/frontend/bin/get-custom-event-names.js
+++ b/frontend/bin/get-custom-event-names.js
@@ -11,7 +11,7 @@ const ts = require("typescript")
const analyticsTypesModule = path.resolve(
__dirname,
"..",
- "src",
+ "shared",
"types",
"analytics.ts"
)
diff --git a/frontend/i18n/locales/scripts/axios.js b/frontend/i18n/locales/scripts/axios.js
index 7367aa18274..65a12621a1a 100644
--- a/frontend/i18n/locales/scripts/axios.js
+++ b/frontend/i18n/locales/scripts/axios.js
@@ -1,6 +1,6 @@
const axios = require("axios")
-const { userAgent } = require("../../../src/constants/user-agent")
+const { userAgent } = require("../../../shared/constants/user-agent")
module.exports = module.exports = axios.create({
headers: { "User-Agent": userAgent },
diff --git a/frontend/i18n/locales/scripts/bulk-download.js b/frontend/i18n/locales/scripts/bulk-download.js
index 163330366ae..fa70fe94a0a 100644
--- a/frontend/i18n/locales/scripts/bulk-download.js
+++ b/frontend/i18n/locales/scripts/bulk-download.js
@@ -1,5 +1,4 @@
const { pipeline } = require("stream/promises")
-
const { createWriteStream } = require("fs")
const AdmZip = require("adm-zip")
diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts
index 1a37f4e89aa..7e4f20d2b09 100644
--- a/frontend/nuxt.config.ts
+++ b/frontend/nuxt.config.ts
@@ -1,6 +1,6 @@
import { defineNuxtConfig } from "nuxt/config"
-import { disallowedBots } from "./src/constants/disallowed-bots"
+import { disallowedBots } from "./shared/constants/disallowed-bots"
import locales from "./i18n/locales/scripts/valid-locales.json"
import type { LocaleObject } from "@nuxtjs/i18n"
diff --git a/frontend/package.json b/frontend/package.json
index c1dc6c9e789..63af396d0a6 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -108,14 +108,14 @@
"jsdom": "^25.0.0",
"node-html-parser": "^6.1.13",
"npm-run-all2": "^7.0.0",
- "nuxt": "3.13.2",
+ "nuxt": "^3.14.1592",
"rimraf": "^6.0.1",
"storybook": "^8.3.6",
"talkback": "^4.2.0",
"typescript": "5.6.3",
"vitest": "^2.1.4",
"vitest-dom": "^0.1.1",
- "vue": "3.5.13",
+ "vue": "^3.5.13",
"vue-router": "^4.4.5",
"vue-tsc": "2.1.10",
"vue-i18n": "10.0.5"
diff --git a/frontend/scripts/document-media.js b/frontend/scripts/document-media.js
index f707bce39a0..76862c56de2 100644
--- a/frontend/scripts/document-media.js
+++ b/frontend/scripts/document-media.js
@@ -182,7 +182,7 @@ function extractComments(node, ast) {
* @returns {ts.SourceFile} the AST for the media types file
*/
function getAst() {
- const tsFilePath = path.resolve(__dirname, "../src/types/media.ts")
+ const tsFilePath = path.resolve(__dirname, "../shared/types/media.ts")
console.log(`Parsing ${tsFilePath}.`)
return ts.createSourceFile(
diff --git a/frontend/server/plugins/sentry.ts b/frontend/server/plugins/sentry.ts
index b3cd54b7f13..99bf095d9ad 100644
--- a/frontend/server/plugins/sentry.ts
+++ b/frontend/server/plugins/sentry.ts
@@ -2,7 +2,6 @@ import { useRuntimeConfig, useAppConfig } from "#imports"
import { defineNitroPlugin } from "nitropack/runtime"
import * as Sentry from "@sentry/node"
-
import { logger } from "~~/server/utils/logger"
export default defineNitroPlugin((nitroApp) => {
diff --git a/frontend/server/utils/logger.ts b/frontend/server/utils/logger.ts
index 88b4352afb7..11ff124748f 100644
--- a/frontend/server/utils/logger.ts
+++ b/frontend/server/utils/logger.ts
@@ -1,6 +1,6 @@
import { consola } from "consola"
-import { LOCAL, PRODUCTION, STAGING } from "~/constants/deploy-env"
+import { LOCAL, PRODUCTION, STAGING } from "#shared/constants/deploy-env"
/**
* This logger is used only in the Nitro server.
diff --git a/frontend/src/constants/audio.ts b/frontend/shared/constants/audio.ts
similarity index 100%
rename from frontend/src/constants/audio.ts
rename to frontend/shared/constants/audio.ts
diff --git a/frontend/src/constants/banners.ts b/frontend/shared/constants/banners.ts
similarity index 100%
rename from frontend/src/constants/banners.ts
rename to frontend/shared/constants/banners.ts
diff --git a/frontend/src/constants/content-report.ts b/frontend/shared/constants/content-report.ts
similarity index 100%
rename from frontend/src/constants/content-report.ts
rename to frontend/shared/constants/content-report.ts
diff --git a/frontend/src/constants/content-safety.ts b/frontend/shared/constants/content-safety.ts
similarity index 100%
rename from frontend/src/constants/content-safety.ts
rename to frontend/shared/constants/content-safety.ts
diff --git a/frontend/src/constants/deploy-env.ts b/frontend/shared/constants/deploy-env.ts
similarity index 100%
rename from frontend/src/constants/deploy-env.ts
rename to frontend/shared/constants/deploy-env.ts
diff --git a/frontend/src/constants/dialogs.ts b/frontend/shared/constants/dialogs.ts
similarity index 100%
rename from frontend/src/constants/dialogs.ts
rename to frontend/shared/constants/dialogs.ts
diff --git a/frontend/src/constants/disallowed-bots.ts b/frontend/shared/constants/disallowed-bots.ts
similarity index 100%
rename from frontend/src/constants/disallowed-bots.ts
rename to frontend/shared/constants/disallowed-bots.ts
diff --git a/frontend/src/constants/errors.ts b/frontend/shared/constants/errors.ts
similarity index 100%
rename from frontend/src/constants/errors.ts
rename to frontend/shared/constants/errors.ts
diff --git a/frontend/src/constants/favicons.ts b/frontend/shared/constants/favicons.ts
similarity index 100%
rename from frontend/src/constants/favicons.ts
rename to frontend/shared/constants/favicons.ts
diff --git a/frontend/src/constants/feature-flag.ts b/frontend/shared/constants/feature-flag.ts
similarity index 100%
rename from frontend/src/constants/feature-flag.ts
rename to frontend/shared/constants/feature-flag.ts
diff --git a/frontend/src/constants/filters.ts b/frontend/shared/constants/filters.ts
similarity index 91%
rename from frontend/src/constants/filters.ts
rename to frontend/shared/constants/filters.ts
index 5e002593f2d..cee9a755356 100644
--- a/frontend/src/constants/filters.ts
+++ b/frontend/shared/constants/filters.ts
@@ -1,8 +1,13 @@
-import { ALL_MEDIA, AUDIO, IMAGE, VIDEO, MODEL_3D } from "~/constants/media"
-import { ACTIVE_LICENSES } from "~/constants/license"
-import { deepFreeze } from "~/utils/deep-freeze"
-
-import type { SearchType } from "~/constants/media"
+import {
+ ALL_MEDIA,
+ AUDIO,
+ IMAGE,
+ VIDEO,
+ MODEL_3D,
+} from "#shared/constants/media"
+import { ACTIVE_LICENSES } from "#shared/constants/license"
+import type { SearchType } from "#shared/constants/media"
+import { deepFreeze } from "#shared/utils/deep-freeze"
export interface FilterItem {
code: string
diff --git a/frontend/src/constants/key-codes.ts b/frontend/shared/constants/key-codes.ts
similarity index 100%
rename from frontend/src/constants/key-codes.ts
rename to frontend/shared/constants/key-codes.ts
diff --git a/frontend/src/constants/license.ts b/frontend/shared/constants/license.ts
similarity index 100%
rename from frontend/src/constants/license.ts
rename to frontend/shared/constants/license.ts
diff --git a/frontend/src/constants/media.ts b/frontend/shared/constants/media.ts
similarity index 100%
rename from frontend/src/constants/media.ts
rename to frontend/shared/constants/media.ts
diff --git a/frontend/src/constants/meta.ts b/frontend/shared/constants/meta.ts
similarity index 100%
rename from frontend/src/constants/meta.ts
rename to frontend/shared/constants/meta.ts
diff --git a/frontend/src/constants/screens.ts b/frontend/shared/constants/screens.ts
similarity index 100%
rename from frontend/src/constants/screens.ts
rename to frontend/shared/constants/screens.ts
diff --git a/frontend/src/constants/user-agent.js b/frontend/shared/constants/user-agent.js
similarity index 100%
rename from frontend/src/constants/user-agent.js
rename to frontend/shared/constants/user-agent.js
diff --git a/frontend/src/constants/window.ts b/frontend/shared/constants/window.ts
similarity index 100%
rename from frontend/src/constants/window.ts
rename to frontend/shared/constants/window.ts
diff --git a/frontend/src/constants/z-indices.ts b/frontend/shared/constants/z-indices.ts
similarity index 100%
rename from frontend/src/constants/z-indices.ts
rename to frontend/shared/constants/z-indices.ts
diff --git a/frontend/src/types/analytics.ts b/frontend/shared/types/analytics.ts
similarity index 97%
rename from frontend/src/types/analytics.ts
rename to frontend/shared/types/analytics.ts
index 3a1c4fb2f35..3d10fed25a9 100644
--- a/frontend/src/types/analytics.ts
+++ b/frontend/shared/types/analytics.ts
@@ -3,13 +3,13 @@ import type {
SearchType,
SupportedMediaType,
SupportedSearchType,
-} from "~/constants/media"
-import type { ReportReason } from "~/constants/content-report"
-import type { FilterCategory } from "~/constants/filters"
-import { ResultKind } from "~/types/result"
-import { RequestKind } from "~/types/fetch-state"
-import { Collection } from "~/types/search"
-import { License } from "~/constants/license"
+} from "#shared/constants/media"
+import type { ReportReason } from "#shared/constants/content-report"
+import type { FilterCategory } from "#shared/constants/filters"
+import type { License } from "#shared/constants/license"
+import type { ResultKind } from "#shared/types/result"
+import type { RequestKind } from "#shared/types/fetch-state"
+import type { Collection } from "#shared/types/search"
export type AudioInteraction = "play" | "pause" | "seek"
export type AudioInteractionData = Exclude<
diff --git a/frontend/src/types/banners.ts b/frontend/shared/types/banners.ts
similarity index 100%
rename from frontend/src/types/banners.ts
rename to frontend/shared/types/banners.ts
diff --git a/frontend/src/types/button.ts b/frontend/shared/types/button.ts
similarity index 100%
rename from frontend/src/types/button.ts
rename to frontend/shared/types/button.ts
diff --git a/frontend/src/types/collection-component-props.ts b/frontend/shared/types/collection-component-props.ts
similarity index 76%
rename from frontend/src/types/collection-component-props.ts
rename to frontend/shared/types/collection-component-props.ts
index 7d9b113de30..1811393e894 100644
--- a/frontend/src/types/collection-component-props.ts
+++ b/frontend/shared/types/collection-component-props.ts
@@ -1,6 +1,6 @@
-import { type SupportedSearchType, IMAGE, AUDIO } from "~/constants/media"
-import type { AudioDetail, ImageDetail } from "~/types/media"
-import type { ResultKind, Results } from "~/types/result"
+import { type SupportedSearchType, IMAGE, AUDIO } from "#shared/constants/media"
+import type { AudioDetail, ImageDetail } from "#shared/types/media"
+import type { ResultKind, Results } from "#shared/types/result"
export type SingleResultProps = {
kind: ResultKind
diff --git a/frontend/src/types/cookies.ts b/frontend/shared/types/cookies.ts
similarity index 93%
rename from frontend/src/types/cookies.ts
rename to frontend/shared/types/cookies.ts
index 8db284bfcde..06ec392ac2c 100644
--- a/frontend/src/types/cookies.ts
+++ b/frontend/shared/types/cookies.ts
@@ -1,4 +1,4 @@
-import type { FeatureState } from "~/constants/feature-flag"
+import type { FeatureState } from "#shared/constants/feature-flag"
import { UiState } from "~/stores/ui"
const baseCookieOptions = {
diff --git a/frontend/src/types/events.ts b/frontend/shared/types/events.ts
similarity index 100%
rename from frontend/src/types/events.ts
rename to frontend/shared/types/events.ts
diff --git a/frontend/src/types/feature-flag.ts b/frontend/shared/types/feature-flag.ts
similarity index 89%
rename from frontend/src/types/feature-flag.ts
rename to frontend/shared/types/feature-flag.ts
index 43cb4b1dc6f..329b217606d 100644
--- a/frontend/src/types/feature-flag.ts
+++ b/frontend/shared/types/feature-flag.ts
@@ -4,8 +4,8 @@ import type {
FeatureState,
FlagStatus,
Storage,
-} from "~/constants/feature-flag"
-import type { DeployEnv } from "~/constants/deploy-env"
+} from "#shared/constants/feature-flag"
+import type { DeployEnv } from "#shared/constants/deploy-env"
export type FlagName = keyof (typeof featureData)["features"]
diff --git a/frontend/src/types/fetch-state.ts b/frontend/shared/types/fetch-state.ts
similarity index 86%
rename from frontend/src/types/fetch-state.ts
rename to frontend/shared/types/fetch-state.ts
index e2cae63ae2c..dd80cdc9e00 100644
--- a/frontend/src/types/fetch-state.ts
+++ b/frontend/shared/types/fetch-state.ts
@@ -1,5 +1,5 @@
-import type { ErrorCode } from "~/constants/errors"
-import type { SupportedSearchType } from "~/constants/media"
+import type { ErrorCode } from "#shared/constants/errors"
+import type { SupportedSearchType } from "#shared/constants/media"
/**
* Describes the kind of API request that was made.
diff --git a/frontend/src/types/home-gallery.ts b/frontend/shared/types/home-gallery.ts
similarity index 100%
rename from frontend/src/types/home-gallery.ts
rename to frontend/shared/types/home-gallery.ts
diff --git a/frontend/src/types/item-group.ts b/frontend/shared/types/item-group.ts
similarity index 100%
rename from frontend/src/types/item-group.ts
rename to frontend/shared/types/item-group.ts
diff --git a/frontend/src/types/media-provider.ts b/frontend/shared/types/media-provider.ts
similarity index 100%
rename from frontend/src/types/media-provider.ts
rename to frontend/shared/types/media-provider.ts
diff --git a/frontend/src/types/media.ts b/frontend/shared/types/media.ts
similarity index 97%
rename from frontend/src/types/media.ts
rename to frontend/shared/types/media.ts
index 2a175b97c41..2ff5c1a84ce 100644
--- a/frontend/src/types/media.ts
+++ b/frontend/shared/types/media.ts
@@ -3,13 +3,13 @@
// The source code of this package is parsed to create the media properties
// documentation by the script `scripts/document-media.js`.
-import type { SupportedMediaType } from "~/constants/media"
-import type { License, LicenseVersion } from "~/constants/license"
+import type { SupportedMediaType } from "#shared/constants/media"
+import type { License, LicenseVersion } from "#shared/constants/license"
import {
SENSITIVITY_RESPONSE_PARAM,
Sensitivity,
-} from "~/constants/content-safety"
-import { AUDIO, IMAGE } from "~/constants/media"
+} from "#shared/constants/content-safety"
+import { AUDIO, IMAGE } from "#shared/constants/media"
export interface Tag {
name: string
diff --git a/frontend/src/types/modal.ts b/frontend/shared/types/modal.ts
similarity index 100%
rename from frontend/src/types/modal.ts
rename to frontend/shared/types/modal.ts
diff --git a/frontend/src/types/prop-extraction.ts b/frontend/shared/types/prop-extraction.ts
similarity index 100%
rename from frontend/src/types/prop-extraction.ts
rename to frontend/shared/types/prop-extraction.ts
diff --git a/frontend/src/types/provides.ts b/frontend/shared/types/provides.ts
similarity index 100%
rename from frontend/src/types/provides.ts
rename to frontend/shared/types/provides.ts
diff --git a/frontend/src/types/result.ts b/frontend/shared/types/result.ts
similarity index 73%
rename from frontend/src/types/result.ts
rename to frontend/shared/types/result.ts
index a209feabc21..ce86283375c 100644
--- a/frontend/src/types/result.ts
+++ b/frontend/shared/types/result.ts
@@ -1,5 +1,5 @@
-import { ALL_MEDIA, AUDIO, IMAGE } from "~/constants/media"
-import { AudioDetail, ImageDetail } from "~/types/media"
+import { ALL_MEDIA, AUDIO, IMAGE } from "#shared/constants/media"
+import type { AudioDetail, ImageDetail } from "#shared/types/media"
export type ResultKind = "search" | "related" | "collection"
export type ImageResults = {
diff --git a/frontend/src/types/search.ts b/frontend/shared/types/search.ts
similarity index 94%
rename from frontend/src/types/search.ts
rename to frontend/shared/types/search.ts
index a7d4c873904..c1d9a301d7c 100644
--- a/frontend/src/types/search.ts
+++ b/frontend/shared/types/search.ts
@@ -1,4 +1,4 @@
-import { INCLUDE_SENSITIVE_QUERY_PARAM } from "~/constants/content-safety"
+import { INCLUDE_SENSITIVE_QUERY_PARAM } from "#shared/constants/content-safety"
export type Collection = "tag" | "creator" | "source"
export type SearchStrategy = "default" | Collection
diff --git a/frontend/src/types/tabs.ts b/frontend/shared/types/tabs.ts
similarity index 100%
rename from frontend/src/types/tabs.ts
rename to frontend/shared/types/tabs.ts
diff --git a/frontend/src/types/utils.ts b/frontend/shared/types/utils.ts
similarity index 100%
rename from frontend/src/types/utils.ts
rename to frontend/shared/types/utils.ts
diff --git a/frontend/src/utils/attribution-html.ts b/frontend/shared/utils/attribution-html.ts
similarity index 98%
rename from frontend/src/utils/attribution-html.ts
rename to frontend/shared/utils/attribution-html.ts
index 394601935ca..2d47ad10db1 100644
--- a/frontend/src/utils/attribution-html.ts
+++ b/frontend/shared/utils/attribution-html.ts
@@ -5,13 +5,13 @@
*/
import enJson from "~~/i18n/locales/en.json"
-import type { Media } from "~/types/media"
+import type { LicenseElement } from "#shared/constants/license"
import {
getElements,
getFullLicenseName,
isPublicDomain,
-} from "~/utils/license"
-import type { LicenseElement } from "~/constants/license"
+} from "#shared/utils/license"
+import type { Media } from "#shared/types/media"
import type { Composer } from "vue-i18n"
diff --git a/frontend/src/utils/case.ts b/frontend/shared/utils/case.ts
similarity index 100%
rename from frontend/src/utils/case.ts
rename to frontend/shared/utils/case.ts
diff --git a/frontend/src/utils/clone.ts b/frontend/shared/utils/clone.ts
similarity index 100%
rename from frontend/src/utils/clone.ts
rename to frontend/shared/utils/clone.ts
diff --git a/frontend/src/utils/content-safety.ts b/frontend/shared/utils/content-safety.ts
similarity index 92%
rename from frontend/src/utils/content-safety.ts
rename to frontend/shared/utils/content-safety.ts
index 967d6201975..c0c9864f376 100644
--- a/frontend/src/utils/content-safety.ts
+++ b/frontend/shared/utils/content-safety.ts
@@ -2,14 +2,14 @@
* Contains utilities related to content safety.
*/
-import { hash, rand as prng } from "~/utils/prng"
-import { log } from "~/utils/console"
import {
USER_REPORTED,
PROVIDER_SUPPLIED,
TEXT_FILTERED,
Sensitivity,
-} from "~/constants/content-safety"
+} from "#shared/constants/content-safety"
+import { hash, rand as prng } from "#shared/utils/prng"
+import { log } from "~/utils/console"
/**
* Get an array of randomly selected sensitive content flags for an item with
diff --git a/frontend/src/utils/decode-data.ts b/frontend/shared/utils/decode-data.ts
similarity index 100%
rename from frontend/src/utils/decode-data.ts
rename to frontend/shared/utils/decode-data.ts
diff --git a/frontend/src/utils/deep-freeze.ts b/frontend/shared/utils/deep-freeze.ts
similarity index 100%
rename from frontend/src/utils/deep-freeze.ts
rename to frontend/shared/utils/deep-freeze.ts
diff --git a/frontend/src/utils/errors.ts b/frontend/shared/utils/errors.ts
similarity index 86%
rename from frontend/src/utils/errors.ts
rename to frontend/shared/utils/errors.ts
index 7ff9d8cee83..5f19d07c84b 100644
--- a/frontend/src/utils/errors.ts
+++ b/frontend/shared/utils/errors.ts
@@ -1,5 +1,5 @@
-import { clientSideErrorCodes, NO_RESULT } from "~/constants/errors"
-import type { FetchingError } from "~/types/fetch-state"
+import { clientSideErrorCodes, NO_RESULT } from "#shared/constants/errors"
+import type { FetchingError } from "#shared/types/fetch-state"
const NON_RETRYABLE_ERROR_CODES = [429, 500, 404] as const
const isNonRetryableErrorStatusCode = (statusCode: number | undefined) => {
diff --git a/frontend/src/utils/get-additional-sources.ts b/frontend/shared/utils/get-additional-sources.ts
similarity index 97%
rename from frontend/src/utils/get-additional-sources.ts
rename to frontend/shared/utils/get-additional-sources.ts
index dedb81a351e..25a730ec7ca 100644
--- a/frontend/src/utils/get-additional-sources.ts
+++ b/frontend/shared/utils/get-additional-sources.ts
@@ -1,7 +1,6 @@
-import type { MediaType } from "~/constants/media"
-import type { PaginatedSearchQuery } from "~/types/search"
-
-import { MODEL_3D } from "~/constants/media"
+import type { MediaType } from "#shared/constants/media"
+import { MODEL_3D } from "#shared/constants/media"
+import type { PaginatedSearchQuery } from "#shared/types/search"
/**
* Describes the query format used by the URL builder functions of additional
diff --git a/frontend/src/utils/license.ts b/frontend/shared/utils/license.ts
similarity index 97%
rename from frontend/src/utils/license.ts
rename to frontend/shared/utils/license.ts
index b67d3344d44..370c9544b02 100644
--- a/frontend/src/utils/license.ts
+++ b/frontend/shared/utils/license.ts
@@ -7,13 +7,13 @@ import type {
License,
LicenseVersion,
LicenseElement,
-} from "~/constants/license"
+} from "#shared/constants/license"
import {
CC_LICENSES,
DEPRECATED_CC_LICENSES,
PUBLIC_DOMAIN_MARKS,
-} from "~/constants/license"
-import { camelCase } from "~/utils/case"
+} from "#shared/constants/license"
+import { camelCase } from "#shared/utils/case"
import type { Composer } from "vue-i18n"
diff --git a/frontend/src/utils/load-script.ts b/frontend/shared/utils/load-script.ts
similarity index 100%
rename from frontend/src/utils/load-script.ts
rename to frontend/shared/utils/load-script.ts
diff --git a/frontend/src/utils/math.ts b/frontend/shared/utils/math.ts
similarity index 100%
rename from frontend/src/utils/math.ts
rename to frontend/shared/utils/math.ts
diff --git a/frontend/src/utils/og.ts b/frontend/shared/utils/og.ts
similarity index 100%
rename from frontend/src/utils/og.ts
rename to frontend/shared/utils/og.ts
diff --git a/frontend/src/utils/prng.ts b/frontend/shared/utils/prng.ts
similarity index 100%
rename from frontend/src/utils/prng.ts
rename to frontend/shared/utils/prng.ts
diff --git a/frontend/src/utils/query-utils.ts b/frontend/shared/utils/query-utils.ts
similarity index 90%
rename from frontend/src/utils/query-utils.ts
rename to frontend/shared/utils/query-utils.ts
index f22635dbe45..5e26059d4cc 100644
--- a/frontend/src/utils/query-utils.ts
+++ b/frontend/shared/utils/query-utils.ts
@@ -1,4 +1,4 @@
-import { SupportedMediaType } from "~/constants/media"
+import { SupportedMediaType } from "#shared/constants/media"
import type { LocationQueryValue } from "vue-router"
diff --git a/frontend/src/utils/resampling.ts b/frontend/shared/utils/resampling.ts
similarity index 100%
rename from frontend/src/utils/resampling.ts
rename to frontend/shared/utils/resampling.ts
diff --git a/frontend/src/utils/route-utils.ts b/frontend/shared/utils/route-utils.ts
similarity index 100%
rename from frontend/src/utils/route-utils.ts
rename to frontend/shared/utils/route-utils.ts
diff --git a/frontend/src/utils/search-query-transform.ts b/frontend/shared/utils/search-query-transform.ts
similarity index 96%
rename from frontend/src/utils/search-query-transform.ts
rename to frontend/shared/utils/search-query-transform.ts
index 8ce9fd8b650..11fe7a91512 100644
--- a/frontend/src/utils/search-query-transform.ts
+++ b/frontend/shared/utils/search-query-transform.ts
@@ -5,24 +5,22 @@ import {
FilterItem,
Filters,
mediaFilterKeys,
-} from "~/constants/filters"
+} from "#shared/constants/filters"
import {
ALL_MEDIA,
mediaTypes,
SearchType,
SupportedSearchType,
supportedSearchTypes,
-} from "~/constants/media"
-import { INCLUDE_SENSITIVE_QUERY_PARAM } from "~/constants/content-safety"
-import { deepClone } from "~/utils/clone"
-
-import {
+} from "#shared/constants/media"
+import { INCLUDE_SENSITIVE_QUERY_PARAM } from "#shared/constants/content-safety"
+import { deepClone } from "#shared/utils/clone"
+import { firstParam } from "#shared/utils/query-utils"
+import type {
SearchFilterKeys,
PaginatedSearchQuery,
SearchFilterQuery,
-} from "~/types/search"
-
-import { firstParam } from "~/utils/query-utils"
+} from "#shared/types/search"
import type { LocationQuery } from "vue-router"
diff --git a/frontend/src/utils/time-fmt.ts b/frontend/shared/utils/time-fmt.ts
similarity index 100%
rename from frontend/src/utils/time-fmt.ts
rename to frontend/shared/utils/time-fmt.ts
diff --git a/frontend/src/utils/translation-banner.ts b/frontend/shared/utils/translation-banner.ts
similarity index 100%
rename from frontend/src/utils/translation-banner.ts
rename to frontend/shared/utils/translation-banner.ts
diff --git a/frontend/src/app.vue b/frontend/src/app.vue
index c8d93f8d5a8..9d920ea8914 100644
--- a/frontend/src/app.vue
+++ b/frontend/src/app.vue
@@ -7,16 +7,13 @@ import {
useRuntimeConfig,
} from "#imports"
+import { meta as commonMeta } from "#shared/constants/meta"
+import { favicons } from "#shared/constants/favicons"
import { useUiStore } from "~/stores/ui"
import { useFeatureFlagStore } from "~/stores/feature-flag"
-
import { useLayout } from "~/composables/use-layout"
import { useDarkMode } from "~/composables/use-dark-mode"
-import { meta as commonMeta } from "~/constants/meta"
-
-import { favicons } from "~/constants/favicons"
-
import VSkipToContentButton from "~/components/VSkipToContentButton.vue"
const { updateBreakpoint } = useLayout()
diff --git a/frontend/src/components/VAudioThumbnail/VAudioThumbnail.vue b/frontend/src/components/VAudioThumbnail/VAudioThumbnail.vue
index aa2e686035d..279063757f9 100644
--- a/frontend/src/components/VAudioThumbnail/VAudioThumbnail.vue
+++ b/frontend/src/components/VAudioThumbnail/VAudioThumbnail.vue
@@ -3,12 +3,11 @@
* Displays the cover art for the audio in a square aspect ratio.
*/
import { useI18n } from "#imports"
-
import { toRefs, ref, onMounted } from "vue"
-import { rand, hash } from "~/utils/prng"
-import { lerp, dist, bezier, Point } from "~/utils/math"
-import type { AudioDetail } from "~/types/media"
+import { rand, hash } from "#shared/utils/prng"
+import { lerp, dist, bezier, Point } from "#shared/utils/math"
+import type { AudioDetail } from "#shared/types/media"
import { useSensitiveMedia } from "~/composables/use-sensitive-media"
const props = defineProps<{
diff --git a/frontend/src/components/VAudioThumbnail/meta/VAudioThumbnail.stories.ts b/frontend/src/components/VAudioThumbnail/meta/VAudioThumbnail.stories.ts
index 56f34af86ca..3bd21c05394 100644
--- a/frontend/src/components/VAudioThumbnail/meta/VAudioThumbnail.stories.ts
+++ b/frontend/src/components/VAudioThumbnail/meta/VAudioThumbnail.stories.ts
@@ -1,9 +1,9 @@
-import { Meta, type StoryObj } from "@storybook/vue3"
import { h } from "vue"
+import { Meta, type StoryObj } from "@storybook/vue3"
import { getAudioObj } from "~~/test/unit/fixtures/audio"
-import { AudioDetail } from "~/types/media"
+import type { AudioDetail } from "#shared/types/media"
import VAudioThumbnail from "~/components/VAudioThumbnail/VAudioThumbnail.vue"
diff --git a/frontend/src/components/VAudioTrack/VAudioControl.vue b/frontend/src/components/VAudioTrack/VAudioControl.vue
index c89fca4fdb9..cf066fcb5e8 100644
--- a/frontend/src/components/VAudioTrack/VAudioControl.vue
+++ b/frontend/src/components/VAudioTrack/VAudioControl.vue
@@ -3,11 +3,15 @@
* Displays the control for switching between the playing and paused states of
* a media file.
*/
-import { computed } from "vue"
-import { AudioLayout, AudioStatus, statusVerbMap } from "~/constants/audio"
-import type { ButtonConnections } from "~/types/button"
+import { computed } from "vue"
+import {
+ AudioLayout,
+ AudioStatus,
+ statusVerbMap,
+} from "#shared/constants/audio"
+import type { ButtonConnections } from "#shared/types/button"
import { useHydrating } from "~/composables/use-hydrating"
import VIconButton from "~/components/VIconButton/VIconButton.vue"
diff --git a/frontend/src/components/VAudioTrack/VAudioTrack.vue b/frontend/src/components/VAudioTrack/VAudioTrack.vue
index ff93e3f21e3..4a35904356a 100644
--- a/frontend/src/components/VAudioTrack/VAudioTrack.vue
+++ b/frontend/src/components/VAudioTrack/VAudioTrack.vue
@@ -3,10 +3,25 @@
* Displays the waveform and basic information about the track, along with
* controls to play, pause or seek to a point on the track.
*/
-import { firstParam, useI18n, useRoute } from "#imports"
-
+import { useI18n, useRoute } from "#imports"
import { computed, onUnmounted, ref, watch } from "vue"
+import { AUDIO } from "#shared/constants/media"
+import {
+ activeAudioStatus,
+ AudioLayout,
+ AudioSize,
+ AudioStatus,
+} from "#shared/constants/audio"
+import { firstParam } from "#shared/utils/query-utils"
+import type {
+ AudioInteraction,
+ AudioInteractionData,
+} from "#shared/types/analytics"
+import type { AudioDetail } from "#shared/types/media"
+import type { AudioTrackClickEvent } from "#shared/types/events"
+import { useActiveMediaStore } from "~/stores/active-media"
+import { useMediaStore } from "~/stores/media"
import { useActiveAudio } from "~/composables/use-active-audio"
import { defaultRef } from "~/composables/default-ref"
import { useSeekable } from "~/composables/use-seekable"
@@ -16,21 +31,6 @@ import {
useMatchSingleResultRoutes,
} from "~/composables/use-match-routes"
-import { useActiveMediaStore } from "~/stores/active-media"
-import { useMediaStore } from "~/stores/media"
-
-import { AUDIO } from "~/constants/media"
-import {
- activeAudioStatus,
- AudioLayout,
- AudioSize,
- AudioStatus,
-} from "~/constants/audio"
-
-import type { AudioInteraction, AudioInteractionData } from "~/types/analytics"
-import type { AudioDetail } from "~/types/media"
-import type { AudioTrackClickEvent } from "~/types/events"
-
import VAudioControl from "~/components/VAudioTrack/VAudioControl.vue"
import VWaveform from "~/components/VAudioTrack/VWaveform.vue"
import VFullLayout from "~/components/VAudioTrack/layouts/VFullLayout.vue"
diff --git a/frontend/src/components/VAudioTrack/VGlobalAudioTrack.vue b/frontend/src/components/VAudioTrack/VGlobalAudioTrack.vue
index 7fc991b0f11..5feacc54998 100644
--- a/frontend/src/components/VAudioTrack/VGlobalAudioTrack.vue
+++ b/frontend/src/components/VAudioTrack/VGlobalAudioTrack.vue
@@ -4,18 +4,15 @@
* controls to play, pause or seek to a point on the track.
*/
import { useI18n, useNuxtApp } from "#imports"
-
import { computed, ref, watch } from "vue"
-import { useActiveAudio } from "~/composables/use-active-audio"
-import { defaultRef } from "~/composables/default-ref"
-
+import type { AudioStatus } from "#shared/constants/audio"
+import type { AudioInteraction } from "#shared/types/analytics"
+import type { AudioDetail } from "#shared/types/media"
import { useActiveMediaStore } from "~/stores/active-media"
import { useMediaStore } from "~/stores/media"
-
-import type { AudioInteraction } from "~/types/analytics"
-import type { AudioDetail } from "~/types/media"
-import type { AudioStatus } from "~/constants/audio"
+import { useActiveAudio } from "~/composables/use-active-audio"
+import { defaultRef } from "~/composables/default-ref"
import VAudioControl from "~/components/VAudioTrack/VAudioControl.vue"
import VWaveform from "~/components/VAudioTrack/VWaveform.vue"
diff --git a/frontend/src/components/VAudioTrack/VWaveform.vue b/frontend/src/components/VAudioTrack/VWaveform.vue
index 62dbdce5f18..183f65998f5 100644
--- a/frontend/src/components/VAudioTrack/VWaveform.vue
+++ b/frontend/src/components/VAudioTrack/VWaveform.vue
@@ -5,16 +5,13 @@
*/
import { computed, ref, toRef } from "vue"
-import { downsampleArray, upsampleArray } from "~/utils/resampling"
-import { timeFmt } from "~/utils/time-fmt"
+import type { AudioFeature } from "#shared/constants/audio"
+import { downsampleArray, upsampleArray } from "#shared/utils/resampling"
+import { timeFmt } from "#shared/utils/time-fmt"
+import { hash, rand as prng } from "#shared/utils/prng"
import { useSeekable } from "~/composables/use-seekable"
-
-import type { AudioFeature } from "~/constants/audio"
-
import useResizeObserver from "~/composables/use-resize-observer"
-import { hash, rand as prng } from "~/utils/prng"
-
/**
* If the duration is above this threshold, the progress timestamp will show ms.
*/
diff --git a/frontend/src/components/VAudioTrack/layouts/VBoxLayout.vue b/frontend/src/components/VAudioTrack/layouts/VBoxLayout.vue
index 1d867fa81ab..f1c2279a1b0 100644
--- a/frontend/src/components/VAudioTrack/layouts/VBoxLayout.vue
+++ b/frontend/src/components/VAudioTrack/layouts/VBoxLayout.vue
@@ -1,10 +1,9 @@
diff --git a/frontend/src/components/VContentReport/VContentReportForm.vue b/frontend/src/components/VContentReport/VContentReportForm.vue
index 5e9bc2999d0..d70641440c6 100644
--- a/frontend/src/components/VContentReport/VContentReportForm.vue
+++ b/frontend/src/components/VContentReport/VContentReportForm.vue
@@ -1,6 +1,5 @@
diff --git a/frontend/src/components/VSourcesTable.vue b/frontend/src/components/VSourcesTable.vue
index 72949df0951..ad5ba7eb556 100644
--- a/frontend/src/components/VSourcesTable.vue
+++ b/frontend/src/components/VSourcesTable.vue
@@ -1,13 +1,12 @@