diff --git a/apps/app-frontend/src/components/ui/skin/EditSkinModal.vue b/apps/app-frontend/src/components/ui/skin/EditSkinModal.vue
index de0ae7132..06b45fd1d 100644
--- a/apps/app-frontend/src/components/ui/skin/EditSkinModal.vue
+++ b/apps/app-frontend/src/components/ui/skin/EditSkinModal.vue
@@ -118,6 +118,7 @@ import {
type Cape,
type SkinModel,
get_normalized_skin_texture,
+ determineModelType,
} from '@/helpers/skins.ts'
import { handleError } from '@/store/notifications'
import {
@@ -253,7 +254,7 @@ async function showNew(e: MouseEvent, skinTextureUrl: string) {
mode.value = 'new'
currentSkin.value = null
uploadedTextureUrl.value = skinTextureUrl
- variant.value = 'CLASSIC'
+ variant.value = await determineModelType(skinTextureUrl)
selectedCape.value = undefined
visibleCapeList.value = []
initVisibleCapeList()
diff --git a/apps/app-frontend/src/components/ui/world/WorldItem.vue b/apps/app-frontend/src/components/ui/world/WorldItem.vue
index f30aca810..c8c1893f7 100644
--- a/apps/app-frontend/src/components/ui/world/WorldItem.vue
+++ b/apps/app-frontend/src/components/ui/world/WorldItem.vue
@@ -128,6 +128,14 @@ const messages = defineMessages({
id: 'instance.worlds.game_already_open',
defaultMessage: 'Instance is already open',
},
+ noContact: {
+ id: 'instance.worlds.no_contact',
+ defaultMessage: "Server couldn't be contacted",
+ },
+ incompatibleServer: {
+ id: 'instance.worlds.incompatible_server',
+ defaultMessage: 'Server is incompatible',
+ },
copyAddress: {
id: 'instance.worlds.copy_address',
defaultMessage: 'Copy address',
@@ -302,39 +310,33 @@ const messages = defineMessages({
-
-
-
-
-
-
+
+
-
-
- {{ formatMessage(commonMessages.playButton) }}
-
-
-
-
-
-
+ "
+ :disabled="!supportsQuickPlay || playingOtherWorld || startingInstance"
+ @click="emit('play')"
+ >
+
+
{{ formatMessage(commonMessages.playButton) }}
diff --git a/apps/app-frontend/src/helpers/skins.ts b/apps/app-frontend/src/helpers/skins.ts
index 28a29ba1a..9b5953f53 100644
--- a/apps/app-frontend/src/helpers/skins.ts
+++ b/apps/app-frontend/src/helpers/skins.ts
@@ -62,16 +62,14 @@ export async function determineModelType(texture: string): Promise<'SLIM' | 'CLA
context.drawImage(image, 0, 0)
- const armX = 44
- const armY = 16
- const armWidth = 4
+ const armX = 54
+ const armY = 20
+ const armWidth = 2
const armHeight = 12
-
const imageData = context.getImageData(armX, armY, armWidth, armHeight).data
-
- for (let y = 0; y < armHeight; y++) {
- const alphaIndex = (3 + y * armWidth) * 4 + 3
- if (imageData[alphaIndex] !== 0) {
+ for (let index = 1; index <= imageData.length; index++) {
+ //every fourth value in RGBA is the alpha channel
+ if (index % 4 == 0 && imageData[index - 1] !== 0) {
resolve('CLASSIC')
return
}
diff --git a/apps/app-frontend/src/locales/en-US/index.json b/apps/app-frontend/src/locales/en-US/index.json
index fa2563da9..b2410efc5 100644
--- a/apps/app-frontend/src/locales/en-US/index.json
+++ b/apps/app-frontend/src/locales/en-US/index.json
@@ -377,6 +377,12 @@
"instance.worlds.hardcore": {
"message": "Hardcore mode"
},
+ "instance.worlds.incompatible_server": {
+ "message": "Server is incompatible"
+ },
+ "instance.worlds.no_contact": {
+ "message": "Server couldn't be contacted"
+ },
"instance.worlds.no_quick_play": {
"message": "You can only jump straight into worlds on Minecraft 1.20+"
},
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index 21cb1752e..7cded8314 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -38,10 +38,10 @@
"@intercom/messenger-js-sdk": "^0.0.14",
"@ltd/j-toml": "^1.38.0",
"@modrinth/assets": "workspace:*",
- "@modrinth/ui": "workspace:*",
- "@modrinth/utils": "workspace:*",
"@modrinth/blog": "workspace:*",
"@modrinth/moderation": "workspace:*",
+ "@modrinth/ui": "workspace:*",
+ "@modrinth/utils": "workspace:*",
"@pinia/nuxt": "^0.5.1",
"@types/three": "^0.172.0",
"@vintl/vintl": "^4.4.1",
@@ -59,6 +59,7 @@
"markdown-it": "14.1.0",
"pathe": "^1.1.2",
"pinia": "^2.1.7",
+ "prettier": "^3.6.2",
"qrcode.vue": "^3.4.0",
"semver": "^7.5.4",
"three": "^0.172.0",
diff --git a/apps/frontend/src/assets/styles/components.scss b/apps/frontend/src/assets/styles/components.scss
index a1ea3a1e9..f647e7e25 100644
--- a/apps/frontend/src/assets/styles/components.scss
+++ b/apps/frontend/src/assets/styles/components.scss
@@ -197,13 +197,13 @@
}
> :where(
- input + *,
- .input-group + *,
- .textarea-wrapper + *,
- .chips + *,
- .resizable-textarea-wrapper + *,
- .input-div + *
- ) {
+ input + *,
+ .input-group + *,
+ .textarea-wrapper + *,
+ .chips + *,
+ .resizable-textarea-wrapper + *,
+ .input-div + *
+ ) {
&:not(:empty) {
margin-block-start: var(--spacing-card-md);
}
diff --git a/apps/frontend/src/assets/styles/global.scss b/apps/frontend/src/assets/styles/global.scss
index b52b738ed..cefb9460c 100644
--- a/apps/frontend/src/assets/styles/global.scss
+++ b/apps/frontend/src/assets/styles/global.scss
@@ -115,10 +115,12 @@ html {
--shadow-inset-sm: inset 0px -1px 2px hsla(221, 39%, 11%, 0.15);
--shadow-raised-lg: 0px 2px 4px hsla(221, 39%, 11%, 0.2);
- --shadow-raised: 0.3px 0.5px 0.6px hsl(var(--shadow-color) / 0.15),
+ --shadow-raised:
+ 0.3px 0.5px 0.6px hsl(var(--shadow-color) / 0.15),
1px 2px 2.2px -1.7px hsl(var(--shadow-color) / 0.12),
4.4px 8.8px 9.7px -3.4px hsl(var(--shadow-color) / 0.09);
- --shadow-floating: hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
+ --shadow-floating:
+ hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
hsla(0, 0%, 0%, 0.1) 0px 4px 6px -1px, hsla(0, 0%, 0%, 0.1) 0px 2px 4px -1px;
--shadow-card: rgba(50, 50, 100, 0.1) 0px 2px 4px 0px;
@@ -150,8 +152,8 @@ html {
rgba(255, 255, 255, 0.35) 0%,
rgba(255, 255, 255, 0.2695) 100%
);
- --landing-blob-shadow: 2px 2px 12px rgba(0, 0, 0, 0.16),
- inset 2px 2px 64px rgba(255, 255, 255, 0.45);
+ --landing-blob-shadow:
+ 2px 2px 12px rgba(0, 0, 0, 0.16), inset 2px 2px 64px rgba(255, 255, 255, 0.45);
--landing-card-bg: rgba(255, 255, 255, 0.8);
--landing-card-shadow: 2px 2px 12px rgba(0, 0, 0, 0.16);
@@ -251,13 +253,15 @@ html {
--shadow-raised-lg: 0px 2px 4px hsla(221, 39%, 11%, 0.2);
--shadow-raised: 0px -2px 4px hsla(221, 39%, 11%, 0.1);
- --shadow-floating: hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
+ --shadow-floating:
+ hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
hsla(0, 0%, 0%, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
--shadow-card: rgba(0, 0, 0, 0.25) 0px 2px 4px 0px;
--landing-maze-bg: url("https://cdn.modrinth.com/landing-new/landing.webp");
- --landing-maze-gradient-bg: linear-gradient(0deg, #31375f 0%, rgba(8, 14, 55, 0) 100%),
+ --landing-maze-gradient-bg:
+ linear-gradient(0deg, #31375f 0%, rgba(8, 14, 55, 0) 100%),
url("https://cdn.modrinth.com/landing-new/landing-lower.webp");
--landing-maze-outer-bg: linear-gradient(180deg, #06060d 0%, #000000 100%);
@@ -284,7 +288,8 @@ html {
rgba(44, 48, 79, 0.35) 0%,
rgba(32, 35, 50, 0.2695) 100%
);
- --landing-blob-shadow: 2px 2px 12px rgba(0, 0, 0, 0.16), inset 2px 2px 64px rgba(57, 61, 94, 0.45);
+ --landing-blob-shadow:
+ 2px 2px 12px rgba(0, 0, 0, 0.16), inset 2px 2px 64px rgba(57, 61, 94, 0.45);
--landing-card-bg: rgba(59, 63, 85, 0.15);
--landing-card-shadow: 2px 2px 12px rgba(0, 0, 0, 0.16);
@@ -360,8 +365,9 @@ body {
// Defaults
background-color: var(--color-bg);
color: var(--color-text);
- --font-standard: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto,
- Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
+ --font-standard:
+ Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto, Cantarell,
+ Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
--mono-font: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
font-family: var(--font-standard);
font-size: 16px;
diff --git a/apps/frontend/src/components/ui/moderation/ModpackPermissionsFlow.vue b/apps/frontend/src/components/ui/moderation/ModpackPermissionsFlow.vue
index ff5db66ac..124a49336 100644
--- a/apps/frontend/src/components/ui/moderation/ModpackPermissionsFlow.vue
+++ b/apps/frontend/src/components/ui/moderation/ModpackPermissionsFlow.vue
@@ -365,26 +365,24 @@ function getJudgements(): ModerationJudgements {
const judgements: ModerationJudgements = {};
modPackData.value.forEach((item) => {
- if (item.status && item.status !== "unidentified") {
- if (item.type === "flame") {
- judgements[item.sha1] = {
- type: "flame",
- id: item.id,
- status: item.status,
- link: item.url,
- title: item.title,
- file_name: item.file_name,
- };
- } else if (item.type === "unknown") {
- judgements[item.sha1] = {
- type: "unknown",
- status: item.status,
- proof: item.proof,
- link: item.url,
- title: item.title,
- file_name: item.file_name,
- };
- }
+ if (item.type === "flame") {
+ judgements[item.sha1] = {
+ type: "flame",
+ id: item.id,
+ status: item.status,
+ link: item.url,
+ title: item.title,
+ file_name: item.file_name,
+ };
+ } else if (item.type === "unknown") {
+ judgements[item.sha1] = {
+ type: "unknown",
+ status: item.status,
+ proof: item.proof,
+ link: item.url,
+ title: item.title,
+ file_name: item.file_name,
+ };
}
});
diff --git a/apps/frontend/src/components/ui/moderation/NewModerationChecklist.vue b/apps/frontend/src/components/ui/moderation/NewModerationChecklist.vue
index 955b87dd0..2126091ad 100644
--- a/apps/frontend/src/components/ui/moderation/NewModerationChecklist.vue
+++ b/apps/frontend/src/components/ui/moderation/NewModerationChecklist.vue
@@ -116,8 +116,10 @@
{{ action.label }}
{{ action.label }}
>();
@@ -461,7 +464,7 @@ const stageTextExpanded = computedAsync(async () => {
const stage = checklist[stageIndex];
if (stage.text) {
return renderHighlightedString(
- expandVariables(await stage.text(), props.project, variables.value),
+ expandVariables(await stage.text(props.project), props.project, variables.value),
);
}
return null;
@@ -559,14 +562,20 @@ function handleKeybinds(event: KeyboardEvent) {
},
trySelectDropdownOption: (actionIndex: number, optionIndex: number) => {
const action = visibleActions.value[actionIndex] as DropdownAction;
- if (action && action.type === "dropdown" && action.options[optionIndex]) {
- selectDropdownOption(action, action.options[optionIndex]);
+ if (action && action.type === "dropdown") {
+ const visibleOptions = getVisibleDropdownOptions(action);
+ if (optionIndex < visibleOptions.length) {
+ selectDropdownOption(action, visibleOptions[optionIndex]);
+ }
}
},
tryToggleChip: (actionIndex: number, chipIndex: number) => {
const action = visibleActions.value[actionIndex] as MultiSelectChipsAction;
if (action && action.type === "multi-select-chips") {
- toggleChip(action, chipIndex);
+ const visibleOptions = getVisibleMultiSelectOptions(action);
+ if (chipIndex < visibleOptions.length) {
+ toggleChip(action, chipIndex);
+ }
}
},
@@ -733,13 +742,17 @@ const multiSelectActions = computed(() =>
function getDropdownValue(action: DropdownAction) {
const actionId = getActionId(action);
+ const visibleOptions = getVisibleDropdownOptions(action);
const currentValue = actionStates.value[actionId]?.value ?? action.defaultOption ?? 0;
- if (action.options && action.options[currentValue]) {
- return action.options[currentValue];
+ const allOptions = action.options;
+ const storedOption = allOptions[currentValue];
+
+ if (storedOption && visibleOptions.includes(storedOption)) {
+ return storedOption;
}
- return action.options?.[0] || null;
+ return visibleOptions[0] || null;
}
function isActionSelected(action: Action): boolean {
@@ -775,20 +788,31 @@ function selectDropdownOption(action: DropdownAction, selected: any) {
function isChipSelected(action: MultiSelectChipsAction, optionIndex: number): boolean {
const actionId = getActionId(action);
const selectedSet = actionStates.value[actionId]?.value as Set | undefined;
- return selectedSet?.has(optionIndex) || false;
+
+ const visibleOptions = getVisibleMultiSelectOptions(action);
+ const visibleOption = visibleOptions[optionIndex];
+ const originalIndex = action.options.findIndex((opt) => opt === visibleOption);
+
+ return selectedSet?.has(originalIndex) || false;
}
function toggleChip(action: MultiSelectChipsAction, optionIndex: number) {
const actionId = getActionId(action);
const state = actionStates.value[actionId];
if (state && state.value instanceof Set) {
- if (state.value.has(optionIndex)) {
- state.value.delete(optionIndex);
- } else {
- state.value.add(optionIndex);
+ const visibleOptions = getVisibleMultiSelectOptions(action);
+ const visibleOption = visibleOptions[optionIndex];
+ const originalIndex = action.options.findIndex((opt) => opt === visibleOption);
+
+ if (originalIndex !== -1) {
+ if (state.value.has(originalIndex)) {
+ state.value.delete(originalIndex);
+ } else {
+ state.value.add(originalIndex);
+ }
+ state.selected = state.value.size > 0;
+ persistState();
}
- state.selected = state.value.size > 0;
- persistState();
}
}
@@ -869,9 +893,23 @@ async function processAction(
stageIndex: number,
messageParts: MessagePart[],
) {
+ const allValidActionIds: string[] = [];
+ checklist.forEach((stage, stageIdx) => {
+ stage.actions.forEach((stageAction, actionIdx) => {
+ allValidActionIds.push(getActionIdForStage(stageAction, stageIdx, actionIdx));
+ if (stageAction.enablesActions) {
+ stageAction.enablesActions.forEach((enabledAction, enabledIdx) => {
+ allValidActionIds.push(
+ getActionIdForStage(enabledAction, stageIdx, actionIdx, enabledIdx),
+ );
+ });
+ }
+ });
+ });
+
if (action.type === "button" || action.type === "toggle") {
const buttonAction = action as ButtonAction | ToggleAction;
- const message = await getActionMessage(buttonAction, selectedActionIds);
+ const message = await getActionMessage(buttonAction, selectedActionIds, allValidActionIds);
if (message) {
messageParts.push({
weight: buttonAction.weight,
@@ -885,16 +923,29 @@ async function processAction(
const matchingVariant = findMatchingVariant(
conditionalAction.messageVariants,
selectedActionIds,
+ allValidActionIds,
+ stageIndex,
);
+
+ let message: string;
+ let weight: number;
+
if (matchingVariant) {
- const message = (await matchingVariant.message()) as string;
- messageParts.push({
- weight: matchingVariant.weight,
- content: processMessage(message, action, stageIndex, textInputValues.value),
- actionId,
- stageIndex,
- });
+ message = (await matchingVariant.message()) as string;
+ weight = matchingVariant.weight;
+ } else if (conditionalAction.fallbackMessage) {
+ message = (await conditionalAction.fallbackMessage()) as string;
+ weight = conditionalAction.fallbackWeight ?? 0;
+ } else {
+ return;
}
+
+ messageParts.push({
+ weight,
+ content: processMessage(message, action, stageIndex, textInputValues.value),
+ actionId,
+ stageIndex,
+ });
} else if (action.type === "dropdown") {
const dropdownAction = action as DropdownAction;
const selectedIndex = state.value ?? 0;
@@ -929,6 +980,18 @@ async function processAction(
}
function shouldShowStage(stage: Stage): boolean {
+ let hasVisibleActions = false;
+
+ for (const a of stage.actions) {
+ if (shouldShowAction(a)) {
+ hasVisibleActions = true;
+ }
+ }
+
+ if (!hasVisibleActions) {
+ return false;
+ }
+
if (typeof stage.shouldShow === "function") {
return stage.shouldShow(props.project);
}
@@ -944,6 +1007,24 @@ function shouldShowAction(action: Action): boolean {
return true;
}
+function getVisibleDropdownOptions(action: DropdownAction) {
+ return action.options.filter((option) => {
+ if (typeof option.shouldShow === "function") {
+ return option.shouldShow(props.project);
+ }
+ return true;
+ });
+}
+
+function getVisibleMultiSelectOptions(action: MultiSelectChipsAction) {
+ return action.options.filter((option) => {
+ if (typeof option.shouldShow === "function") {
+ return option.shouldShow(props.project);
+ }
+ return true;
+ });
+}
+
function shouldShowStageIndex(stageIndex: number): boolean {
return shouldShowStage(checklist[stageIndex]);
}
@@ -1021,7 +1102,20 @@ async function generateMessage() {
}
}
- message.value = fullMessage;
+ try {
+ const formattedMessage = await prettier.format(fullMessage, {
+ parser: "markdown",
+ printWidth: 80,
+ proseWrap: "always",
+ tabWidth: 2,
+ useTabs: false,
+ });
+ message.value = formattedMessage;
+ } catch (formattingError) {
+ console.warn("Failed to format markdown, using original:", formattingError);
+ message.value = fullMessage;
+ }
+
generatedMessage.value = true;
} catch (error) {
console.error("Error generating message:", error);
diff --git a/apps/frontend/src/components/ui/report/ReportInfo.vue b/apps/frontend/src/components/ui/report/ReportInfo.vue
index 5e2b924c1..74bdfd0d8 100644
--- a/apps/frontend/src/components/ui/report/ReportInfo.vue
+++ b/apps/frontend/src/components/ui/report/ReportInfo.vue
@@ -172,6 +172,7 @@ const flags = useFeatureFlags();
.markdown-body {
grid-area: body;
+ max-width: 100%;
}
.reporter-info {
diff --git a/apps/frontend/src/components/ui/servers/ContentVersionEditModal.vue b/apps/frontend/src/components/ui/servers/ContentVersionEditModal.vue
index b142bb814..d204e7aee 100644
--- a/apps/frontend/src/components/ui/servers/ContentVersionEditModal.vue
+++ b/apps/frontend/src/components/ui/servers/ContentVersionEditModal.vue
@@ -31,9 +31,9 @@
class="flex cursor-pointer items-center gap-1 bg-transparent p-0"
@click="
versionFilter &&
- (unlockFilterAccordion.isOpen
- ? unlockFilterAccordion.close()
- : unlockFilterAccordion.open())
+ (unlockFilterAccordion.isOpen
+ ? unlockFilterAccordion.close()
+ : unlockFilterAccordion.open())
"
>
{
- if (!this.general?.datacenter) {
- console.warn("No datacenter info available for ping test");
+ if (!this.general?.node?.instance) {
+ console.warn("No node instance available for ping test");
return false;
}
- const datacenter = this.general.datacenter;
- const wsUrl = `wss://${datacenter}.nodes.modrinth.com/pingtest`;
+ const wsUrl = `wss://${this.general.node.instance}/pingtest`;
try {
return await new Promise((resolve) => {
diff --git a/apps/frontend/src/composables/servers/servers-fetch.ts b/apps/frontend/src/composables/servers/servers-fetch.ts
index 5b5d925b1..45bd48c68 100644
--- a/apps/frontend/src/composables/servers/servers-fetch.ts
+++ b/apps/frontend/src/composables/servers/servers-fetch.ts
@@ -112,7 +112,8 @@ export async function useServersFetch(
const response = await $fetch(fullUrl, {
method,
headers,
- body: body && contentType === "application/json" ? JSON.stringify(body) : body ?? undefined,
+ body:
+ body && contentType === "application/json" ? JSON.stringify(body) : (body ?? undefined),
timeout: 10000,
});
diff --git a/apps/frontend/src/layouts/default.vue b/apps/frontend/src/layouts/default.vue
index 643765456..842eaf376 100644
--- a/apps/frontend/src/layouts/default.vue
+++ b/apps/frontend/src/layouts/default.vue
@@ -1346,6 +1346,15 @@ const footerLinks = [
}),
),
},
+ {
+ href: "/legal/copyright",
+ label: formatMessage(
+ defineMessage({
+ id: "layout.footer.legal.copyright-policy",
+ defaultMessage: "Copyright Policy and DMCA",
+ }),
+ ),
+ },
],
},
];
diff --git a/apps/frontend/src/locales/en-US/index.json b/apps/frontend/src/locales/en-US/index.json
index dff91ec44..468227e53 100644
--- a/apps/frontend/src/locales/en-US/index.json
+++ b/apps/frontend/src/locales/en-US/index.json
@@ -404,6 +404,9 @@
"layout.footer.legal-disclaimer": {
"message": "NOT AN OFFICIAL MINECRAFT SERVICE. NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT."
},
+ "layout.footer.legal.copyright-policy": {
+ "message": "Copyright Policy and DMCA"
+ },
"layout.footer.legal.privacy-policy": {
"message": "Privacy Policy"
},
diff --git a/apps/frontend/src/pages/[type]/[id].vue b/apps/frontend/src/pages/[type]/[id].vue
index 0b51f73bb..7167610ee 100644
--- a/apps/frontend/src/pages/[type]/[id].vue
+++ b/apps/frontend/src/pages/[type]/[id].vue
@@ -29,12 +29,11 @@
class="settings-header__icon"
/>
+
Project settings
+
+
@@ -174,9 +175,11 @@
+
+
@@ -219,8 +222,7 @@
:href="`modrinth://mod/${project.slug}`"
@click="() => installWithApp()"
>
-
- Install with Modrinth App
+
Install with Modrinth App
@@ -240,6 +242,7 @@
+
@@ -327,8 +330,7 @@
}
"
>
- {{ gameVersion }}
-
+ {{ gameVersion }}
@@ -419,7 +421,6 @@
-
downloadModal.show(event)">
-
- Download
+ Download
+
+
Modrinth Servers is the easiest way to play with your friends without hassle!
+
Starting at $5 / month
@@ -625,6 +628,7 @@
{{ option.name }}
+
@@ -632,8 +636,7 @@
class="btn collection-button"
@click="(event) => $refs.modal_collection.show(event)"
>
-
- Create new collection
+ Create new collection
@@ -716,25 +719,14 @@
:dropdown-id="`${baseId}-more-options`"
>
-
-
- Analytics
-
+ Analytics
-
- Review project
-
-
-
- Report
-
-
-
- Copy ID
+ Review project
+ Report
+ Copy ID
-
- Copy permanent link
+ Copy permanent link
@@ -760,6 +752,7 @@
updates unless the author decides to unarchive the project.
+
+
+