From 9b17fa2b93be503778be68eae2bc0664d0132428 Mon Sep 17 00:00:00 2001 From: bbassie <17990055+bbassie@users.noreply.github.com> Date: Wed, 9 Sep 2026 09:39:39 +0000 Subject: [PATCH] Add host, server name and start options to the create instance modal Accounts with core.instance.assign get a host selector, defaulting to unassigned. Picking a host offers "Start after creation", which creates a new save on first run when no save is copied, and together with a config to copy it offers "Copy Save". Copying a config also offers a server name, prefilled from the source, which overwrites factorio.settings.name so the clone does not show up under the source name in the server browser. useInstanceConfig now clears the config while loading and drops errors, so an account without core.instance.get_config just does not get the server name field. Co-Authored-By: Claude Opus 5 (1M context) --- .../web_ui/src/components/InstancesPage.tsx | 134 ++++++++++++------ packages/web_ui/src/model/instance.ts | 13 +- 2 files changed, 99 insertions(+), 48 deletions(-) diff --git a/packages/web_ui/src/components/InstancesPage.tsx b/packages/web_ui/src/components/InstancesPage.tsx index ec5fe7b2..ed1498a2 100644 --- a/packages/web_ui/src/components/InstancesPage.tsx +++ b/packages/web_ui/src/components/InstancesPage.tsx @@ -1,6 +1,6 @@ -import React, { useContext, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { Button, Checkbox, Form, Input, Modal, Select, Space, Tooltip } from "antd"; +import { Button, Checkbox, Form, Input, Modal, Select, Space, Tooltip, Typography } from "antd"; import * as lib from "@clusterio/lib"; @@ -9,28 +9,45 @@ import ControlContext from "./ControlContext"; import PageHeader from "./PageHeader"; import PageLayout from "./PageLayout"; import PluginExtra from "./PluginExtra"; -import { useInstances } from "../model/instance"; +import { useHosts } from "../model/host"; +import { useInstanceConfig, useInstances } from "../model/instance"; import { useSaves } from "../model/saves"; import InstanceList from "./InstanceList"; import { notifyErrorHandler } from "../util/notify"; type SaveMode = "none" | "copy" | "save_and_copy"; +const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare; + function CreateInstanceButton(props: { instances: ReturnType[0] }) { let control = useContext(ControlContext); + let account = useAccount(); let navigate = useNavigate(); let [open, setOpen] = useState(false); let [creating, setCreating] = useState(false); let [form] = Form.useForm(); + let [hosts] = useHosts(); let [saves] = useSaves(); + const canAssign = account.hasPermission("core.instance.assign"); const cloneId: number | undefined = Form.useWatch("instanceClone", form); + const host: number | string | undefined = Form.useWatch("host", form); const saveMode: SaveMode = Form.useWatch("saveMode", form) ?? "none"; + const hostId = typeof host === "number" ? host : undefined; const source = cloneId !== undefined && cloneId >= 0 ? props.instances.get(cloneId) : undefined; const sourceRunning = source?.status === "running"; const activeSave = source ? [...saves.values()].find( save => save.instanceId === source.id && (sourceRunning ? save.loaded : save.loadByDefault) ) : undefined; + const sourceConfig = useInstanceConfig(source?.id); + const sourceSettings = sourceConfig?.["factorio.settings"] as Record | undefined; + const sourceServerName = typeof sourceSettings?.name === "string" ? sourceSettings.name : undefined; + + useEffect(() => { + if (open && sourceServerName !== undefined) { + form.setFieldValue("serverName", sourceServerName); + } + }, [open, sourceServerName]); async function createInstance() { let values = form.getFieldsValue(); @@ -38,7 +55,7 @@ function CreateInstanceButton(props: { instances: ReturnType { setOpen(false); }} destroyOnHidden > -
+ - + {canAssign && + + } + + {source && sourceSettings && + + - - Start after creation - - } + } + {source && hostId !== undefined && +