diff --git a/apps/docs/docs/integrations/docker-labels/index.mdx b/apps/docs/docs/integrations/docker-labels/index.mdx
index 677e7a518..de726e2df 100644
--- a/apps/docs/docs/integrations/docker-labels/index.mdx
+++ b/apps/docs/docs/integrations/docker-labels/index.mdx
@@ -69,7 +69,8 @@ Homepage metadata: once `homarr.name` is present, use `homarr.*` labels for the
## What onboarding creates
In **Connect**, label-discovered apps and integrations appear before image-based suggestions. You can import multiple
-services in one pass and edit every suggested address.
+services in one pass and edit every suggested address. For image-matched apps, Homarr shows the detected service type
+and uses it when building host, subdomain, or reverse-proxy path suggestions.
When setup completes, Homarr rechecks the selected container sources and then:
diff --git a/packages/api/src/router/onboard/onboard-router.ts b/packages/api/src/router/onboard/onboard-router.ts
index 1fc3972da..ef9756292 100644
--- a/packages/api/src/router/onboard/onboard-router.ts
+++ b/packages/api/src/router/onboard/onboard-router.ts
@@ -88,6 +88,7 @@ interface DiscoveredApp {
suggestedUrl: string;
publishedPort: number | null;
iconUrl: string | null;
+ detectedType?: string;
source: "label" | "docker";
group?: string;
boardName?: string;
@@ -207,8 +208,8 @@ export const onboardRouter = createTRPCRouter({
const normalized = imageName.toLowerCase().trim();
if (normalized.length < 3) return null;
return (
- dbIcons.find((icon) => icon.name.toLowerCase() === normalized)?.url ??
- dbIcons.find((icon) => icon.name.toLowerCase().startsWith(normalized))?.url ??
+ dbIcons.find((icon) => icon.name.toLowerCase() === normalized) ??
+ dbIcons.find((icon) => icon.name.toLowerCase().startsWith(normalized)) ??
null
);
};
@@ -257,7 +258,7 @@ export const onboardRouter = createTRPCRouter({
const imageName = extractContainerImageName(container.Image);
const containerName = container.Names[0]?.split("/")[1] ?? "Unknown";
const dbIcon = strictIconMatch(imageName);
- const iconUrl = dbIcon ?? cdnIconUrl(imageName.toLowerCase());
+ const iconUrl = dbIcon?.url ?? cdnIconUrl(imageName.toLowerCase());
const { url: suggestedUrl, publishedPort } = buildSuggestedUrl(container.Ports, container.host);
const kind = matchIntegrationKindFromContainer({ image: container.Image, name: containerName });
const sourceId = createDockerSourceId(container.host, container.Id);
@@ -271,6 +272,7 @@ export const onboardRouter = createTRPCRouter({
suggestedUrl,
publishedPort,
iconUrl,
+ detectedType: dbIcon.name,
source: "docker",
host: container.host,
});
diff --git a/packages/onboarding/src/onboarding-studio.module.css b/packages/onboarding/src/onboarding-studio.module.css
index 31b57b19d..1a0fcda32 100644
--- a/packages/onboarding/src/onboarding-studio.module.css
+++ b/packages/onboarding/src/onboarding-studio.module.css
@@ -181,6 +181,11 @@
border: 1px solid color-mix(in srgb, var(--mantine-color-default-border) 76%, transparent);
}
+.discoveryChoice {
+ width: 100%;
+ min-height: 4.25rem;
+}
+
.discoveryChoice[data-checked] {
border-color: var(--mantine-primary-color-filled);
background: color-mix(in srgb, var(--mantine-primary-color-light) 62%, var(--mantine-color-body));
diff --git a/packages/onboarding/src/setup-studio.tsx b/packages/onboarding/src/setup-studio.tsx
index 878a5c2f9..253bdbf3d 100644
--- a/packages/onboarding/src/setup-studio.tsx
+++ b/packages/onboarding/src/setup-studio.tsx
@@ -269,7 +269,9 @@ export const SetupStudio = ({ environment, assistantConfiguration }: OnboardingS
resolveDiscoveredAppUrl(
appUrlOverrides[app.sourceId],
app.suggestedUrl,
- serverOrigin ? buildAppUrl(app.containerName, serverOrigin, urlMode, app.publishedPort ?? undefined) : null,
+ serverOrigin
+ ? buildAppUrl(app.detectedType ?? app.containerName, serverOrigin, urlMode, app.publishedPort ?? undefined)
+ : null,
),
]),
),
@@ -1148,7 +1150,7 @@ const Connections = (props: StudioSectionProps) => {
{integration.containerName}
- {getIntegrationName(integration.kind)} ยท {integration.suggestedUrl || t("addressNeeded")}
+ {getIntegrationName(integration.kind)}
@@ -1165,7 +1167,7 @@ const Connections = (props: StudioSectionProps) => {
{props.discoveredApps.map((app) => {
const selected = props.selectedAppIds.includes(app.sourceId);
return (
-
+
{
p="sm"
radius="md"
>
-
+
{app.containerName.at(0)}
-
-
-
- {app.containerName}
-
-
- {app.source === "label" ? t("dockerLabelSource") : t("dockerImageSource")}
-
-
+
+
+ {app.containerName}
+
- {props.discoveredAppUrls[app.sourceId] || t("addressNeeded")}
+ {app.detectedType ?? t("appType")}
{selected ? (
props.setDiscoveredAppUrl(app.sourceId, event.currentTarget.value)}
error={props.appErrorSourceId === app.sourceId ? props.appError : undefined}
@@ -1218,6 +1215,12 @@ const Connections = (props: StudioSectionProps) => {
) : null}
+ {props.appError ? (
+
+ {props.appError}
+
+ ) : null}
+
- {props.appError ? (
-
- {props.appError}
-
- ) : null}
-
- {props.drafts.length > 0 ? (
- draft.id)}
- >
- {props.drafts.map((draft) => (
- props.updateDraft(draft.id, patch)}
- selectSecretOption={(option) => props.selectSecretOption(draft, option)}
- />
- ))}
-
- ) : (
- } my="xs">
- {t("empty")}
-
- )}
+
);
};
diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json
index e9898c480..f2f1d65a6 100644
--- a/packages/translation/src/lang/en.json
+++ b/packages/translation/src/lang/en.json
@@ -112,11 +112,9 @@
"detectedIntegrationLabel": "Import {name}, detected as {kind}",
"detectedAppLabel": "Import detected app {name}",
"otherIntegrations": "Add another integration",
+ "configureIntegrations": "Configure integrations",
"discoveredApps": "Detected apps ({count})",
- "dockerLabelSource": "Docker label",
- "dockerImageSource": "Docker image",
- "addressNeeded": "Address needed",
- "appUrl": "Address for {name}",
+ "appType": "App",
"appAddressRequired": "Add an HTTP or HTTPS address for {name}, or remove it from the selection.",
"empty": "No integrations selected. Homarr will still create a useful starter board with general widgets.",
"dockerSource": "Docker",