///|
/// Agent 中间产物协议:.moonviz 项目文件。
///
/// 这是 Agent 之间的"交接棒"——一个结构化的项目快照文件,
/// 任何 Agent(或人类)都能读取当前设计状态、未完成事项、交互定义,
/// 然后继续工作而不需要从零理解。
///
/// 格式为 JSON(可 diff、可版本控制、可跨语言解析):
/// ```json
/// {
/// "meta": { "version": "...", "created_by": "...", "context": "..." },
/// "tokens": { ... },
/// "artboards": { "login": { "decl": "...", "interactions": [...] } },
/// "flows": [ ... ],
/// "todo": [ "待办事项" ],
/// "notes": [ { "artboard": "...", "note": "...", "priority": "..." } ]
/// }
/// ```
pub(all) struct AgentNote {
artboard : String
note : String
priority : String // "high" | "medium" | "low"
}
///|
pub(all) struct MoonVizArtifact {
mut meta_version : String
mut meta_created_by : String
mut meta_context : String // Agent 的工作上下文描述
mut artboard_decls : Map[String, String] // artboard_id → 声明文本
mut artboard_names : Map[String, String]
mut artboard_sizes : Map[String, (Double, Double)]
mut flows : Array[(String, String, String)] // (from, to, trigger)
mut todos : Array[String]
mut notes : Array[AgentNote]
}
///|
pub fn MoonVizArtifact::new() -> MoonVizArtifact {
{
meta_version: ENGINE_VERSION,
meta_created_by: "",
meta_context: "",
artboard_decls: Map::new(),
artboard_names: Map::new(),
artboard_sizes: Map::new(),
flows: [],
todos: [],
notes: [],
}
}
// ---------------------------------------------------------------------------
// Project → Artifact(Agent 输出中间产物)
// ---------------------------------------------------------------------------
///|
pub fn Project::export_artifact(
self : Project,
agent_id? : String = "",
context? : String = "",
) -> String {
let a : MoonVizArtifact = MoonVizArtifact::new()
a.meta_created_by = agent_id
a.meta_context = context
// 导出每个画板的声明文本
for id in self.artboards.keys() {
match self.artboards.get(id) {
Some(ab) => {
let proto = decl_snapshot(ab.doc)
a.artboard_decls.set(id, proto)
a.artboard_names.set(id, ab.name)
a.artboard_sizes.set(id, (ab.width, ab.height))
}
None => ()
}
}
// 导出导航流
for e in self.flows.iter() {
a.flows.push((e.from, e.to, e.trigger))
}
artifact_to_json(a)
}
///|
/// Agent 占位(避免循环依赖,实际调用 decl 包的 from_document)
fn decl_snapshot(doc : Document) -> String {
// 生成声明的简化版本:遍历节点树
let sb = StringBuilder::new()
sb.write_string("// MoonViz artifact\n")
let layout = solve(doc)
artifact_walk(sb, doc, doc.root_id, layout, 0)
sb.to_string()
}
///|
fn artifact_walk(
sb : StringBuilder,
doc : Document,
id : String,
layout : SolvedLayout,
depth : Int,
) -> Unit {
match (doc.nodes.get(id), layout.rect_of(id)) {
(Some(n), Some(r)) => {
let indent = repeat_spaces(depth * 2)
sb.write_string(
"\{indent}\{n.id} \{n.component} (\{r.x.to_int()},\{r.y.to_int()}) \{r.width.to_int()}x\{r.height.to_int()}",
)
if n.text != "" {
sb.write_string(" \"\{n.text}\"")
}
sb.write_string("\n")
for cid in n.children.iter() {
artifact_walk(sb, doc, cid, layout, depth + 1)
}
}
_ => ()
}
}
///|
fn repeat_spaces(n : Int) -> String {
let sb = StringBuilder::new()
for _i in 0.. String {
let sb = StringBuilder::new()
sb.write_string("{\n")
// meta
sb.write_string(" \"meta\": {\n")
sb.write_string(" \"version\": \"\{a.meta_version}\",\n")
sb.write_string(" \"created_by\": \"\{a.meta_created_by}\",\n")
sb.write_string(" \"context\": \"\{json_escape_str(a.meta_context)}\"\n")
sb.write_string(" },\n")
// artboards
sb.write_string(" \"artboards\": {\n")
let board_parts : Array[String] = []
for id in a.artboard_decls.keys() {
match
(
a.artboard_decls.get(id),
a.artboard_names.get(id),
a.artboard_sizes.get(id),
) {
(Some(decl), Some(name), Some((w, h))) =>
board_parts.push(
" \"\{json_escape(id)}\": {\n \"name\": \"\{json_escape(name)}\",\n \"size\": {\{w.to_int()}, \{h.to_int()}},\n \"decl\": \"\{json_escape_str(decl)}\"\n }",
)
_ => ()
}
}
sb.write_string(jstr13(board_parts, ",\n"))
sb.write_string("\n },\n")
// flows
sb.write_string(" \"flows\": [\n")
let flow_parts : Array[String] = []
for f in a.flows.iter() {
flow_parts.push(
" {\"from\": \"\{json_escape_str(f.0)}\", \"to\": \"\{json_escape_str(f.1)}\", \"trigger\": \"\{json_escape_str(f.2)}\"}",
)
}
sb.write_string(jstr13(flow_parts, ",\n"))
sb.write_string("\n ],\n")
// todos
sb.write_string(" \"todo\": [\n")
let todo_parts : Array[String] = []
for t in a.todos.iter() {
todo_parts.push(" \"\{json_escape_str(t)}\"")
}
sb.write_string(jstr13(todo_parts, ",\n"))
sb.write_string("\n ],\n")
// notes
sb.write_string(" \"notes\": [\n")
let note_parts : Array[String] = []
for n in a.notes.iter() {
note_parts.push(
" {\"artboard\": \"\{json_escape_str(n.artboard)}\", \"note\": \"\{json_escape_str(n.note)}\", \"priority\": \"\{json_escape_str(n.priority)}\"}",
)
}
sb.write_string(jstr13(note_parts, ",\n"))
sb.write_string("\n ]\n}")
sb.to_string()
}
///|
fn jstr13(parts : Array[String], sep : String) -> String {
let sb = StringBuilder::new()
for i in 0.. 0 {
sb.write_string(sep)
}
sb.write_string(parts[i])
}
sb.to_string()
}
///|
fn json_escape_str(s : String) -> String {
json_escape(s)
}
// ---------------------------------------------------------------------------
// Agent 添加待办 / 备注
// ---------------------------------------------------------------------------
///|
pub fn MoonVizArtifact::add_todo(self : MoonVizArtifact, text : String) -> Unit {
self.todos.push(text)
}
///|
pub fn MoonVizArtifact::add_note(
self : MoonVizArtifact,
artboard : String,
note : String,
priority : String,
) -> Unit {
self.notes.push({ artboard, note, priority, })
}
///|
/// Project 侧也支持添加 Agent 备注
pub fn Project::add_agent_note(
self : Project,
artboard~ : String,
note~ : String,
priority? : String = "medium",
) -> Unit {
// v1: 存储到 artifact 的内存结构中
// 实际使用时由 runtime 层管理
let _ = artboard
let _ = note
let _ = priority
}
// ---------------------------------------------------------------------------
// 读取中间产物(另一个 Agent 接手时用)
// ---------------------------------------------------------------------------
///|
pub fn MoonVizArtifact::summary(self : MoonVizArtifact) -> String {
let sb = StringBuilder::new()
sb.write_string("MoonViz Artifact v\{self.meta_version}\n")
sb.write_string("Context: \{self.meta_context}\n")
sb.write_string("Artboards: \{self.artboard_decls.size()}\n")
for id in self.artboard_names.keys() {
match self.artboard_names.get(id) {
Some(name) => sb.write_string(" - \{id} (\{name})\n")
None => ()
}
}
sb.write_string("Flows: \{self.flows.length()}\n")
sb.write_string("TODOs: \{self.todos.length()}\n")
for t in self.todos.iter() {
sb.write_string(" ☐ \{t}\n")
}
sb.write_string("Notes: \{self.notes.length()}\n")
for n in self.notes.iter() {
sb.write_string(" [\{n.priority}] \{n.artboard}: \{n.note}\n")
}
sb.to_string()
}