///|
/// A structured outline for generated brand guides and documentation consumers.
pub struct GuideSection {
id : String
title : String
summary : String
body : String
order : Int
} derive(Debug, Eq)
///|
pub fn GuideSection::new(
id : String,
title : String,
summary : String,
body : String,
order : Int,
) -> GuideSection {
{ id, title, summary, body, order }
}
///|
pub fn GuideSection::anchor(self : GuideSection) -> String {
normalize_identifier(self.id)
}
///|
pub fn GuideSection::is_empty(self : GuideSection) -> Bool {
self.body.trim().to_owned() == ""
}
///|
pub fn GuideSection::to_markdown(self : GuideSection) -> String {
"## " + self.title + "\n\n" + self.summary + "\n\n" + self.body
}
///|
pub fn GuideSection::to_json(self : GuideSection) -> String {
"{\"id\":\"" +
json_escape(self.id) +
"\",\"title\":\"" +
json_escape(self.title) +
"\",\"summary\":\"" +
json_escape(self.summary) +
"\",\"order\":" +
self.order.to_string() +
"}"
}
///|
pub struct GuideOutline {
title : String
sections : Array[GuideSection]
} derive(Debug, Eq)
///|
pub fn GuideOutline::for_brand_book(book : BrandBook) -> GuideOutline {
{
title: book.name,
sections: [
GuideSection::new(
"metadata",
"Positioning",
book.tagline,
book.render_metadata_panel(),
10,
),
GuideSection::new(
"voice",
"Voice",
book.voice.tone,
book.render_voice_section(),
20,
),
GuideSection::new(
"logo",
"Logo",
book.assets.imagery_style,
book.render_logo_section(),
30,
),
GuideSection::new(
"color",
"Color",
"Semantic palette and gradients",
book.render_color_section(),
40,
),
GuideSection::new(
"type",
"Typography",
"Readable type scale",
book.render_typography_section(),
50,
),
GuideSection::new(
"components",
"Components",
"Reusable component contracts",
book.render_component_section(),
60,
),
GuideSection::new(
"misuse",
"Misuse",
"Guardrails for common mistakes",
book.render_misuse_section(),
70,
),
],
}
}
///|
pub fn GuideOutline::empty(title : String) -> GuideOutline {
{ title, sections: [] }
}
///|
pub fn GuideOutline::add(
self : GuideOutline,
section : GuideSection,
) -> GuideOutline {
let sections = self.sections
sections.push(section)
{ ..self, sections, }
}
///|
pub fn GuideOutline::len(self : GuideOutline) -> Int {
self.sections.length()
}
///|
pub fn GuideOutline::ids(self : GuideOutline) -> Array[String] {
self.sections.map(fn(section) { section.id })
}
///|
pub fn GuideOutline::find(self : GuideOutline, id : String) -> GuideSection? {
for section in self.sections {
if normalize_identifier(section.id) == normalize_identifier(id) {
return Some(section)
}
}
None
}
///|
pub fn GuideOutline::non_empty(self : GuideOutline) -> Array[GuideSection] {
self.sections.filter(fn(section) { !section.is_empty() })
}
///|
pub fn GuideOutline::to_markdown(self : GuideOutline) -> String {
let lines : Array[String] = ["# " + self.title, ""]
for section in self.sections {
if !section.is_empty() {
lines.push(section.to_markdown())
lines.push("")
}
}
lines.join("\n")
}
///|
pub fn GuideOutline::to_toc(self : GuideOutline) -> String {
let lines : Array[String] = ["## Contents", ""]
for section in self.sections {
lines.push("- [" + section.title + "](#" + section.anchor() + ")")
}
lines.join("\n")
}
///|
pub fn GuideOutline::to_json(self : GuideOutline) -> String {
"{\"title\":\"" +
json_escape(self.title) +
"\",\"sections\":[" +
self.sections.map(fn(section) { section.to_json() }).join(",") +
"]}"
}
///|
pub fn BrandBook::guide_outline(self : BrandBook) -> GuideOutline {
GuideOutline::for_brand_book(self)
}
///|
pub fn BrandBook::guide_toc(self : BrandBook) -> String {
self.guide_outline().to_toc()
}