diff --git a/packages/api-client/src/modules/archon/servers/v0.ts b/packages/api-client/src/modules/archon/servers/v0.ts index 8a85532e21..7cf2c3418b 100644 --- a/packages/api-client/src/modules/archon/servers/v0.ts +++ b/packages/api-client/src/modules/archon/servers/v0.ts @@ -103,7 +103,7 @@ export class ArchonServersV0Module extends AbstractModule { */ public async power( serverId: string, - action: 'Start' | 'Stop' | 'Restart' | 'Kill', + action: Archon.Servers.v0.PowerAction, ): Promise { await this.client.request(`/servers/${serverId}/power`, { api: 'archon', diff --git a/packages/api-client/src/modules/archon/types.ts b/packages/api-client/src/modules/archon/types.ts index 7add0d5a84..f1e53aa9e4 100644 --- a/packages/api-client/src/modules/archon/types.ts +++ b/packages/api-client/src/modules/archon/types.ts @@ -681,6 +681,8 @@ export namespace Archon { token: string // JWT token for filesystem access } + export type PowerAction = 'Start' | 'Stop' | 'Restart' | 'Kill' + export type ReinstallLoaderRequest = { loader: string loader_version?: string diff --git a/packages/ui/src/components/servers/server-header/ServerManageHeader.vue b/packages/ui/src/components/servers/server-header/ServerManageHeader.vue index 447bbf965f..430d7b8c78 100644 --- a/packages/ui/src/components/servers/server-header/ServerManageHeader.vue +++ b/packages/ui/src/components/servers/server-header/ServerManageHeader.vue @@ -320,6 +320,22 @@ const startSplitActions = computed(() => [ })), ]) +const restartSplitActions = computed(() => [ + { + id: 'restart', + label: primaryActionText.value, + icon: UpdatedIcon, + action: () => initiateAction('Restart'), + }, + // TODO: Implement world scoping when Archon/Kyros support target worlds in power requests. + ...startableWorlds.value.map((world) => ({ + id: `restart-${world.id}`, + label: `Restart with ${world.name}`, + icon: GlobeIcon, + action: () => initiateAction('Restart'), + })), +]) + const stopSplitActions = computed(() => [ { id: 'stop', @@ -353,11 +369,10 @@ const powerActions = computed(() => { { id: 'restart', label: primaryActionText.value, - icon: UpdatedIcon, color: 'orange', - tooltip: busyTooltip.value, - disabled: !canTakeAction.value, - onClick: handlePrimaryAction, + joinedActions: restartSplitActions.value, + primaryDisabled: !canTakeAction.value, + dropdownDisabled: !canTakeAction.value, }, { id: 'stop', diff --git a/packages/ui/src/components/servers/server-header/use-server-power-action.ts b/packages/ui/src/components/servers/server-header/use-server-power-action.ts index 2c0743ce0f..05fdfb5073 100644 --- a/packages/ui/src/components/servers/server-header/use-server-power-action.ts +++ b/packages/ui/src/components/servers/server-header/use-server-power-action.ts @@ -1,3 +1,4 @@ +import type { Archon } from '@modrinth/api-client' import { computed, type Ref } from 'vue' import { useVIntl } from '#ui/composables/i18n' @@ -8,7 +9,7 @@ import { injectNotificationManager, } from '#ui/providers' -export type PowerAction = 'Start' | 'Stop' | 'Restart' | 'Kill' +export type PowerAction = Archon.Servers.v0.PowerAction export function useServerPowerAction(options?: { disabled?: Ref }) { const { formatMessage } = useVIntl() diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/instances/[instance-id]/index.vue b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/instances/[instance-id]/index.vue index c4fdc437fb..f4c79dbc9d 100644 --- a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/instances/[instance-id]/index.vue +++ b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/instances/[instance-id]/index.vue @@ -24,9 +24,10 @@ import { BoxesIcon, DatabaseBackupIcon, FolderOpenIcon, + GlobeIcon, LoaderCircleIcon, PlayIcon, - SettingsIcon, + Settings2Icon, SlashIcon, StopCircleIcon, UpdatedIcon, @@ -35,6 +36,7 @@ import { useQuery } from '@tanstack/vue-query' import { computed, watch } from 'vue' import { useRouter } from 'vue-router' +import type { JoinedButtonAction } from '#ui/components/base/JoinedButtons.vue' import NavTabs from '#ui/components/base/NavTabs.vue' import { WorldManageHeader } from '#ui/components/servers/server-header' import { useServerPowerAction } from '#ui/components/servers/server-header/use-server-power-action' @@ -167,6 +169,24 @@ const stopSplitActions = computed(() => [ action: () => initiateAction('Kill'), }, ]) +const restartableWorlds = computed(() => + (serverFull.value?.worlds ?? []).filter((world) => world.id !== worldId.value), +) +const restartSplitActions = computed(() => [ + { + id: 'restart', + label: primaryActionText.value, + icon: UpdatedIcon, + action: () => initiateAction('Restart'), + }, + // TODO: Implement world scoping when Archon/Kyros support target worlds in power requests. + ...restartableWorlds.value.map((world) => ({ + id: `restart-${world.id}`, + label: `Restart with ${world.name}`, + icon: GlobeIcon, + action: () => initiateAction('Restart'), + })), +]) const powerActions = computed(() => { if (isInstalling.value) { return [ @@ -185,11 +205,10 @@ const powerActions = computed(() => { { id: 'restart', label: primaryActionText.value, - icon: UpdatedIcon, color: 'orange' as const, - tooltip: busyTooltip.value, - disabled: !canTakeAction.value, - onClick: handlePrimaryAction, + joinedActions: restartSplitActions.value, + primaryDisabled: !canTakeAction.value, + dropdownDisabled: !canTakeAction.value, }, { id: 'stop', @@ -231,7 +250,7 @@ const headerActions = computed(() => [ { id: 'settings', label: formatMessage(messages.instanceSettings), - icon: SettingsIcon, + icon: Settings2Icon, labelHidden: true, tooltip: formatMessage(messages.instanceSettings), onClick: () => openServerSettings({ tabId: 'installation' }),