diff --git a/apps/ios/EdgeEver/Features/Workspace/MemoDetailView.swift b/apps/ios/EdgeEver/Features/Workspace/MemoDetailView.swift
index 1dbfc9d6..15a4f06b 100644
--- a/apps/ios/EdgeEver/Features/Workspace/MemoDetailView.swift
+++ b/apps/ios/EdgeEver/Features/Workspace/MemoDetailView.swift
@@ -20,9 +20,11 @@ struct MemoDetailView: View {
@State private var imageExportBuffer = MemoImageExportBuffer()
@State private var imageShareOptionsOpen = false
@State private var imageShareFormat = "png"
- @State private var imageShareBackground = "slate"
- @State private var imageShareNotebook = true
- @State private var imageShareTags = true
+ @State private var imageShareTheme = "slate"
+ @State private var imageShareFontStyle = "serif"
+ @State private var imageShareTitle = true
+ @State private var imageShareNotebook = false
+ @State private var imageShareTags = false
@State private var imageShareUpdatedAt = true
@State private var imageShareBranding = true
@State private var error: String?
@@ -130,15 +132,29 @@ struct MemoDetailView: View {
.sheet(isPresented: $imageShareOptionsOpen) {
NavigationStack {
Form {
- Section(env.preferences.t("背景", en: "Background")) {
- Picker(env.preferences.t("背景", en: "Background"), selection: $imageShareBackground) {
- Text(env.preferences.t("简洁", en: "Clean")).tag("slate")
+ Section(env.preferences.t("主题风格", en: "Theme")) {
+ Picker(env.preferences.t("主题风格", en: "Theme"), selection: $imageShareTheme) {
+ Text(env.preferences.t("经典浅色", en: "Light")).tag("slate")
+ Text(env.preferences.t("极光渐变", en: "Aurora")).tag("aurora")
+ Text(env.preferences.t("暮色晚霞", en: "Sunset")).tag("sunset")
+ Text(env.preferences.t("暗夜曜石", en: "Midnight")).tag("midnight")
Text(env.preferences.t("薄荷", en: "Mint")).tag("mint")
- Text(env.preferences.t("暖色", en: "Warm")).tag("warm")
+ Text(env.preferences.t("紫雾流光", en: "Lavender")).tag("lavender")
+ Text(env.preferences.t("经典便签", en: "Notepad")).tag("notepad")
+ Text(env.preferences.t("水墨宣纸", en: "Rice Paper")).tag("xuan")
+ }
+ .pickerStyle(.menu)
+ }
+ Section(env.preferences.t("字体风格", en: "Typography")) {
+ Picker(env.preferences.t("字体风格", en: "Typography"), selection: $imageShareFontStyle) {
+ Text(env.preferences.t("文艺衬线", en: "Serif")).tag("serif")
+ Text(env.preferences.t("现代无衬线", en: "Sans")).tag("sans")
+ Text(env.preferences.t("极客等宽", en: "Mono")).tag("mono")
}
.pickerStyle(.segmented)
}
- Section(env.preferences.t("笔记信息", en: "Note information")) {
+ Section(env.preferences.t("显示内容", en: "Content elements")) {
+ Toggle(env.preferences.t("笔记标题", en: "Note title"), isOn: $imageShareTitle)
Toggle(env.preferences.t("笔记本", en: "Notebook"), isOn: $imageShareNotebook)
Toggle(env.preferences.t("标签", en: "Tags"), isOn: $imageShareTags)
Toggle(env.preferences.t("更新时间", en: "Updated time"), isOn: $imageShareUpdatedAt)
@@ -146,8 +162,8 @@ struct MemoDetailView: View {
}
Section(env.preferences.t("图片格式", en: "Image format")) {
Picker(env.preferences.t("图片格式", en: "Image format"), selection: $imageShareFormat) {
- Text(env.preferences.t("PNG · 文字更清晰", en: "PNG · Best for text")).tag("png")
- Text(env.preferences.t("JPEG · 文件更小", en: "JPEG · Smaller file")).tag("jpeg")
+ Text(env.preferences.t("PNG · 超清无损", en: "PNG · Best for text")).tag("png")
+ Text(env.preferences.t("JPEG · 体积小", en: "JPEG · Smaller file")).tag("jpeg")
}
.pickerStyle(.segmented)
}
@@ -165,7 +181,9 @@ struct MemoDetailView: View {
exportMemoImage(
memo,
format: imageShareFormat,
- background: imageShareBackground,
+ theme: imageShareTheme,
+ fontStyle: imageShareFontStyle,
+ showTitle: imageShareTitle,
showNotebook: imageShareNotebook,
showTags: imageShareTags,
showUpdatedAt: imageShareUpdatedAt,
@@ -923,11 +941,13 @@ struct MemoDetailView: View {
private func exportMemoImage(
_ memo: MemoDetail,
format: String,
- background: String = "slate",
- showNotebook: Bool = true,
- showTags: Bool = true,
+ theme: String = "slate",
+ fontStyle: String = "serif",
+ showTitle: Bool = true,
+ showNotebook: Bool = false,
+ showTags: Bool = false,
showUpdatedAt: Bool = true,
- showBranding: Bool = false
+ showBranding: Bool = true
) {
guard !imageExporting, bodyReady else { return }
let requestId = UUID().uuidString
@@ -942,7 +962,12 @@ struct MemoDetailView: View {
"notebook": showNotebook ? notebookName(for: memo) : "",
"tags": showTags ? memo.tags : [],
"updatedAt": showUpdatedAt ? memo.updatedAt : "",
- "background": background,
+ "theme": theme,
+ "fontStyle": fontStyle,
+ "showTitle": showTitle,
+ "showNotebook": showNotebook,
+ "showTags": showTags,
+ "showUpdatedAt": showUpdatedAt,
"branding": showBranding,
])
}
diff --git a/apps/ios/EdgeEver/Resources/EditorBundle/index.html b/apps/ios/EdgeEver/Resources/EditorBundle/index.html
index 75859501..faab5462 100644
--- a/apps/ios/EdgeEver/Resources/EditorBundle/index.html
+++ b/apps/ios/EdgeEver/Resources/EditorBundle/index.html
@@ -7,38 +7,38 @@
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
EdgeEver Editor
-
+`},"getStyles"),Dx,zl=(Dx=class{constructor(){this.d=""}moveTo(e,r){return this.d+=`M ${e} ${r} `,this}lineTo(e,r){return this.d+=`L ${e} ${r} `,this}horizontalTo(e){return this.d+=`H ${e} `,this}verticalTo(e){return this.d+=`V ${e} `,this}arcTo(e,r,n,i,a,s,o){return this.d+=`A ${e} ${r} ${n} ${i?1:0} ${a?1:0} ${s} ${o} `,this}build(){return this.d.trim()}},T(Dx,"PathBuilder"),Dx),Nx,Jqt=(Nx=class{constructor(e,r=SK()){this.textCache=new Map,this.svg=e,this.config=r}measureText(e){if(this.textCache.has(e))return this.textCache.get(e);const r=this.svg.append("text").attr("font-family",this.config.fontFamily).attr("font-size",this.config.fontSize).text(e),n=r.node().getBBox(),i={width:n.width,height:n.height};return r.remove(),this.textCache.set(e,i),i}renderTerminal(e,r){const n=this.measureText(r),i=n.width+this.config.padding*2,a=n.height+this.config.padding*2,s=e.append("g").attr("class","railroad-terminal");return s.append("rect").attr("x",0).attr("y",0).attr("width",i).attr("height",a).attr("rx",10).attr("ry",10),s.append("text").attr("x",i/2).attr("y",a/2).text(r),{element:s.node(),dimensions:{width:i,height:a,up:a/2,down:a/2}}}renderNonTerminal(e,r){const n=this.measureText(r),i=n.width+this.config.padding*2,a=n.height+this.config.padding*2,s=e.append("g").attr("class","railroad-nonterminal");return s.append("rect").attr("x",0).attr("y",0).attr("width",i).attr("height",a),s.append("text").attr("x",i/2).attr("y",a/2).text(r),{element:s.node(),dimensions:{width:i,height:a,up:a/2,down:a/2}}}renderSequence(e,r){const n=r.map(u=>this.renderExpression(e,u));let i=0,a=0,s=0;for(const u of n)i+=u.dimensions.width,a=Math.max(a,u.dimensions.up),s=Math.max(s,u.dimensions.down);i+=(n.length-1)*this.config.horizontalSeparation;const o=e.append("g").attr("class","railroad-sequence");let l=0;for(let u=0;uthis.renderExpression(e,f));let i=0,a=0;for(const f of n)i=Math.max(i,f.dimensions.width),a+=f.dimensions.height;a+=(n.length-1)*this.config.verticalSeparation;const s=this.config.arcRadius,o=s*4,l=i+o,u=e.append("g").attr("class","railroad-choice");let h=0;const d=a/2;for(const f of n){const p=h,g=p+f.dimensions.up,m=s*2+(i-f.dimensions.width)/2;u.node().appendChild(f.element).setAttribute("transform",`translate(${m}, ${p})`);const v=new zl,x=g>d;g===d?v.moveTo(0,d).lineTo(m,g):v.moveTo(0,d).arcTo(s,s,0,!1,x,s,d+(x?s:-s)).lineTo(s,g-(x?s:-s)).arcTo(s,s,0,!1,!x,s*2,g).lineTo(m,g),u.append("path").attr("class","railroad-line").attr("d",v.build());const b=new zl,w=m+f.dimensions.width,k=l-s*2;g===d?b.moveTo(w,g).lineTo(l,d):b.moveTo(w,g).lineTo(k,g).arcTo(s,s,0,!1,!x,l-s,g+(x?-s:s)).lineTo(l-s,d+(x?s:-s)).arcTo(s,s,0,!1,x,l,d),u.append("path").attr("class","railroad-line").attr("d",b.build()),h+=f.dimensions.height+this.config.verticalSeparation}return{element:u.node(),dimensions:{width:l,height:a,up:d,down:a-d}}}renderOptional(e,r){const n=this.renderExpression(e,r),i=this.config.arcRadius,a=i*2,s=n.dimensions.width+i*4,o=n.dimensions.height+a,l=e.append("g").attr("class","railroad-optional"),u=i*2,h=a;l.node().appendChild(n.element).setAttribute("transform",`translate(${u}, ${h})`);const f=h+n.dimensions.up,p=new zl().moveTo(0,f).lineTo(i*2,f);l.append("path").attr("class","railroad-line").attr("d",p.build());const g=new zl().moveTo(u+n.dimensions.width,f).lineTo(s,f);l.append("path").attr("class","railroad-line").attr("d",g.build());const m=new zl().moveTo(0,f).arcTo(i,i,0,!1,!1,i,f-i).lineTo(i,i).arcTo(i,i,0,!1,!0,i*2,0).lineTo(s-i*2,0).arcTo(i,i,0,!1,!0,s-i,i).lineTo(s-i,f-i).arcTo(i,i,0,!1,!1,s,f);return l.append("path").attr("class","railroad-line").attr("d",m.build()),{element:l.node(),dimensions:{width:s,height:o,up:f,down:o-f}}}renderRepetition(e,r,n){const i=this.renderExpression(e,r),a=this.config.arcRadius,s=a*2,o=i.dimensions.width+a*4,l=n===0,u=i.dimensions.height+s+(l?s:0),h=e.append("g").attr("class","railroad-repetition"),d=a*2,f=l?s:0;h.node().appendChild(i.element).setAttribute("transform",`translate(${d}, ${f})`);const g=f+i.dimensions.up;h.append("path").attr("class","railroad-line").attr("d",new zl().moveTo(0,g).lineTo(a*2,g).build()),h.append("path").attr("class","railroad-line").attr("d",new zl().moveTo(d+i.dimensions.width,g).lineTo(o,g).build());const m=f+i.dimensions.height+a,y=new zl().moveTo(d+i.dimensions.width,g).arcTo(a,a,0,!1,!0,d+i.dimensions.width+a,g+a).lineTo(d+i.dimensions.width+a,m).arcTo(a,a,0,!1,!0,d+i.dimensions.width,m+a).lineTo(a*2,m+a).arcTo(a,a,0,!1,!0,a,m).lineTo(a,g+a).arcTo(a,a,0,!1,!0,a*2,g);if(h.append("path").attr("class","railroad-line").attr("d",y.build()),l){const v=new zl().moveTo(0,g).arcTo(a,a,0,!1,!1,a,g-a).lineTo(a,a).arcTo(a,a,0,!1,!0,a*2,0).lineTo(o-a*2,0).arcTo(a,a,0,!1,!0,o-a,a).lineTo(o-a,g-a).arcTo(a,a,0,!1,!1,o,g);h.append("path").attr("class","railroad-line").attr("d",v.build())}return{element:h.node(),dimensions:{width:o,height:u,up:g,down:u-g}}}renderSpecial(e,r){const n=this.measureText("? "+r+" ?"),i=n.width+this.config.padding*2,a=n.height+this.config.padding*2,s=e.append("g").attr("class","railroad-special");return s.append("rect").attr("x",0).attr("y",0).attr("width",i).attr("height",a),s.append("text").attr("x",i/2).attr("y",a/2).text("? "+r+" ?"),{element:s.node(),dimensions:{width:i,height:a,up:a/2,down:a/2}}}renderExpression(e,r){switch(r.type){case"terminal":return this.renderTerminal(e,r.value);case"nonterminal":return this.renderNonTerminal(e,r.name);case"sequence":return this.renderSequence(e,r.elements);case"choice":return this.renderChoice(e,r.alternatives);case"optional":return this.renderOptional(e,r.element);case"repetition":return this.renderRepetition(e,r.element,r.min);case"special":return this.renderSpecial(e,r.text);default:throw new Error(`Unknown node type: ${r.type}`)}}renderRule(e,r){const n=this.svg.append("g").attr("class","railroad-rule").attr("transform",`translate(0, ${r})`),i=e.name+" =",a=this.measureText(i).width+20,s=a+20,o=n.append("g"),l=this.renderExpression(o,e.definition),u=Math.max(20,l.dimensions.up),h=u-l.dimensions.up;return o.attr("transform",`translate(${s}, ${h})`),n.append("g").attr("class","railroad-rule-name-group").append("text").attr("class","railroad-rule-name").attr("x",0).attr("y",u).text(i),n.append("g").attr("class","railroad-start").append("circle").attr("cx",a).attr("cy",u).attr("r",this.config.markerRadius),n.append("g").attr("class","railroad-end").append("circle").attr("cx",s+l.dimensions.width+10).attr("cy",u).attr("r",this.config.markerRadius),n.append("path").attr("class","railroad-line").attr("d",new zl().moveTo(a+this.config.markerRadius,u).lineTo(s,u).build()),n.append("path").attr("class","railroad-line").attr("d",new zl().moveTo(s+l.dimensions.width,u).lineTo(s+l.dimensions.width+10-this.config.markerRadius,u).build()),{height:Math.max(40,h+l.dimensions.height+this.config.padding*2),width:s+l.dimensions.width+10+this.config.markerRadius}}renderDiagram(e){let r=this.config.padding,n=0;for(const i of e){const a=this.renderRule(i,r);r+=a.height+this.config.verticalSeparation,n=Math.max(n,a.width)}return{width:n+this.config.padding*2,height:r+this.config.padding}}},T(Nx,"RailroadRenderer"),Nx),Ice=T((t,e,r)=>{ta(t,e.height,e.width,r),t.attr("viewBox",`0 0 ${e.width} ${e.height}`)},"configureRailroadSvgSize"),eWt=T((t,e,r)=>{he.debug(`[Railroad] Rendering diagram
+`+t);try{const n=Ks(e);n.attr("class","railroad-diagram");const i=lr().railroad,a=(i==null?void 0:i.useMaxWidth)??!0,s=Ci.getRules();if(he.debug(`[Railroad] Rendering ${s.length} rules`),s.length===0){he.warn("[Railroad] No rules to render"),Ice(n,{height:100,width:200},a);return}const l=new Jqt(n,SK()).renderDiagram(s);Ice(n,l,a),he.debug("[Railroad] Render complete")}catch(n){throw he.error("[Railroad] Render error:",n),n}},"draw"),oM={draw:eWt},tWt=wj().Railroad.parser.LangiumParser,oy=T(t=>{switch(t.$type){case"RailroadTerminalExpr":return{type:"terminal",value:t.value};case"RailroadNonTerminalExpr":return{type:"nonterminal",name:t.name};case"RailroadSpecialExpr":return{type:"special",text:t.text};case"RailroadSequenceExpr":{const e=t.elements.map(oy);return e.length===1?e[0]:{type:"sequence",elements:e}}case"RailroadChoiceExpr":{const e=t.alternatives.map(oy);return e.length===1?e[0]:{type:"choice",alternatives:e}}case"RailroadOptionalExpr":return{type:"optional",element:oy(t.element)};case"RailroadOneOrMoreExpr":return{type:"repetition",element:oy(t.element),min:1,max:1/0};case"RailroadZeroOrMoreExpr":return{type:"repetition",element:oy(t.element),min:0,max:1/0};default:throw new Error(`Unsupported railroad expression: ${t.$type}`)}},"transformExpression"),rWt=T(t=>({name:t.name,definition:oy(t.definition)}),"transformRule"),nWt=T(t=>{No(t,Ci),t.title&&Ci.setTitle(t.title),t.rules.map(e=>Ci.addRule(rWt(e)))},"populateDb"),iWt={parse:T(t=>{Ci.clear(),he.debug("[Railroad Parser] Starting Langium parse");const e=tWt.parse(t);if(e.lexerErrors.length>0||e.parserErrors.length>0)throw new EC(e);const r=e.value;he.debug("[Railroad Parser] Parsed rules:",r.rules.length),nWt(r),he.debug("[Railroad Parser] Parse complete")},"parse"),parser:{yy:Ci}},aWt={parser:iWt,db:Ci,renderer:oM,styles:sM};const sWt=Object.freeze(Object.defineProperty({__proto__:null,diagram:aWt},Symbol.toStringTag,{value:"Module"}));var oWt=kj().RailroadEbnf.parser.LangiumParser,eE=T(t=>{const e=t.alternatives.map(lWt);return e.length===1?e[0]:{type:"choice",alternatives:e}},"transformChoice"),lWt=T(t=>{const e=t.elements.map(uWt);return e.length===1?e[0]:{type:"sequence",elements:e}},"transformSequence"),FDe=T(t=>{switch(t.$type){case"EbnfTerminal":return{type:"terminal",value:t.value};case"EbnfNonTerminal":return{type:"nonterminal",name:t.name};case"EbnfSpecial":return{type:"special",text:t.text};case"EbnfGroup":return eE(t.element);case"EbnfOptional":return{type:"optional",element:eE(t.element)};case"EbnfRepetition":return{type:"repetition",element:eE(t.element),min:0,max:1/0};default:throw new Error(`Unsupported EBNF primary node: ${t.$type}`)}},"transformPrimary"),cWt=T((t,e)=>{switch(e.$type){case"EbnfOptionalPostfix":return{type:"optional",element:t};case"EbnfZeroOrMorePostfix":return{type:"repetition",element:t,min:0,max:1/0};case"EbnfOneOrMorePostfix":return{type:"repetition",element:t,min:1,max:1/0};case"EbnfExceptionPostfix":return{type:"sequence",elements:[t,{type:"terminal",value:"-"},FDe(e.except)]};default:throw new Error(`Unsupported EBNF postfix node: ${e.$type}`)}},"transformPostfix"),uWt=T(t=>t.postfixes.reduce((e,r)=>cWt(e,r),FDe(t.base)),"transformTerm"),hWt=T(t=>({name:t.name,definition:eE(t.definition)}),"transformRule"),dWt=T(t=>{No(t,Ci),t.title&&Ci.setTitle(t.title),t.rules.map(e=>Ci.addRule(hWt(e)))},"populateDb"),fWt={parse:T(t=>{Ci.clear(),he.debug("[EBNF Parser] Starting Langium parse");const e=oWt.parse(t);if(e.lexerErrors.length>0||e.parserErrors.length>0)throw new EC(e);const r=e.value;he.debug("[EBNF Parser] Parsed rules:",r.rules.length),dWt(r),he.debug("[EBNF Parser] Parse complete")},"parse"),parser:{yy:Ci}},pWt={parser:fWt,db:Ci,renderer:oM,styles:sM};const gWt=Object.freeze(Object.defineProperty({__proto__:null,diagram:pWt},Symbol.toStringTag,{value:"Module"}));var mWt=Cj().RailroadAbnf.parser.LangiumParser,hV=T(t=>{const e=t.alternatives.map(yWt);return e.length===1?e[0]:{type:"choice",alternatives:e}},"transformAlternation"),yWt=T(t=>{const e=t.elements.map(bWt);return e.length===1?e[0]:{type:"sequence",elements:e}},"transformConcatenation"),vWt=T(t=>{if(t.includes("*")){const[r,n]=t.split("*"),i=r?parseInt(r,10):0,a=n?parseInt(n,10):1/0;return{min:i,max:a}}const e=parseInt(t,10);return{min:e,max:e}},"parseRepeat"),bWt=T(t=>{const e=xWt(t.primary);if(!t.repeat)return e;const{min:r,max:n}=vWt(t.repeat);return r===0&&n===1?{type:"optional",element:e}:{type:"repetition",element:e,min:r,max:n}},"transformElement"),xWt=T(t=>{switch(t.$type){case"AbnfStringLiteral":return{type:"terminal",value:t.value};case"AbnfNumVal":return{type:"terminal",value:t.value};case"AbnfRuleName":return{type:"nonterminal",name:t.name};case"AbnfGroup":return hV(t.element);case"AbnfOptionalGroup":return{type:"optional",element:hV(t.element)};default:throw new Error(`Unsupported ABNF primary node: ${t.$type}`)}},"transformPrimary"),TWt=T(t=>({name:t.name,definition:hV(t.definition)}),"transformRule"),wWt=T(t=>{No(t,Ci),t.title&&Ci.setTitle(t.title),t.rules.map(e=>Ci.addRule(TWt(e)))},"populateDb"),kWt={parse:T(t=>{Ci.clear(),he.debug("[ABNF Parser] Starting Langium parse");const e=mWt.parse(t);if(e.lexerErrors.length>0||e.parserErrors.length>0)throw new EC(e);const r=e.value;he.debug("[ABNF Parser] Parsed rules:",r.rules.length),wWt(r),he.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:Ci}},CWt={parser:kWt,db:Ci,renderer:oM,styles:sM};const SWt=Object.freeze(Object.defineProperty({__proto__:null,diagram:CWt},Symbol.toStringTag,{value:"Module"}));var EWt=Sj().RailroadPeg.parser.LangiumParser,zDe=T(t=>{const e=t.alternatives.map(AWt);return e.length===1?e[0]:{type:"choice",alternatives:e}},"transformOrderedChoice"),AWt=T(t=>{const e=t.elements.map(_Wt);return e.length===1?e[0]:{type:"sequence",elements:e}},"transformSequence"),_Wt=T(t=>{const e=RWt(t.suffix);return t.operator?{type:"special",text:t.operator==="&"?`&${Dce(e)}`:`!${Dce(e)}`}:e},"transformPrefix"),Dce=T(t=>{switch(t.type){case"terminal":return`"${t.value}"`;case"nonterminal":return t.name;case"special":return t.text;default:return"(...)"}},"nodeToLabel"),RWt=T(t=>{const e=LWt(t.primary);if(!t.operator)return e;switch(t.operator){case"?":return{type:"optional",element:e};case"*":return{type:"repetition",element:e,min:0,max:1/0};case"+":return{type:"repetition",element:e,min:1,max:1/0};default:throw new Error(`Unsupported PEG suffix operator: ${t.operator}`)}},"transformSuffix"),LWt=T(t=>{switch(t.$type){case"PegLiteral":return{type:"terminal",value:t.value};case"PegIdentifier":return{type:"nonterminal",name:t.name};case"PegGroup":return zDe(t.element);case"PegAny":return{type:"special",text:t.dot};default:throw new Error(`Unsupported PEG primary node: ${t.$type}`)}},"transformPrimary"),MWt=T(t=>({name:t.name,definition:zDe(t.definition)}),"transformRule"),IWt=T(t=>{No(t,Ci),t.title&&Ci.setTitle(t.title),t.rules.map(e=>Ci.addRule(MWt(e)))},"populateDb"),DWt={parse:T(t=>{Ci.clear(),he.debug("[PEG Parser] Starting Langium parse");const e=EWt.parse(t);if(e.lexerErrors.length>0||e.parserErrors.length>0)throw new EC(e);const r=e.value;he.debug("[PEG Parser] Parsed rules:",r.rules.length),IWt(r),he.debug("[PEG Parser] Parse complete")},"parse"),parser:{yy:Ci}},NWt={parser:DWt,db:Ci,renderer:oM,styles:sM};const OWt=Object.freeze(Object.defineProperty({__proto__:null,diagram:NWt},Symbol.toStringTag,{value:"Module"}));var $Wt=1;function EK(){if(!(typeof globalThis>"u"))return globalThis}T(EK,"getCaptureGlobal");function HDe(){var t;return!!((t=EK())!=null&&t.mermaidCaptureSizes)}T(HDe,"shouldCaptureSizes");function VDe(){return typeof location>"u"?"browser-dev":`${location.pathname}${location.search}`}T(VDe,"capturedFromLocation");function GDe(t,e){const r=EK();if(!r)return;const n=e.node(),i=(n&&"ownerSVGElement"in n?n.ownerSVGElement:null)??n,a=(i==null?void 0:i.id)??"(unknown)";r.mermaidCapturedSizes??(r.mermaidCapturedSizes=[]);const s={svgId:a,sizes:t};r.mermaidCapturedSizes.push(s),r.mermaidLastCapturedSizes=s}T(GDe,"emitCapturedSizes");function qDe(t,e){const r=[];for(const n of e.nodes)n.isGroup||r.push({id:n.id,width:n.width??0,height:n.height??0});r.length!==0&&GDe({metadata:{captureVersion:$Wt,capturedAt:new Date().toISOString(),capturedFrom:VDe()},nodes:r},t)}T(qDe,"captureNodeSizes");const PWt=Object.freeze(Object.defineProperty({__proto__:null,captureNodeSizes:qDe,shouldCaptureSizes:HDe},Symbol.toStringTag,{value:"Module"})),BWt=Object.freeze(Object.defineProperty({__proto__:null,InfoModule:iRe,createInfoServices:aRe},Symbol.toStringTag,{value:"Module"})),FWt=Object.freeze(Object.defineProperty({__proto__:null,PacketModule:sRe,createPacketServices:oRe},Symbol.toStringTag,{value:"Module"})),zWt=Object.freeze(Object.defineProperty({__proto__:null,PieModule:lRe,createPieServices:cRe},Symbol.toStringTag,{value:"Module"})),HWt=Object.freeze(Object.defineProperty({__proto__:null,TreeViewModule:uRe,createTreeViewServices:hRe},Symbol.toStringTag,{value:"Module"})),VWt=Object.freeze(Object.defineProperty({__proto__:null,ArchitectureModule:dRe,createArchitectureServices:fRe},Symbol.toStringTag,{value:"Module"})),GWt=Object.freeze(Object.defineProperty({__proto__:null,GitGraphModule:rRe,createGitGraphServices:nRe},Symbol.toStringTag,{value:"Module"})),qWt=Object.freeze(Object.defineProperty({__proto__:null,EventModelingModule:gRe,createEventModelingServices:mRe},Symbol.toStringTag,{value:"Module"})),WWt=Object.freeze(Object.defineProperty({__proto__:null,RadarModule:G_e,createRadarServices:q_e},Symbol.toStringTag,{value:"Module"})),UWt=Object.freeze(Object.defineProperty({__proto__:null,RailroadModule:W_e,createRailroadServices:wj},Symbol.toStringTag,{value:"Module"})),YWt=Object.freeze(Object.defineProperty({__proto__:null,RailroadEbnfModule:U_e,createRailroadEbnfServices:kj},Symbol.toStringTag,{value:"Module"})),XWt=Object.freeze(Object.defineProperty({__proto__:null,RailroadAbnfModule:Y_e,createRailroadAbnfServices:Cj},Symbol.toStringTag,{value:"Module"})),jWt=Object.freeze(Object.defineProperty({__proto__:null,RailroadPegModule:X_e,createRailroadPegServices:Sj},Symbol.toStringTag,{value:"Module"})),KWt=Object.freeze(Object.defineProperty({__proto__:null,TreemapModule:K_e,createTreemapServices:Q_e},Symbol.toStringTag,{value:"Module"})),QWt=Object.freeze(Object.defineProperty({__proto__:null,WardleyModule:Z_e,createWardleyServices:J_e},Symbol.toStringTag,{value:"Module"})),ZWt=Object.freeze(Object.defineProperty({__proto__:null,CynefinModule:eRe,createCynefinServices:tRe},Symbol.toStringTag,{value:"Module"}));
diff --git a/apps/ios/EditorBundle/index.html b/apps/ios/EditorBundle/index.html
index 75859501..faab5462 100644
--- a/apps/ios/EditorBundle/index.html
+++ b/apps/ios/EditorBundle/index.html
@@ -7,38 +7,38 @@
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
EdgeEver Editor
-
+`},"getStyles"),Dx,zl=(Dx=class{constructor(){this.d=""}moveTo(e,r){return this.d+=`M ${e} ${r} `,this}lineTo(e,r){return this.d+=`L ${e} ${r} `,this}horizontalTo(e){return this.d+=`H ${e} `,this}verticalTo(e){return this.d+=`V ${e} `,this}arcTo(e,r,n,i,a,s,o){return this.d+=`A ${e} ${r} ${n} ${i?1:0} ${a?1:0} ${s} ${o} `,this}build(){return this.d.trim()}},T(Dx,"PathBuilder"),Dx),Nx,Jqt=(Nx=class{constructor(e,r=SK()){this.textCache=new Map,this.svg=e,this.config=r}measureText(e){if(this.textCache.has(e))return this.textCache.get(e);const r=this.svg.append("text").attr("font-family",this.config.fontFamily).attr("font-size",this.config.fontSize).text(e),n=r.node().getBBox(),i={width:n.width,height:n.height};return r.remove(),this.textCache.set(e,i),i}renderTerminal(e,r){const n=this.measureText(r),i=n.width+this.config.padding*2,a=n.height+this.config.padding*2,s=e.append("g").attr("class","railroad-terminal");return s.append("rect").attr("x",0).attr("y",0).attr("width",i).attr("height",a).attr("rx",10).attr("ry",10),s.append("text").attr("x",i/2).attr("y",a/2).text(r),{element:s.node(),dimensions:{width:i,height:a,up:a/2,down:a/2}}}renderNonTerminal(e,r){const n=this.measureText(r),i=n.width+this.config.padding*2,a=n.height+this.config.padding*2,s=e.append("g").attr("class","railroad-nonterminal");return s.append("rect").attr("x",0).attr("y",0).attr("width",i).attr("height",a),s.append("text").attr("x",i/2).attr("y",a/2).text(r),{element:s.node(),dimensions:{width:i,height:a,up:a/2,down:a/2}}}renderSequence(e,r){const n=r.map(u=>this.renderExpression(e,u));let i=0,a=0,s=0;for(const u of n)i+=u.dimensions.width,a=Math.max(a,u.dimensions.up),s=Math.max(s,u.dimensions.down);i+=(n.length-1)*this.config.horizontalSeparation;const o=e.append("g").attr("class","railroad-sequence");let l=0;for(let u=0;uthis.renderExpression(e,f));let i=0,a=0;for(const f of n)i=Math.max(i,f.dimensions.width),a+=f.dimensions.height;a+=(n.length-1)*this.config.verticalSeparation;const s=this.config.arcRadius,o=s*4,l=i+o,u=e.append("g").attr("class","railroad-choice");let h=0;const d=a/2;for(const f of n){const p=h,g=p+f.dimensions.up,m=s*2+(i-f.dimensions.width)/2;u.node().appendChild(f.element).setAttribute("transform",`translate(${m}, ${p})`);const v=new zl,x=g>d;g===d?v.moveTo(0,d).lineTo(m,g):v.moveTo(0,d).arcTo(s,s,0,!1,x,s,d+(x?s:-s)).lineTo(s,g-(x?s:-s)).arcTo(s,s,0,!1,!x,s*2,g).lineTo(m,g),u.append("path").attr("class","railroad-line").attr("d",v.build());const b=new zl,w=m+f.dimensions.width,k=l-s*2;g===d?b.moveTo(w,g).lineTo(l,d):b.moveTo(w,g).lineTo(k,g).arcTo(s,s,0,!1,!x,l-s,g+(x?-s:s)).lineTo(l-s,d+(x?s:-s)).arcTo(s,s,0,!1,x,l,d),u.append("path").attr("class","railroad-line").attr("d",b.build()),h+=f.dimensions.height+this.config.verticalSeparation}return{element:u.node(),dimensions:{width:l,height:a,up:d,down:a-d}}}renderOptional(e,r){const n=this.renderExpression(e,r),i=this.config.arcRadius,a=i*2,s=n.dimensions.width+i*4,o=n.dimensions.height+a,l=e.append("g").attr("class","railroad-optional"),u=i*2,h=a;l.node().appendChild(n.element).setAttribute("transform",`translate(${u}, ${h})`);const f=h+n.dimensions.up,p=new zl().moveTo(0,f).lineTo(i*2,f);l.append("path").attr("class","railroad-line").attr("d",p.build());const g=new zl().moveTo(u+n.dimensions.width,f).lineTo(s,f);l.append("path").attr("class","railroad-line").attr("d",g.build());const m=new zl().moveTo(0,f).arcTo(i,i,0,!1,!1,i,f-i).lineTo(i,i).arcTo(i,i,0,!1,!0,i*2,0).lineTo(s-i*2,0).arcTo(i,i,0,!1,!0,s-i,i).lineTo(s-i,f-i).arcTo(i,i,0,!1,!1,s,f);return l.append("path").attr("class","railroad-line").attr("d",m.build()),{element:l.node(),dimensions:{width:s,height:o,up:f,down:o-f}}}renderRepetition(e,r,n){const i=this.renderExpression(e,r),a=this.config.arcRadius,s=a*2,o=i.dimensions.width+a*4,l=n===0,u=i.dimensions.height+s+(l?s:0),h=e.append("g").attr("class","railroad-repetition"),d=a*2,f=l?s:0;h.node().appendChild(i.element).setAttribute("transform",`translate(${d}, ${f})`);const g=f+i.dimensions.up;h.append("path").attr("class","railroad-line").attr("d",new zl().moveTo(0,g).lineTo(a*2,g).build()),h.append("path").attr("class","railroad-line").attr("d",new zl().moveTo(d+i.dimensions.width,g).lineTo(o,g).build());const m=f+i.dimensions.height+a,y=new zl().moveTo(d+i.dimensions.width,g).arcTo(a,a,0,!1,!0,d+i.dimensions.width+a,g+a).lineTo(d+i.dimensions.width+a,m).arcTo(a,a,0,!1,!0,d+i.dimensions.width,m+a).lineTo(a*2,m+a).arcTo(a,a,0,!1,!0,a,m).lineTo(a,g+a).arcTo(a,a,0,!1,!0,a*2,g);if(h.append("path").attr("class","railroad-line").attr("d",y.build()),l){const v=new zl().moveTo(0,g).arcTo(a,a,0,!1,!1,a,g-a).lineTo(a,a).arcTo(a,a,0,!1,!0,a*2,0).lineTo(o-a*2,0).arcTo(a,a,0,!1,!0,o-a,a).lineTo(o-a,g-a).arcTo(a,a,0,!1,!1,o,g);h.append("path").attr("class","railroad-line").attr("d",v.build())}return{element:h.node(),dimensions:{width:o,height:u,up:g,down:u-g}}}renderSpecial(e,r){const n=this.measureText("? "+r+" ?"),i=n.width+this.config.padding*2,a=n.height+this.config.padding*2,s=e.append("g").attr("class","railroad-special");return s.append("rect").attr("x",0).attr("y",0).attr("width",i).attr("height",a),s.append("text").attr("x",i/2).attr("y",a/2).text("? "+r+" ?"),{element:s.node(),dimensions:{width:i,height:a,up:a/2,down:a/2}}}renderExpression(e,r){switch(r.type){case"terminal":return this.renderTerminal(e,r.value);case"nonterminal":return this.renderNonTerminal(e,r.name);case"sequence":return this.renderSequence(e,r.elements);case"choice":return this.renderChoice(e,r.alternatives);case"optional":return this.renderOptional(e,r.element);case"repetition":return this.renderRepetition(e,r.element,r.min);case"special":return this.renderSpecial(e,r.text);default:throw new Error(`Unknown node type: ${r.type}`)}}renderRule(e,r){const n=this.svg.append("g").attr("class","railroad-rule").attr("transform",`translate(0, ${r})`),i=e.name+" =",a=this.measureText(i).width+20,s=a+20,o=n.append("g"),l=this.renderExpression(o,e.definition),u=Math.max(20,l.dimensions.up),h=u-l.dimensions.up;return o.attr("transform",`translate(${s}, ${h})`),n.append("g").attr("class","railroad-rule-name-group").append("text").attr("class","railroad-rule-name").attr("x",0).attr("y",u).text(i),n.append("g").attr("class","railroad-start").append("circle").attr("cx",a).attr("cy",u).attr("r",this.config.markerRadius),n.append("g").attr("class","railroad-end").append("circle").attr("cx",s+l.dimensions.width+10).attr("cy",u).attr("r",this.config.markerRadius),n.append("path").attr("class","railroad-line").attr("d",new zl().moveTo(a+this.config.markerRadius,u).lineTo(s,u).build()),n.append("path").attr("class","railroad-line").attr("d",new zl().moveTo(s+l.dimensions.width,u).lineTo(s+l.dimensions.width+10-this.config.markerRadius,u).build()),{height:Math.max(40,h+l.dimensions.height+this.config.padding*2),width:s+l.dimensions.width+10+this.config.markerRadius}}renderDiagram(e){let r=this.config.padding,n=0;for(const i of e){const a=this.renderRule(i,r);r+=a.height+this.config.verticalSeparation,n=Math.max(n,a.width)}return{width:n+this.config.padding*2,height:r+this.config.padding}}},T(Nx,"RailroadRenderer"),Nx),Ice=T((t,e,r)=>{ta(t,e.height,e.width,r),t.attr("viewBox",`0 0 ${e.width} ${e.height}`)},"configureRailroadSvgSize"),eWt=T((t,e,r)=>{he.debug(`[Railroad] Rendering diagram
+`+t);try{const n=Ks(e);n.attr("class","railroad-diagram");const i=lr().railroad,a=(i==null?void 0:i.useMaxWidth)??!0,s=Ci.getRules();if(he.debug(`[Railroad] Rendering ${s.length} rules`),s.length===0){he.warn("[Railroad] No rules to render"),Ice(n,{height:100,width:200},a);return}const l=new Jqt(n,SK()).renderDiagram(s);Ice(n,l,a),he.debug("[Railroad] Render complete")}catch(n){throw he.error("[Railroad] Render error:",n),n}},"draw"),oM={draw:eWt},tWt=wj().Railroad.parser.LangiumParser,oy=T(t=>{switch(t.$type){case"RailroadTerminalExpr":return{type:"terminal",value:t.value};case"RailroadNonTerminalExpr":return{type:"nonterminal",name:t.name};case"RailroadSpecialExpr":return{type:"special",text:t.text};case"RailroadSequenceExpr":{const e=t.elements.map(oy);return e.length===1?e[0]:{type:"sequence",elements:e}}case"RailroadChoiceExpr":{const e=t.alternatives.map(oy);return e.length===1?e[0]:{type:"choice",alternatives:e}}case"RailroadOptionalExpr":return{type:"optional",element:oy(t.element)};case"RailroadOneOrMoreExpr":return{type:"repetition",element:oy(t.element),min:1,max:1/0};case"RailroadZeroOrMoreExpr":return{type:"repetition",element:oy(t.element),min:0,max:1/0};default:throw new Error(`Unsupported railroad expression: ${t.$type}`)}},"transformExpression"),rWt=T(t=>({name:t.name,definition:oy(t.definition)}),"transformRule"),nWt=T(t=>{No(t,Ci),t.title&&Ci.setTitle(t.title),t.rules.map(e=>Ci.addRule(rWt(e)))},"populateDb"),iWt={parse:T(t=>{Ci.clear(),he.debug("[Railroad Parser] Starting Langium parse");const e=tWt.parse(t);if(e.lexerErrors.length>0||e.parserErrors.length>0)throw new EC(e);const r=e.value;he.debug("[Railroad Parser] Parsed rules:",r.rules.length),nWt(r),he.debug("[Railroad Parser] Parse complete")},"parse"),parser:{yy:Ci}},aWt={parser:iWt,db:Ci,renderer:oM,styles:sM};const sWt=Object.freeze(Object.defineProperty({__proto__:null,diagram:aWt},Symbol.toStringTag,{value:"Module"}));var oWt=kj().RailroadEbnf.parser.LangiumParser,eE=T(t=>{const e=t.alternatives.map(lWt);return e.length===1?e[0]:{type:"choice",alternatives:e}},"transformChoice"),lWt=T(t=>{const e=t.elements.map(uWt);return e.length===1?e[0]:{type:"sequence",elements:e}},"transformSequence"),FDe=T(t=>{switch(t.$type){case"EbnfTerminal":return{type:"terminal",value:t.value};case"EbnfNonTerminal":return{type:"nonterminal",name:t.name};case"EbnfSpecial":return{type:"special",text:t.text};case"EbnfGroup":return eE(t.element);case"EbnfOptional":return{type:"optional",element:eE(t.element)};case"EbnfRepetition":return{type:"repetition",element:eE(t.element),min:0,max:1/0};default:throw new Error(`Unsupported EBNF primary node: ${t.$type}`)}},"transformPrimary"),cWt=T((t,e)=>{switch(e.$type){case"EbnfOptionalPostfix":return{type:"optional",element:t};case"EbnfZeroOrMorePostfix":return{type:"repetition",element:t,min:0,max:1/0};case"EbnfOneOrMorePostfix":return{type:"repetition",element:t,min:1,max:1/0};case"EbnfExceptionPostfix":return{type:"sequence",elements:[t,{type:"terminal",value:"-"},FDe(e.except)]};default:throw new Error(`Unsupported EBNF postfix node: ${e.$type}`)}},"transformPostfix"),uWt=T(t=>t.postfixes.reduce((e,r)=>cWt(e,r),FDe(t.base)),"transformTerm"),hWt=T(t=>({name:t.name,definition:eE(t.definition)}),"transformRule"),dWt=T(t=>{No(t,Ci),t.title&&Ci.setTitle(t.title),t.rules.map(e=>Ci.addRule(hWt(e)))},"populateDb"),fWt={parse:T(t=>{Ci.clear(),he.debug("[EBNF Parser] Starting Langium parse");const e=oWt.parse(t);if(e.lexerErrors.length>0||e.parserErrors.length>0)throw new EC(e);const r=e.value;he.debug("[EBNF Parser] Parsed rules:",r.rules.length),dWt(r),he.debug("[EBNF Parser] Parse complete")},"parse"),parser:{yy:Ci}},pWt={parser:fWt,db:Ci,renderer:oM,styles:sM};const gWt=Object.freeze(Object.defineProperty({__proto__:null,diagram:pWt},Symbol.toStringTag,{value:"Module"}));var mWt=Cj().RailroadAbnf.parser.LangiumParser,hV=T(t=>{const e=t.alternatives.map(yWt);return e.length===1?e[0]:{type:"choice",alternatives:e}},"transformAlternation"),yWt=T(t=>{const e=t.elements.map(bWt);return e.length===1?e[0]:{type:"sequence",elements:e}},"transformConcatenation"),vWt=T(t=>{if(t.includes("*")){const[r,n]=t.split("*"),i=r?parseInt(r,10):0,a=n?parseInt(n,10):1/0;return{min:i,max:a}}const e=parseInt(t,10);return{min:e,max:e}},"parseRepeat"),bWt=T(t=>{const e=xWt(t.primary);if(!t.repeat)return e;const{min:r,max:n}=vWt(t.repeat);return r===0&&n===1?{type:"optional",element:e}:{type:"repetition",element:e,min:r,max:n}},"transformElement"),xWt=T(t=>{switch(t.$type){case"AbnfStringLiteral":return{type:"terminal",value:t.value};case"AbnfNumVal":return{type:"terminal",value:t.value};case"AbnfRuleName":return{type:"nonterminal",name:t.name};case"AbnfGroup":return hV(t.element);case"AbnfOptionalGroup":return{type:"optional",element:hV(t.element)};default:throw new Error(`Unsupported ABNF primary node: ${t.$type}`)}},"transformPrimary"),TWt=T(t=>({name:t.name,definition:hV(t.definition)}),"transformRule"),wWt=T(t=>{No(t,Ci),t.title&&Ci.setTitle(t.title),t.rules.map(e=>Ci.addRule(TWt(e)))},"populateDb"),kWt={parse:T(t=>{Ci.clear(),he.debug("[ABNF Parser] Starting Langium parse");const e=mWt.parse(t);if(e.lexerErrors.length>0||e.parserErrors.length>0)throw new EC(e);const r=e.value;he.debug("[ABNF Parser] Parsed rules:",r.rules.length),wWt(r),he.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:Ci}},CWt={parser:kWt,db:Ci,renderer:oM,styles:sM};const SWt=Object.freeze(Object.defineProperty({__proto__:null,diagram:CWt},Symbol.toStringTag,{value:"Module"}));var EWt=Sj().RailroadPeg.parser.LangiumParser,zDe=T(t=>{const e=t.alternatives.map(AWt);return e.length===1?e[0]:{type:"choice",alternatives:e}},"transformOrderedChoice"),AWt=T(t=>{const e=t.elements.map(_Wt);return e.length===1?e[0]:{type:"sequence",elements:e}},"transformSequence"),_Wt=T(t=>{const e=RWt(t.suffix);return t.operator?{type:"special",text:t.operator==="&"?`&${Dce(e)}`:`!${Dce(e)}`}:e},"transformPrefix"),Dce=T(t=>{switch(t.type){case"terminal":return`"${t.value}"`;case"nonterminal":return t.name;case"special":return t.text;default:return"(...)"}},"nodeToLabel"),RWt=T(t=>{const e=LWt(t.primary);if(!t.operator)return e;switch(t.operator){case"?":return{type:"optional",element:e};case"*":return{type:"repetition",element:e,min:0,max:1/0};case"+":return{type:"repetition",element:e,min:1,max:1/0};default:throw new Error(`Unsupported PEG suffix operator: ${t.operator}`)}},"transformSuffix"),LWt=T(t=>{switch(t.$type){case"PegLiteral":return{type:"terminal",value:t.value};case"PegIdentifier":return{type:"nonterminal",name:t.name};case"PegGroup":return zDe(t.element);case"PegAny":return{type:"special",text:t.dot};default:throw new Error(`Unsupported PEG primary node: ${t.$type}`)}},"transformPrimary"),MWt=T(t=>({name:t.name,definition:zDe(t.definition)}),"transformRule"),IWt=T(t=>{No(t,Ci),t.title&&Ci.setTitle(t.title),t.rules.map(e=>Ci.addRule(MWt(e)))},"populateDb"),DWt={parse:T(t=>{Ci.clear(),he.debug("[PEG Parser] Starting Langium parse");const e=EWt.parse(t);if(e.lexerErrors.length>0||e.parserErrors.length>0)throw new EC(e);const r=e.value;he.debug("[PEG Parser] Parsed rules:",r.rules.length),IWt(r),he.debug("[PEG Parser] Parse complete")},"parse"),parser:{yy:Ci}},NWt={parser:DWt,db:Ci,renderer:oM,styles:sM};const OWt=Object.freeze(Object.defineProperty({__proto__:null,diagram:NWt},Symbol.toStringTag,{value:"Module"}));var $Wt=1;function EK(){if(!(typeof globalThis>"u"))return globalThis}T(EK,"getCaptureGlobal");function HDe(){var t;return!!((t=EK())!=null&&t.mermaidCaptureSizes)}T(HDe,"shouldCaptureSizes");function VDe(){return typeof location>"u"?"browser-dev":`${location.pathname}${location.search}`}T(VDe,"capturedFromLocation");function GDe(t,e){const r=EK();if(!r)return;const n=e.node(),i=(n&&"ownerSVGElement"in n?n.ownerSVGElement:null)??n,a=(i==null?void 0:i.id)??"(unknown)";r.mermaidCapturedSizes??(r.mermaidCapturedSizes=[]);const s={svgId:a,sizes:t};r.mermaidCapturedSizes.push(s),r.mermaidLastCapturedSizes=s}T(GDe,"emitCapturedSizes");function qDe(t,e){const r=[];for(const n of e.nodes)n.isGroup||r.push({id:n.id,width:n.width??0,height:n.height??0});r.length!==0&&GDe({metadata:{captureVersion:$Wt,capturedAt:new Date().toISOString(),capturedFrom:VDe()},nodes:r},t)}T(qDe,"captureNodeSizes");const PWt=Object.freeze(Object.defineProperty({__proto__:null,captureNodeSizes:qDe,shouldCaptureSizes:HDe},Symbol.toStringTag,{value:"Module"})),BWt=Object.freeze(Object.defineProperty({__proto__:null,InfoModule:iRe,createInfoServices:aRe},Symbol.toStringTag,{value:"Module"})),FWt=Object.freeze(Object.defineProperty({__proto__:null,PacketModule:sRe,createPacketServices:oRe},Symbol.toStringTag,{value:"Module"})),zWt=Object.freeze(Object.defineProperty({__proto__:null,PieModule:lRe,createPieServices:cRe},Symbol.toStringTag,{value:"Module"})),HWt=Object.freeze(Object.defineProperty({__proto__:null,TreeViewModule:uRe,createTreeViewServices:hRe},Symbol.toStringTag,{value:"Module"})),VWt=Object.freeze(Object.defineProperty({__proto__:null,ArchitectureModule:dRe,createArchitectureServices:fRe},Symbol.toStringTag,{value:"Module"})),GWt=Object.freeze(Object.defineProperty({__proto__:null,GitGraphModule:rRe,createGitGraphServices:nRe},Symbol.toStringTag,{value:"Module"})),qWt=Object.freeze(Object.defineProperty({__proto__:null,EventModelingModule:gRe,createEventModelingServices:mRe},Symbol.toStringTag,{value:"Module"})),WWt=Object.freeze(Object.defineProperty({__proto__:null,RadarModule:G_e,createRadarServices:q_e},Symbol.toStringTag,{value:"Module"})),UWt=Object.freeze(Object.defineProperty({__proto__:null,RailroadModule:W_e,createRailroadServices:wj},Symbol.toStringTag,{value:"Module"})),YWt=Object.freeze(Object.defineProperty({__proto__:null,RailroadEbnfModule:U_e,createRailroadEbnfServices:kj},Symbol.toStringTag,{value:"Module"})),XWt=Object.freeze(Object.defineProperty({__proto__:null,RailroadAbnfModule:Y_e,createRailroadAbnfServices:Cj},Symbol.toStringTag,{value:"Module"})),jWt=Object.freeze(Object.defineProperty({__proto__:null,RailroadPegModule:X_e,createRailroadPegServices:Sj},Symbol.toStringTag,{value:"Module"})),KWt=Object.freeze(Object.defineProperty({__proto__:null,TreemapModule:K_e,createTreemapServices:Q_e},Symbol.toStringTag,{value:"Module"})),QWt=Object.freeze(Object.defineProperty({__proto__:null,WardleyModule:Z_e,createWardleyServices:J_e},Symbol.toStringTag,{value:"Module"})),ZWt=Object.freeze(Object.defineProperty({__proto__:null,CynefinModule:eRe,createCynefinServices:tRe},Symbol.toStringTag,{value:"Module"}));
diff --git a/apps/ios/EditorSource/bun.lock b/apps/ios/EditorSource/bun.lock
index 88542a28..dc8155bf 100644
--- a/apps/ios/EditorSource/bun.lock
+++ b/apps/ios/EditorSource/bun.lock
@@ -5,6 +5,7 @@
"": {
"name": "@edgeever/ios-editor",
"dependencies": {
+ "@edgeever/shared": "file:../../../packages/shared",
"@tiptap/core": "3.27.1",
"@tiptap/extension-code-block": "3.27.1",
"@tiptap/extension-image": "3.27.1",
@@ -33,6 +34,8 @@
"@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="],
+ "@edgeever/shared": ["@edgeever/shared@file:../../../packages/shared", { "dependencies": { "@tiptap/core": "3.27.1", "@tiptap/extension-image": "3.27.1", "@tiptap/extension-list": "3.27.1", "@tiptap/extension-mathematics": "3.27.1", "@tiptap/extension-table": "3.27.1", "@tiptap/markdown": "3.27.1", "@tiptap/pm": "3.27.1", "@tiptap/starter-kit": "3.27.1", "katex": "0.16.22" } }],
+
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
diff --git a/apps/ios/EditorSource/package.json b/apps/ios/EditorSource/package.json
index 28dd432f..b8f6c8af 100644
--- a/apps/ios/EditorSource/package.json
+++ b/apps/ios/EditorSource/package.json
@@ -9,6 +9,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
+ "@edgeever/shared": "file:../../../packages/shared",
"@tiptap/core": "3.27.1",
"@tiptap/extension-code-block": "3.27.1",
"@tiptap/extension-image": "3.27.1",
diff --git a/apps/ios/EditorSource/src/main.ts b/apps/ios/EditorSource/src/main.ts
index 13bcf67b..04484277 100644
--- a/apps/ios/EditorSource/src/main.ts
+++ b/apps/ios/EditorSource/src/main.ts
@@ -12,6 +12,19 @@ import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
import { NodeSelection } from "@tiptap/pm/state";
import mermaid from "mermaid";
import { toCanvas } from "html-to-image";
+import {
+ type NoteImageTheme,
+ type NoteImageFontStyle,
+ type NoteImageFontSize,
+ type NoteImageCardWidth,
+ NOTE_IMAGE_CARD_WIDTH_PIXELS,
+ NOTE_IMAGE_BACKGROUND_COLORS,
+ NOTE_IMAGE_THEMES,
+ resolveTheme,
+ buildImageExportBasename,
+ buildNoteImageCardMarkup,
+ generateCardCss,
+} from "@edgeever/shared/note-image-card";
import { createEdgeEverMathematics } from "./mathematics";
/** Keep in sync with packages/shared MergeDivider (iOS bundle cannot import monorepo shared). */
@@ -142,7 +155,15 @@ type ImageExportRequest = {
notebook?: string;
tags?: string[];
updatedAt?: string;
- background?: "mint" | "slate" | "warm";
+ background?: "mint" | "slate" | "warm" | NoteImageTheme;
+ theme?: NoteImageTheme;
+ fontStyle?: NoteImageFontStyle;
+ fontSize?: NoteImageFontSize;
+ cardWidth?: NoteImageCardWidth;
+ showTitle?: boolean;
+ showNotebook?: boolean;
+ showTags?: boolean;
+ showUpdatedAt?: boolean;
branding?: boolean;
};
let mode: "viewer" | "editor" = "viewer";
@@ -1089,41 +1110,49 @@ async function afterContentSet(theme: "light" | "dark" = "light") {
async function exportNoteImage(request: ImageExportRequest) {
if (!request.requestId || (request.format !== "png" && request.format !== "jpeg")) return;
- const host = document.createElement("div");
- host.style.cssText = `position:fixed;left:-100000px;top:0;width:${IMAGE_EXPORT_WIDTH}px;pointer-events:none;`;
- const documentRoot = document.createElement("div");
- documentRoot.className = "edgeever-image-document";
- const card = document.createElement("article");
- card.className = "edgeever-image-card";
- const title = document.createElement("h1");
- title.className = "edgeever-image-title";
- title.textContent = request.title || request.fallbackTitle;
- card.appendChild(title);
- const metadata = [request.notebook, request.updatedAt, ...(request.tags ?? []).map((tag) => `#${tag}`)].filter(Boolean);
- if (metadata.length > 0) {
- const meta = document.createElement("div");
- meta.className = "edgeever-image-meta";
- meta.textContent = metadata.join(" · ");
- card.appendChild(meta);
- }
- const content = document.createElement("div");
- content.className = "edgeever-image-content";
+ const resolvedTheme = resolveTheme(request.background, request.theme);
+ const fontStyle = request.fontStyle ?? "serif";
+ const fontSize = request.fontSize ?? "lg";
+ const cardWidth = request.cardWidth ?? "standard";
+ const targetWidth = NOTE_IMAGE_CARD_WIDTH_PIXELS[cardWidth] || 680;
+ const themeCfg = NOTE_IMAGE_THEMES[resolvedTheme] || NOTE_IMAGE_THEMES.slate;
+
const editorClone = editor.view.dom.cloneNode(true) as HTMLElement;
editorClone.removeAttribute("contenteditable");
editorClone.querySelectorAll("button, [contenteditable='true']").forEach((element) => {
element.removeAttribute("contenteditable");
if (element instanceof HTMLButtonElement) element.remove();
});
- content.appendChild(editorClone);
- card.appendChild(content);
- if (request.branding) {
- const footer = document.createElement("footer");
- footer.className = "edgeever-image-brand";
- footer.innerHTML = 'Made with EdgeEver';
- card.appendChild(footer);
- }
- documentRoot.appendChild(card);
- host.appendChild(documentRoot);
+
+ const bodyHtml = editorClone.innerHTML;
+
+ const host = document.createElement("div");
+ host.style.cssText = `position:fixed;left:-100000px;top:0;width:${targetWidth}px;pointer-events:none;`;
+ const style = document.createElement("style");
+ style.textContent = generateCardCss({ theme: resolvedTheme, fontStyle, fontSize, cardWidth });
+
+ const cardMarkup = buildNoteImageCardMarkup({
+ title: request.title || request.fallbackTitle,
+ notebook: request.notebook,
+ tags: request.tags,
+ updatedAt: request.updatedAt,
+ bodyHtml,
+ theme: resolvedTheme,
+ fontStyle,
+ showTitle: request.showTitle ?? true,
+ showNotebook: request.showNotebook ?? false,
+ showTags: request.showTags ?? false,
+ showUpdatedAt: request.showUpdatedAt ?? true,
+ showBranding: request.branding ?? true,
+ });
+
+ host.appendChild(style);
+ host.insertAdjacentHTML("beforeend", cardMarkup);
+ const documentRoot = host.lastElementChild as HTMLElement;
+ documentRoot.style.width = `${targetWidth}px`;
+ documentRoot.style.maxWidth = "none";
+ documentRoot.style.margin = "0";
+
document.body.appendChild(host);
try {
@@ -1133,29 +1162,26 @@ async function exportNoteImage(request: ImageExportRequest) {
try { await image.decode(); } catch { /* Export the readable remainder. */ }
}));
const totalHeight = Math.max(1, Math.ceil(documentRoot.getBoundingClientRect().height));
- const backgroundColor = {
- mint: "#ecfdf5",
- slate: "#f8fafc",
- warm: "#fffbeb",
- }[request.background ?? "slate"];
- documentRoot.style.backgroundColor = backgroundColor;
+ const backgroundColor = NOTE_IMAGE_BACKGROUND_COLORS[resolvedTheme] || themeCfg.canvasBg;
+
const canvas = await toCanvas(documentRoot, {
backgroundColor,
+ cacheBust: false,
height: totalHeight,
- pixelRatio: IMAGE_EXPORT_PIXEL_RATIO,
+ pixelRatio: 2,
skipFonts: true,
- width: IMAGE_EXPORT_WIDTH,
+ width: targetWidth,
});
const blob = await new Promise((resolve, reject) => {
canvas.toBlob(
(result) => result ? resolve(result) : reject(new Error("Image renderer returned an empty file")),
request.format === "jpeg" ? "image/jpeg" : "image/png",
- request.format === "jpeg" ? 0.9 : 1,
+ request.format === "jpeg" ? 0.92 : 1,
);
});
const extension = request.format === "jpeg" ? "jpg" : "png";
- const basename = sanitizeImageExportBasename(request.title, request.fallbackTitle);
+ const basename = buildImageExportBasename(request.title, request.fallbackTitle);
const bytes = await blobToBytes(blob);
const filename = `${basename}.${extension}`;
const mimeType = request.format === "jpeg" ? "image/jpeg" : "image/png";