// HTML Builder for Shards
//
// Generates portable HTML snippets for resumable components (shards)
//
///|
/// Generate the complete shard HTML
pub fn generate_shard(config : ShardConfig) -> ShardOutput {
let sb = StringBuilder(size_hint=512)
let head_scripts : Array[String] = []
let state_scripts : Array[String] = []
// 1. Loader script (if requested)
if config.include_loader {
sb.write_string("\n")
head_scripts.push(config.loader_url)
}
// 2. State script (if using ScriptRef)
// Note: actual script generation is done by generate_shard_with_state_script
// 3. Main container div
sb.write_string("')
// 4. SSR content (if any)
if config.ssr_content is Some(content) {
sb.write_string(content)
}
// 5. Close container
sb.write_string("
")
{ html: sb.to_string(), head_scripts, state_scripts, }
}
///|
/// Generate shard with automatic state script
/// Use this when you have large state that should be in a separate script tag
pub fn generate_shard_with_state_script(
config : ShardConfig,
state_json : String,
) -> ShardOutput {
let state_script_id = "luna-state-" + config.id
let state_script = generate_state_script(state_script_id, state_json)
// Update config to use script reference
let updated_config = { ..config, state: ScriptRef(state_script_id), }
let base_output = generate_shard(updated_config)
// Append state script to HTML
let combined_html = base_output.html + "\n" + state_script
let state_scripts = base_output.state_scripts
state_scripts.push(state_script)
{
html: combined_html,
head_scripts: base_output.head_scripts,
state_scripts,
}
}
///|
/// Generate minimal shard (no loader, inline state)
pub fn generate_minimal_shard(
id : String,
script_url : String,
state_json : String,
ssr_content : String,
) -> String {
let config = ShardConfig::new(id, script_url)
.with_state(state_json)
.with_ssr_content(ssr_content)
generate_shard(config).html
}
///|
/// Generate standalone shard (with loader)
pub fn generate_standalone_shard(
id : String,
script_url : String,
loader_url : String,
state_json : String,
ssr_content : String,
) -> String {
let config = ShardConfig::new(id, script_url)
.with_loader(loader_url)
.with_state(state_json)
.with_ssr_content(ssr_content)
generate_shard(config).html
}
///|
/// Generate shard with visible trigger
pub fn generate_lazy_shard(
id : String,
script_url : String,
state_json : String,
ssr_content : String,
) -> String {
let config = ShardConfig::new(id, script_url)
.with_trigger(Visible)
.with_state(state_json)
.with_ssr_content(ssr_content)
generate_shard(config).html
}
///|
/// Generate multiple shards that share a single loader
pub fn generate_shard_group(
loader_url : String,
configs : Array[ShardConfig],
) -> String {
let sb = StringBuilder(size_hint=1024)
// Single loader for all
sb.write_string("\n")
// Each shard without its own loader
for config in configs {
let no_loader_config = { ..config, include_loader: false, }
let output = generate_shard(no_loader_config)
sb.write_string(output.html)
sb.write_char('\n')
}
sb.to_string()
}
///|
/// Estimate the recommended state format based on JSON size
pub fn auto_select_state_format(
json : String,
base_config : ShardConfig,
) -> ShardConfig {
let size = json.length()
if size < 1024 {
// Small: inline in attribute
{ ..base_config, state: Inline(json), }
} else if size < 102400 {
// Medium: script reference (caller should also call generate_state_script)
let script_id = "luna-state-" + base_config.id
{ ..base_config, state: ScriptRef(script_id), }
} else {
// Large: would need URL, but we can't generate that here
// Default to script reference with a warning
let script_id = "luna-state-" + base_config.id
{ ..base_config, state: ScriptRef(script_id), }
}
}