diff --git a/android/app/detekt-baseline.xml b/android/app/detekt-baseline.xml
index 88a28094f..2faf7416e 100644
--- a/android/app/detekt-baseline.xml
+++ b/android/app/detekt-baseline.xml
@@ -5,10 +5,10 @@
LongMethod:EditorActivity.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun EditorScreen( configuration: EditorConfiguration, dependencies: EditorDependencies? = null, accountId: ULong? = null, enableNativeMediaUpload: Boolean = true, coroutineScope: CoroutineScope, onClose: () -> Unit, onGutenbergViewCreated: (GutenbergView) -> Unit = {} )
LongMethod:MainActivity.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun MainScreen( configurations: List<ConfigurationItem>, onConfigurationClick: (ConfigurationItem) -> Unit, onConfigurationLongClick: (ConfigurationItem) -> Boolean, onAddConfiguration: (String) -> Unit, onDeleteConfiguration: (ConfigurationItem) -> Unit, onMediaProxyServer: () -> Unit = {}, isDiscoveringSite: Boolean = false, onDismissDiscovering: () -> Unit = {}, isLoadingCapabilities: Boolean = false, authError: String? = null, onDismissAuthError: () -> Unit = {} )
LongMethod:MediaProxyServerActivity.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun MediaProxyServerScreen(onBack: () -> Unit)
- LongMethod:PostsListActivity.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun PostsListScreen( viewModel: PostsListViewModel, onClose: () -> Unit, onPostSelected: (AnyPostWithEditContext) -> Unit )
- LongMethod:SitePreparationActivity.kt$@Composable private fun FeatureConfigurationCard( enableNativeInserter: Boolean, onEnableNativeInserterChange: (Boolean) -> Unit, enableInserterMediaStrip: Boolean, onEnableInserterMediaStripChange: (Boolean) -> Unit, enableNativeMediaUpload: Boolean, onEnableNativeMediaUploadChange: (Boolean) -> Unit, siteSupportsThemeStyles: Boolean, enableThemeStyles: Boolean, onEnableThemeStylesChange: (Boolean) -> Unit, siteSupportsBlockAssets: Boolean, enableThirdPartyBlocks: Boolean, onEnableThirdPartyBlocksChange: (Boolean) -> Unit, isPreparing: Boolean, enableNetworkLogging: Boolean, onEnableNetworkLoggingChange: (Boolean) -> Unit, postTypes: List<PostTypeDetails>, selectedPostType: PostTypeDetails?, onPostTypeChange: (PostTypeDetails) -> Unit, showBrowseButton: Boolean = false, onBrowsePosts: () -> Unit = {} )
+ LongMethod:PostsListActivity.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun PostsListScreen( viewModel: PostsListViewModel, title: String, onClose: () -> Unit, onPostSelected: (AnyPostWithEditContext) -> Unit )
+ LongMethod:SitePreparationActivity.kt$@Composable private fun FeatureConfigurationCard( enableNativeInserter: Boolean, onEnableNativeInserterChange: (Boolean) -> Unit, enableInserterMediaStrip: Boolean, onEnableInserterMediaStripChange: (Boolean) -> Unit, enableNativeMediaUpload: Boolean, onEnableNativeMediaUploadChange: (Boolean) -> Unit, siteSupportsThemeStyles: Boolean, enableThemeStyles: Boolean, onEnableThemeStylesChange: (Boolean) -> Unit, siteSupportsBlockAssets: Boolean, enableThirdPartyBlocks: Boolean, onEnableThirdPartyBlocksChange: (Boolean) -> Unit, isPreparing: Boolean, enableNetworkLogging: Boolean, onEnableNetworkLoggingChange: (Boolean) -> Unit, postTypes: List<PostTypeOption>, selectedPostType: PostTypeOption?, onPostTypeChange: (PostTypeOption) -> Unit, showBrowseButton: Boolean = false, onBrowsePosts: () -> Unit = {} )
LongParameterList:MainActivity.kt$( configurations: List<ConfigurationItem>, onConfigurationClick: (ConfigurationItem) -> Unit, onConfigurationLongClick: (ConfigurationItem) -> Boolean, onAddConfiguration: (String) -> Unit, onDeleteConfiguration: (ConfigurationItem) -> Unit, onMediaProxyServer: () -> Unit = {}, isDiscoveringSite: Boolean = false, onDismissDiscovering: () -> Unit = {}, isLoadingCapabilities: Boolean = false, authError: String? = null, onDismissAuthError: () -> Unit = {} )
- LongParameterList:SitePreparationActivity.kt$( enableNativeInserter: Boolean, onEnableNativeInserterChange: (Boolean) -> Unit, enableInserterMediaStrip: Boolean, onEnableInserterMediaStripChange: (Boolean) -> Unit, enableNativeMediaUpload: Boolean, onEnableNativeMediaUploadChange: (Boolean) -> Unit, siteSupportsThemeStyles: Boolean, enableThemeStyles: Boolean, onEnableThemeStylesChange: (Boolean) -> Unit, siteSupportsBlockAssets: Boolean, enableThirdPartyBlocks: Boolean, onEnableThirdPartyBlocksChange: (Boolean) -> Unit, isPreparing: Boolean, enableNetworkLogging: Boolean, onEnableNetworkLoggingChange: (Boolean) -> Unit, postTypes: List<PostTypeDetails>, selectedPostType: PostTypeDetails?, onPostTypeChange: (PostTypeDetails) -> Unit, showBrowseButton: Boolean = false, onBrowsePosts: () -> Unit = {} )
+ LongParameterList:SitePreparationActivity.kt$( enableNativeInserter: Boolean, onEnableNativeInserterChange: (Boolean) -> Unit, enableInserterMediaStrip: Boolean, onEnableInserterMediaStripChange: (Boolean) -> Unit, enableNativeMediaUpload: Boolean, onEnableNativeMediaUploadChange: (Boolean) -> Unit, siteSupportsThemeStyles: Boolean, enableThemeStyles: Boolean, onEnableThemeStylesChange: (Boolean) -> Unit, siteSupportsBlockAssets: Boolean, enableThirdPartyBlocks: Boolean, onEnableThirdPartyBlocksChange: (Boolean) -> Unit, isPreparing: Boolean, enableNetworkLogging: Boolean, onEnableNetworkLoggingChange: (Boolean) -> Unit, postTypes: List<PostTypeOption>, selectedPostType: PostTypeOption?, onPostTypeChange: (PostTypeOption) -> Unit, showBrowseButton: Boolean = false, onBrowsePosts: () -> Unit = {} )
MaxLineLength:MediaProxyServerActivity.kt$Text("Size", fontFamily = FontFamily.Monospace, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.weight(1f))
MaxLineLength:MediaProxyServerActivity.kt$Text("Throughput", fontFamily = FontFamily.Monospace, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.weight(1f))
MaxLineLength:MediaProxyServerActivity.kt$Text("Time", fontFamily = FontFamily.Monospace, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.weight(1f))
diff --git a/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt
index 933c07c04..efc079b8a 100644
--- a/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt
+++ b/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt
@@ -68,17 +68,20 @@ class PostsListActivity : ComponentActivity() {
companion object {
const val EXTRA_ACCOUNT_ID = "account_id"
const val EXTRA_POST_TYPE = "post_type"
+ const val EXTRA_TITLE = "title"
fun createIntent(
context: Context,
accountId: ULong,
postType: PostTypeDetails,
+ title: String,
configuration: EditorConfiguration,
dependencies: EditorDependencies?
): Intent {
return Intent(context, PostsListActivity::class.java).apply {
putExtra(EXTRA_ACCOUNT_ID, accountId.toLong())
putExtra(EXTRA_POST_TYPE, postType)
+ putExtra(EXTRA_TITLE, title)
putExtra(MainActivity.EXTRA_CONFIGURATION, configuration)
if (dependencies != null) {
val filePath = EditorDependenciesSerializer.writeToDisk(context, dependencies)
@@ -95,6 +98,7 @@ class PostsListActivity : ComponentActivity() {
val accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1L).takeIf { it >= 0 }?.toULong()
val postType = intent.getParcelableExtra(EXTRA_POST_TYPE, PostTypeDetails::class.java)
?: PostTypeDetails.post
+ val title = intent.getStringExtra(EXTRA_TITLE) ?: getString(R.string.posts)
val configuration = intent.getParcelableExtra(MainActivity.EXTRA_CONFIGURATION, EditorConfiguration::class.java)
val dependenciesPath = intent.getStringExtra(EditorActivity.EXTRA_DEPENDENCIES_PATH)
@@ -116,6 +120,7 @@ class PostsListActivity : ComponentActivity() {
AppTheme {
PostsListScreen(
viewModel = viewModel,
+ title = title,
onClose = { finish() },
onPostSelected = { post ->
launchEditor(post, configuration, dependenciesPath, accountId)
@@ -209,6 +214,7 @@ class PostsListViewModel(
@Composable
fun PostsListScreen(
viewModel: PostsListViewModel,
+ title: String,
onClose: () -> Unit,
onPostSelected: (AnyPostWithEditContext) -> Unit
) {
@@ -222,7 +228,7 @@ fun PostsListScreen(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
- title = { Text(stringResource(R.string.posts)) },
+ title = { Text(title) },
navigationIcon = {
IconButton(onClick = onClose) {
Icon(
diff --git a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt
index c999bbce0..6ae8449a7 100644
--- a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt
+++ b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt
@@ -70,7 +70,6 @@ import java.util.Locale
import org.wordpress.gutenberg.model.EditorConfiguration
import org.wordpress.gutenberg.model.EditorDependencies
import org.wordpress.gutenberg.model.EditorDependenciesSerializer
-import org.wordpress.gutenberg.model.PostTypeDetails
class SitePreparationActivity : ComponentActivity() {
@@ -197,12 +196,19 @@ class SitePreparationActivity : ComponentActivity() {
private fun launchPostsList(
accountId: ULong,
- postType: PostTypeDetails,
+ postType: PostTypeOption,
configuration: EditorConfiguration,
dependencies: EditorDependencies?
) {
startActivity(
- PostsListActivity.createIntent(this, accountId, postType, configuration, dependencies)
+ PostsListActivity.createIntent(
+ this,
+ accountId,
+ postType.details,
+ postType.pluralLabel,
+ configuration,
+ dependencies
+ )
)
}
}
@@ -214,7 +220,7 @@ fun SitePreparationScreen(
accountId: ULong?,
onClose: () -> Unit,
onStartEditor: (EditorConfiguration, EditorDependencies?, Boolean) -> Unit,
- onBrowsePosts: (EditorConfiguration, EditorDependencies?, PostTypeDetails) -> Unit
+ onBrowsePosts: (EditorConfiguration, EditorDependencies?, PostTypeOption) -> Unit
) {
val uiState by viewModel.uiState.collectAsState()
@@ -426,9 +432,9 @@ private fun FeatureConfigurationCard(
isPreparing: Boolean,
enableNetworkLogging: Boolean,
onEnableNetworkLoggingChange: (Boolean) -> Unit,
- postTypes: List,
- selectedPostType: PostTypeDetails?,
- onPostTypeChange: (PostTypeDetails) -> Unit,
+ postTypes: List,
+ selectedPostType: PostTypeOption?,
+ onPostTypeChange: (PostTypeOption) -> Unit,
showBrowseButton: Boolean = false,
onBrowsePosts: () -> Unit = {}
) {
@@ -566,7 +572,7 @@ private fun FeatureConfigurationCard(
enabled = !isPreparing
)
Text(
- text = postType.postType.replaceFirstChar { it.uppercase() },
+ text = postType.label,
modifier = Modifier.padding(start = 8.dp)
)
}
diff --git a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt
index 740f155fc..b71615e04 100644
--- a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt
+++ b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt
@@ -28,9 +28,9 @@ data class SitePreparationUiState(
val enableThirdPartyBlocks: Boolean = true,
val enableNetworkLogging: Boolean = false,
/** All viewable post types fetched from the site, or empty while loading. */
- val postTypes: List = emptyList(),
+ val postTypes: List = emptyList(),
/** The post type currently selected in the picker. Null until [postTypes] loads. */
- val selectedPostType: PostTypeDetails? = null,
+ val selectedPostType: PostTypeOption? = null,
val cacheBundleCount: Int? = null,
val isLoading: Boolean = false,
val error: String? = null,
@@ -39,6 +39,18 @@ data class SitePreparationUiState(
val loadingProgress: Float? = null
)
+/** A post type offered in the picker, with the names shown for it. */
+data class PostTypeOption(val details: PostTypeDetails, val label: String, val pluralLabel: String) {
+ companion object {
+ /** Falls back to the slug and REST base when the site's labels are missing or blank. */
+ fun from(details: PostTypeDetails, label: String? = null, pluralLabel: String? = null) = PostTypeOption(
+ details = details,
+ label = label?.ifBlank { null } ?: details.postType.replaceFirstChar { it.uppercase() },
+ pluralLabel = pluralLabel?.ifBlank { null } ?: details.restBase.replaceFirstChar { it.uppercase() }
+ )
+ }
+}
+
class SitePreparationViewModel(
application: Application,
private val configurationItem: ConfigurationItem
@@ -113,7 +125,7 @@ class SitePreparationViewModel(
_uiState.update { it.copy(enableNetworkLogging = enabled) }
}
- fun setPostType(postType: PostTypeDetails) {
+ fun setPostType(postType: PostTypeOption) {
// Discard dependencies prepared for the previous post type.
_uiState.update { it.copy(selectedPostType = postType, editorDependencies = null) }
}
@@ -227,10 +239,11 @@ class SitePreparationViewModel(
private fun createBundledConfiguration(): EditorConfiguration {
// Bundled offline editor: only the standard `post` type is meaningful.
+ val post = PostTypeOption.from(PostTypeDetails.post)
_uiState.update {
it.copy(
- postTypes = listOf(PostTypeDetails.post),
- selectedPostType = PostTypeDetails.post
+ postTypes = listOf(post),
+ selectedPostType = post
)
}
return EditorConfiguration.builder(
@@ -274,8 +287,8 @@ class SitePreparationViewModel(
// available (the typical case); otherwise pick the first type in the
// list. Falls back to `PostTypeDetails.post` if the call fails so the
// editor can still launch with a sensible default.
- val postTypes = loadPostTypes(config) ?: listOf(PostTypeDetails.post)
- val defaultPostType = postTypes.firstOrNull { it.postType == "post" } ?: postTypes.first()
+ val postTypes = loadPostTypes(config) ?: listOf(PostTypeOption.from(PostTypeDetails.post))
+ val defaultPostType = postTypes.firstOrNull { it.details.postType == "post" } ?: postTypes.first()
_uiState.update {
it.copy(
postTypes = postTypes,
@@ -286,7 +299,7 @@ class SitePreparationViewModel(
return EditorConfiguration.builder(
siteURL = config.siteUrl,
siteApiRoot = siteApiRoot,
- postType = defaultPostType
+ postType = defaultPostType.details
)
.setPlugins(capabilities.supportsPlugins)
.setThemeStyles(capabilities.supportsThemeStyles)
@@ -310,7 +323,7 @@ class SitePreparationViewModel(
/**
* Fetches the site's post types from the WordPress REST API and returns the
- * subset relevant to the editor picker.
+ * subset relevant to the editor picker, each with its singular and plural labels.
*
* Mirrors the iOS [SitePreparationView.loadPostTypes] filter:
* - always include the standard `post` and `page` types
@@ -322,7 +335,7 @@ class SitePreparationViewModel(
*/
private suspend fun loadPostTypes(
config: ConfigurationItem.ConfiguredEditor
- ): List? {
+ ): List? {
val app = getApplication()
val account = app.accountRepository.all().firstOrNull { it.id() == config.accountId }
?: return null
@@ -342,13 +355,20 @@ class SitePreparationViewModel(
}
}
.map { (_, details) ->
- PostTypeDetails(
- postType = details.slug,
- restBase = details.restBase,
- restNamespace = details.restNamespace
+ PostTypeOption.from(
+ details = PostTypeDetails(
+ postType = details.slug,
+ restBase = details.restBase,
+ restNamespace = details.restNamespace
+ ),
+ label = details.labels.singularName,
+ pluralLabel = details.labels.name
)
}
- .sortedBy { it.postType }
+ .sortedWith(
+ compareBy(String.CASE_INSENSITIVE_ORDER) { it.label }
+ .thenBy { it.details.postType }
+ )
}
/**
@@ -372,7 +392,7 @@ class SitePreparationViewModel(
.setEnableInserterMediaStrip(_uiState.value.enableInserterMediaStrip)
.setThemeStyles(baseConfig.themeStyles && _uiState.value.enableThemeStyles)
.setPlugins(baseConfig.plugins && _uiState.value.enableThirdPartyBlocks)
- .setPostType(selectedPostType)
+ .setPostType(selectedPostType.details)
.build()
}
}
diff --git a/ios/Demo-iOS/Sources/Views/PostsListView.swift b/ios/Demo-iOS/Sources/Views/PostsListView.swift
index f0f3331aa..8e81f5651 100644
--- a/ios/Demo-iOS/Sources/Views/PostsListView.swift
+++ b/ios/Demo-iOS/Sources/Views/PostsListView.swift
@@ -10,7 +10,16 @@ struct PostsListView: View {
@State
private var viewModel: PostsListViewModel
- init(client: WordPressAPI, postTypeDetails: PostTypeDetails, editorConfiguration: EditorConfiguration, editorDependencies: EditorDependencies?) {
+ private let title: String
+
+ init(
+ client: WordPressAPI,
+ postTypeDetails: PostTypeDetails,
+ title: String,
+ editorConfiguration: EditorConfiguration,
+ editorDependencies: EditorDependencies?
+ ) {
+ self.title = title
self.viewModel = PostsListViewModel(
client: client,
postTypeDetails: postTypeDetails,
@@ -37,7 +46,7 @@ struct PostsListView: View {
}
} else if viewModel.posts.isEmpty {
ContentUnavailableView {
- Label("No Entires", systemImage: "doc.text")
+ Label("No Entries", systemImage: "doc.text")
} description: {
Text("No entries found for this post type.")
}
@@ -63,7 +72,7 @@ struct PostsListView: View {
.listStyle(.plain)
}
}
- .navigationTitle(viewModel.postTypeDetails.restBase.capitalized)
+ .navigationTitle(title)
.task {
await viewModel.loadPosts()
}
diff --git a/ios/Demo-iOS/Sources/Views/SitePreparationView.swift b/ios/Demo-iOS/Sources/Views/SitePreparationView.swift
index 94a6477d2..1e2fc875e 100644
--- a/ios/Demo-iOS/Sources/Views/SitePreparationView.swift
+++ b/ios/Demo-iOS/Sources/Views/SitePreparationView.swift
@@ -75,8 +75,8 @@ struct SitePreparationView: View {
}
} else {
Picker("Post Type", selection: $viewModel.selectedPostTypeDetails) {
- ForEach(viewModel.postTypes, id: \.self) { postType in
- Text(postType.name).tag(postType)
+ ForEach(viewModel.postTypes, id: \.details) { option in
+ Text(option.label).tag(option.details)
}
}
.disabled(viewModel.disableButtons)
@@ -87,6 +87,7 @@ struct SitePreparationView: View {
PostsListView(
client: client,
postTypeDetails: viewModel.selectedPostTypeDetails,
+ title: viewModel.selectedPostType.pluralLabel,
editorConfiguration: configuration,
editorDependencies: viewModel.editorDependencies
)
@@ -243,7 +244,12 @@ class SitePreparationViewModel {
}
}
- var postTypes: [PostTypeDetails] = []
+ var postTypes: [PostTypeOption] = []
+
+ var selectedPostType: PostTypeOption {
+ postTypes.first { $0.details == selectedPostTypeDetails }
+ ?? PostTypeOption(details: selectedPostTypeDetails)
+ }
var selectedPostTypeDetails: PostTypeDetails {
get { editorConfiguration?.postType ?? .post }
@@ -288,7 +294,7 @@ class SitePreparationViewModel {
switch configurationItem {
case .bundledEditor:
self.editorConfiguration = Self.applyDemoAppDefaults(to: .bundled)
- self.postTypes = [.post, .page]
+ self.postTypes = PostTypeOption.builtIn
case .localWordPress:
guard let credentials = LocalWordPressCredentials.load() else {
throw AppError(errorDescription: "Local WordPress not configured.\n\nRun 'make wp-env-start' from the project root to set up a local WordPress environment.")
@@ -345,7 +351,7 @@ class SitePreparationViewModel {
let newConfiguration = try await self.loadConfiguration(for: account)
self.editorConfiguration = Self.applyDemoAppDefaults(to: newConfiguration)
} catch let error as WpApiError where error.isDeviceOffline {
- self.postTypes = [.post, .page]
+ self.postTypes = PostTypeOption.builtIn
let fallback = Self.buildOfflineConfiguration(for: account)
self.editorConfiguration = Self.applyDemoAppDefaults(to: fallback)
}
@@ -524,7 +530,7 @@ class SitePreparationViewModel {
@MainActor
private func loadPostTypes() async throws {
guard let client = self.client else {
- self.postTypes = [.post, .page]
+ self.postTypes = PostTypeOption.builtIn
return
}
@@ -547,17 +553,20 @@ class SitePreparationViewModel {
}
.values
.map { postType in
- PostTypeDetails(
- postType: postType.slug,
- restBase: postType.restBase,
- restNamespace: postType.restNamespace
+ PostTypeOption(
+ details: PostTypeDetails(
+ postType: postType.slug,
+ restBase: postType.restBase,
+ restNamespace: postType.restNamespace
+ ),
+ label: postType.labels.singularName,
+ pluralLabel: postType.labels.name
)
}
- .sorted(using: KeyPathComparator(\.postType))
-
- if let firstType = postTypes.first {
- self.selectedPostTypeDetails = firstType
- }
+ .sorted(using: [
+ KeyPathComparator(\.label, comparator: .localizedStandard),
+ KeyPathComparator(\.details.postType),
+ ])
}
func buildAndLoadConfiguration(navigation: Navigation) {
@@ -575,6 +584,27 @@ class SitePreparationViewModel {
}
}
+/// A post type offered in the picker, with the names shown for it.
+struct PostTypeOption {
+ static let builtIn = [PostTypeOption(details: .post), PostTypeOption(details: .page)]
+
+ let details: PostTypeDetails
+ let label: String
+ let pluralLabel: String
+
+ /// Falls back to the slug and REST base when the site's labels are missing or blank.
+ init(details: PostTypeDetails, label: String? = nil, pluralLabel: String? = nil) {
+ self.details = details
+ self.label = Self.nonBlank(label) ?? details.postType.capitalized
+ self.pluralLabel = Self.nonBlank(pluralLabel) ?? details.restBase.capitalized
+ }
+
+ private static func nonBlank(_ value: String?) -> String? {
+ guard let value, !value.trimmingCharacters(in: .whitespaces).isEmpty else { return nil }
+ return value
+ }
+}
+
struct KeyValueRow: View {
enum Value {
@@ -613,12 +643,6 @@ struct KeyValueRow: View {
}
}
-extension PostTypeDetails {
- var name: String {
- postType.capitalized
- }
-}
-
#Preview("Bundled Editor") {
NavigationStack {
SitePreparationView(site: .bundledEditor)