diff --git a/scarf/scarf/Features/Templates/Views/TemplateConfigSheet.swift b/scarf/scarf/Features/Templates/Views/TemplateConfigSheet.swift index 81700ef..c9d4875 100644 --- a/scarf/scarf/Features/Templates/Views/TemplateConfigSheet.swift +++ b/scarf/scarf/Features/Templates/Views/TemplateConfigSheet.swift @@ -23,6 +23,20 @@ struct TemplateConfigSheet: View { header Divider() ScrollView { + // `.frame(maxWidth: .infinity, alignment: .leading)` is + // load-bearing: without it, SwiftUI resolves width + // bottom-up and an unbreakable token in a child (e.g. a + // raw URL inside a field description rendered via + // AttributedString markdown) sets the whole VStack's + // ideal width to that token's length. ScrollView's + // content then exceeds the sheet's viewport, the outer + // `.frame(minWidth: 560)` grows to content width, and + // the window clips the result with labels cut off on + // the left + URL spilling off the right. With the + // explicit maxWidth, the ScrollView's offered width + // propagates down and the description Text's + // `.fixedSize(horizontal: false, vertical: true)` + // wraps at whitespace boundaries as intended. VStack(alignment: .leading, spacing: 18) { if viewModel.schema.fields.isEmpty { ContentUnavailableView( @@ -40,6 +54,7 @@ struct TemplateConfigSheet: View { modelRecommendation(rec) } } + .frame(maxWidth: .infinity, alignment: .leading) .padding(20) } Divider() @@ -116,7 +131,11 @@ struct TemplateConfigSheet: View { // Inline markdown so descriptions can include // `[Create one](https://…)`-style links to token // generation pages, **bold** emphasis on important - // prerequisites, etc. + // prerequisites, etc. Raw URLs (not wrapped in + // markdown link syntax) will still render but can't + // word-break mid-token — keep the parent maxWidth + // constraint below so a rogue raw URL wraps cleanly + // instead of expanding the entire sheet. TemplateMarkdown.inlineText(description) .font(.caption) .foregroundStyle(.secondary) @@ -129,6 +148,12 @@ struct TemplateConfigSheet: View { .foregroundStyle(.red) } } + // maxWidth: .infinity forces this row to span the column's + // full width so its internal description Text wraps instead + // of expanding the outer VStack when a description contains + // a long unbreakable token (raw URL, path, etc.). See the + // comment on the parent ScrollView's inner VStack. + .frame(maxWidth: .infinity, alignment: .leading) .padding(12) .background( RoundedRectangle(cornerRadius: 8) diff --git a/scarf/scarf/Features/Templates/Views/TemplateInstallSheet.swift b/scarf/scarf/Features/Templates/Views/TemplateInstallSheet.swift index e697050..c1e01c1 100644 --- a/scarf/scarf/Features/Templates/Views/TemplateInstallSheet.swift +++ b/scarf/scarf/Features/Templates/Views/TemplateInstallSheet.swift @@ -126,6 +126,16 @@ struct TemplateInstallSheet: View { .padding(.bottom, 8) Divider() ScrollView { + // `.frame(maxWidth: .infinity, alignment: .leading)` — + // without it, a subsection containing an unbreakable + // token (raw URL in a cron prompt or README block, a + // long file path in the project-files list, a schema + // description with a bare URL, etc.) sets the VStack's + // ideal width to that token's length; the sheet grows + // past its `.frame(minWidth: 620)` and gets clipped by + // the window. Same fix as `TemplateConfigSheet`'s + // inner VStack — propagate the ScrollView's width down + // so inner Text wraps instead of expanding outward. VStack(alignment: .leading, spacing: 16) { projectFilesSection(plan: plan) if plan.skillsNamespaceDir != nil { @@ -142,6 +152,7 @@ struct TemplateInstallSheet: View { } readmeSection } + .frame(maxWidth: .infinity, alignment: .leading) .padding(.vertical) } Divider()