diff --git a/test/sageclient/README.md b/test/sageclient/README.md index 60851e1..bce148c 100644 --- a/test/sageclient/README.md +++ b/test/sageclient/README.md @@ -35,6 +35,8 @@ Extra Gradle arguments can be appended, for example: ## Run +Run without arguments to open the sample shell: + ```bash cd /path/to/bricks-mp/test/sageclient ../../gradlew run \ @@ -42,6 +44,21 @@ cd /path/to/bricks-mp/test/sageclient -Dsage.centerUi=/center.ui ``` +Run with a startup URL to enter that UI directly: + +```bash +cd /path/to/bricks-mp/test/sageclient +../../gradlew run --args="https://ai.atvoe.com/center.ui" +``` + +The packaged macOS app also accepts the URL as its first argument: + +```bash +open build/compose/binaries/main/app/sageclient.app --args https://ai.atvoe.com/center.ui +``` + +If the argument is an absolute `http://` or `https://` URL, `sageclient` uses the URL origin as `baseUrl` and loads the path/query part as the initial Bricks UI. If the argument is a relative path such as `/center.ui`, it uses `sage.baseUrl` and loads that path. + Optional properties: - `sage.baseUrl` defaults to `http://localhost:8080` diff --git a/test/sageclient/src/jvmMain/kotlin/com/bricks/test/sageclient/Main.kt b/test/sageclient/src/jvmMain/kotlin/com/bricks/test/sageclient/Main.kt index d67a1e7..3832d89 100644 --- a/test/sageclient/src/jvmMain/kotlin/com/bricks/test/sageclient/Main.kt +++ b/test/sageclient/src/jvmMain/kotlin/com/bricks/test/sageclient/Main.kt @@ -51,14 +51,21 @@ private const val DEFAULT_CENTER_UI = "/center.ui" private const val DEFAULT_LOGIN_ACTION = "/rbac/user/login" private const val DEFAULT_LOGIN_UI = "/rbac/user/login.ui" -fun main() = application { +fun main(args: Array) = application { val context = remember { BricksContext() } val http = remember { BricksHttp(context) } val scope = rememberCoroutineScope() val windowState = remember { WindowState(width = 1280.dp, height = 800.dp) } + val startupUrl = remember(args) { args.firstOrNull()?.takeIf { it.isNotBlank() } } + LaunchedEffect(Unit) { - context.baseUrl = System.getProperty("sage.baseUrl", DEFAULT_BASE_URL) + val initialUrl = startupUrl + if (initialUrl != null) { + configureContextFromStartupUrl(context, initialUrl) + } else { + context.baseUrl = System.getProperty("sage.baseUrl", DEFAULT_BASE_URL) + } } Window( @@ -115,6 +122,19 @@ fun main() = application { } val currentWidget by context.currentWidget.collectAsState() + + LaunchedEffect(actionDispatcher, startupUrl) { + startupUrl?.let { url -> + actionDispatcher.dispatch( + BricksBind( + event = "startup", + actiontype = "urlwidget", + url = toWidgetPath(url) + ) + ) + } + } + SageClientScreen( baseUrl = context.baseUrl, widget = currentWidget, @@ -246,6 +266,38 @@ private fun SageClientScreen( } } +private fun configureContextFromStartupUrl(context: BricksContext, startupUrl: String) { + if (startupUrl.startsWith("http://") || startupUrl.startsWith("https://")) { + context.baseUrl = startupUrl.originPart() + } else { + context.baseUrl = System.getProperty("sage.baseUrl", DEFAULT_BASE_URL) + } +} + +private fun toWidgetPath(startupUrl: String): String { + if (!startupUrl.startsWith("http://") && !startupUrl.startsWith("https://")) return startupUrl + val pathWithQuery = startupUrl.pathAndQueryPart() + return pathWithQuery.ifBlank { "/" } +} + +private fun String.originPart(): String { + val schemeEnd = indexOf("://") + if (schemeEnd < 0) return "" + val authorityStart = schemeEnd + 3 + val authorityEnd = indexOf('/', startIndex = authorityStart).let { if (it < 0) length else it } + return substring(0, authorityEnd) +} + +private fun String.pathAndQueryPart(): String { + val schemeEnd = indexOf("://") + val pathStart = if (schemeEnd >= 0) { + indexOf('/', startIndex = schemeEnd + 3).let { if (it < 0) return "" else it } + } else { + 0 + } + return substring(pathStart).substringBefore('#') +} + private suspend fun loginAndLoadCenter( context: BricksContext, http: BricksHttp,