Fix sageclient startup URL direct rendering

This commit is contained in:
yumoqing 2026-05-19 07:44:58 +08:00
parent f642667bab
commit 51b207626a
2 changed files with 63 additions and 45 deletions

View File

@ -44,7 +44,8 @@ cd /path/to/bricks-mp/test/sageclient
-Dsage.centerUi=/center.ui
```
Run with a startup URL to enter that UI directly:
Run with a startup URL to enter that UI directly without showing the sample
base URL / login input shell:
```bash
cd /path/to/bricks-mp/test/sageclient
@ -57,7 +58,7 @@ The packaged macOS app also accepts the URL as its first argument:
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. The actual HTTP request is still issued through `BricksHttp`, so startup URLs ending in `.ui` / `.dspy` are requested with `_webbricks_=1`, `_width`, `_height`, `_is_mobile` and `_lang`; they must not be fetched as raw HTML/template pages.
If the argument is an absolute `http://` or `https://` URL, `sageclient` uses the URL origin as `baseUrl` and dispatches that same absolute URL through the Bricks `urlwidget` loading path. If the argument is a relative path such as `/center.ui`, it uses `sage.baseUrl` and loads that path. The actual HTTP request is still issued through `BricksHttp`, so startup URLs ending in `.ui` / `.dspy` are requested with `_webbricks_=1`, `_width`, `_height`, `_is_mobile` and `_lang`; they must not be fetched as raw HTML/template pages.
Optional properties:

View File

@ -123,45 +123,55 @@ fun main(args: Array<String>) = application {
val currentWidget by context.currentWidget.collectAsState()
LaunchedEffect(actionDispatcher, startupUrl) {
startupUrl?.let { url ->
val startupTarget = remember(startupUrl) { startupUrl?.let(::toWidgetUrl) }
LaunchedEffect(actionDispatcher, startupTarget) {
startupTarget?.let { url ->
configureContextFromStartupUrl(context, url)
actionDispatcher.dispatch(
BricksBind(
event = "startup",
actiontype = "urlwidget",
url = toWidgetPath(url)
url = url
)
)
}
}
SageClientScreen(
baseUrl = context.baseUrl,
widget = currentWidget,
actionDispatcher = actionDispatcher,
onBaseUrlChange = { context.baseUrl = it.trimEnd('/') },
onLoadCenter = {
actionDispatcher.dispatch(
BricksBind(
event = "click",
actiontype = "urlwidget",
url = System.getProperty("sage.centerUi", DEFAULT_CENTER_UI)
)
)
},
onLogin = { username, password ->
scope.launch {
loginAndLoadCenter(
context = context,
http = http,
actionDispatcher = actionDispatcher,
username = username,
password = password,
onMessage = { title, body, isError -> message = Triple(title, body, isError) }
if (startupTarget != null) {
BricksStartupScreen(
widget = currentWidget,
actionDispatcher = actionDispatcher
)
} else {
SageClientScreen(
baseUrl = context.baseUrl,
widget = currentWidget,
actionDispatcher = actionDispatcher,
onBaseUrlChange = { context.baseUrl = it.trimEnd('/') },
onLoadCenter = {
actionDispatcher.dispatch(
BricksBind(
event = "click",
actiontype = "urlwidget",
url = System.getProperty("sage.centerUi", DEFAULT_CENTER_UI)
)
)
},
onLogin = { username, password ->
scope.launch {
loginAndLoadCenter(
context = context,
http = http,
actionDispatcher = actionDispatcher,
username = username,
password = password,
onMessage = { title, body, isError -> message = Triple(title, body, isError) }
)
}
}
}
)
)
}
dialogWidget?.let { widget ->
AlertDialog(
@ -188,6 +198,27 @@ fun main(args: Array<String>) = application {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun BricksStartupScreen(
widget: BricksWidget?,
actionDispatcher: ActionDispatcher
) {
Scaffold { padding ->
Box(modifier = Modifier.padding(padding).fillMaxSize()) {
if (widget == null) {
Text("Loading...")
} else {
RenderWidget(
widget = widget,
actionDispatcher = actionDispatcher,
modifier = Modifier.fillMaxSize()
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SageClientScreen(
@ -274,11 +305,7 @@ private fun configureContextFromStartupUrl(context: BricksContext, startupUrl: S
}
}
private fun toWidgetPath(startupUrl: String): String {
if (!startupUrl.startsWith("http://") && !startupUrl.startsWith("https://")) return startupUrl
val pathWithQuery = startupUrl.pathAndQueryPart()
return pathWithQuery.ifBlank { "/" }
}
private fun toWidgetUrl(startupUrl: String): String = startupUrl.ifBlank { "/" }
private fun String.originPart(): String {
val schemeEnd = indexOf("://")
@ -288,16 +315,6 @@ private fun String.originPart(): String {
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,