hyperbook 0.84.5 → 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,112 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+ window.hyperbook = window.hyperbook || {};
3
+
4
+ hyperbook.embed = hyperbook.embed || {};
5
+
6
+ hyperbook.embed.consent = (function () {
7
+ function getDomain(url) {
8
+ try {
9
+ return new URL(url).hostname;
10
+ } catch (e) {
11
+ return url;
12
+ }
13
+ }
14
+
15
+ async function isAllowed(consentId) {
16
+ try {
17
+ var entry = await hyperbook.store.consent.get(consentId);
18
+ return entry?.allowed === true;
19
+ } catch (e) {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ async function allow(consentId) {
25
+ await hyperbook.store.consent.put({ id: consentId, allowed: true });
26
+ }
27
+
28
+ function loadContent(wrapper) {
29
+ var banner = wrapper.querySelector(".directive-embed-consent-banner");
30
+ if (banner) {
31
+ banner.remove();
32
+ }
33
+ var iframe = wrapper.querySelector("iframe");
34
+ if (iframe) {
35
+ var src = iframe.getAttribute("data-consent-src");
36
+ if (src) {
37
+ iframe.setAttribute("src", src);
38
+ iframe.removeAttribute("data-consent-src");
39
+ iframe.style.display = "";
40
+ }
41
+ }
42
+ }
43
+
44
+ async function initWrapper(wrapper) {
45
+ if (wrapper.getAttribute("data-consent-initialized")) return;
46
+ wrapper.setAttribute("data-consent-initialized", "true");
47
+
48
+ var consentSrc = wrapper.getAttribute("data-consent-src");
49
+ var domain = getDomain(consentSrc);
50
+ var consentId = "domain:" + domain;
51
+
52
+ var allowed = await isAllowed(consentId);
53
+ if (allowed) {
54
+ loadContent(wrapper);
55
+ return;
56
+ }
57
+
58
+ var btn = wrapper.querySelector(".directive-embed-consent-accept-btn");
59
+ var checkbox = wrapper.querySelector(
60
+ ".directive-embed-consent-always-checkbox"
61
+ );
62
+ if (btn) {
63
+ btn.addEventListener("click", async function () {
64
+ if (checkbox && checkbox.checked) {
65
+ await allow(consentId);
66
+ }
67
+ loadContent(wrapper);
68
+ if (checkbox && checkbox.checked) {
69
+ document
70
+ .querySelectorAll(".directive-embed-consent")
71
+ .forEach(function (el) {
72
+ var elSrc = el.getAttribute("data-consent-src");
73
+ if (getDomain(elSrc) === domain) {
74
+ loadContent(el);
75
+ }
76
+ });
77
+ }
78
+ });
79
+ }
80
+ }
81
+
82
+ function init(root) {
83
+ var wrappers = root.querySelectorAll
84
+ ? root.querySelectorAll(".directive-embed-consent")
85
+ : [];
86
+ wrappers.forEach(function (w) {
87
+ initWrapper(w);
88
+ });
89
+ }
90
+
91
+ document.addEventListener("DOMContentLoaded", function () {
92
+ init(document);
93
+ });
94
+
95
+ var observer = new MutationObserver(function (mutations) {
96
+ mutations.forEach(function (mutation) {
97
+ mutation.addedNodes.forEach(function (node) {
98
+ if (node.nodeType === 1) {
99
+ init(node);
100
+ }
101
+ });
102
+ });
103
+ });
104
+
105
+ observer.observe(document.body, { childList: true, subtree: true });
106
+
107
+ return {
108
+ isAllowed: isAllowed,
109
+ allow: allow,
110
+ init: init,
111
+ };
112
+ })();
@@ -5,7 +5,7 @@
5
5
  background-color: white;
6
6
  border-radius: 8px;
7
7
  border-style: solid;
8
- border-width: 4px;
8
+ border-width: 2px;
9
9
  border-color: var(--color-brand);
10
10
  }
11
11
 
@@ -21,3 +21,72 @@
21
21
  right: 0;
22
22
  position: absolute;
23
23
  }
24
+
25
+ .directive-embed-consent {
26
+ position: absolute;
27
+ top: 0;
28
+ left: 0;
29
+ width: 100%;
30
+ height: 100%;
31
+ }
32
+
33
+ .directive-embed-consent iframe[data-consent-src] {
34
+ display: none;
35
+ }
36
+
37
+ .directive-embed-consent-banner {
38
+ display: flex;
39
+ flex-direction: column;
40
+ align-items: center;
41
+ justify-content: center;
42
+ gap: 12px;
43
+ padding: 24px;
44
+ background-color: var(--color-background-tinted, #f5f5f5);
45
+ border-radius: 8px;
46
+ text-align: center;
47
+ height: 100%;
48
+ box-sizing: border-box;
49
+ }
50
+
51
+ .directive-embed-consent-banner-text {
52
+ font-size: 0.95em;
53
+ line-height: 1.5;
54
+ color: var(--color-text, #333);
55
+ max-width: 600px;
56
+ }
57
+
58
+ .directive-embed-consent-banner-url {
59
+ font-size: 0.85em;
60
+ color: var(--color-text-muted, #666);
61
+ word-break: break-all;
62
+ }
63
+
64
+ .directive-embed-consent-always-label {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 6px;
68
+ font-size: 0.85em;
69
+ color: var(--color-text, #333);
70
+ cursor: pointer;
71
+ }
72
+
73
+ .directive-embed-consent-always-checkbox {
74
+ cursor: pointer;
75
+ margin: 0!important;
76
+ vertical-align: middle;
77
+ }
78
+
79
+ .directive-embed-consent-accept-btn {
80
+ padding: 8px 24px;
81
+ background-color: var(--color-brand, #333);
82
+ color: var(--color-brand-text, #fff);
83
+ border: none;
84
+ border-radius: 4px;
85
+ cursor: pointer;
86
+ font-size: 0.95em;
87
+ }
88
+
89
+ .directive-embed-consent-accept-btn:hover {
90
+ opacity: 0.9;
91
+ }
92
+
@@ -0,0 +1,54 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+
3
+ /**
4
+ * @type {HyperbookStruktolab}
5
+ * @memberof hyperbook
6
+ * @see hyperbook.store
7
+ */
8
+ hyperbook.struktolab = (function () {
9
+ const init = (root) => {
10
+ let allStruktolabEditors = root.querySelectorAll("struktolab-editor");
11
+
12
+ allStruktolabEditors.forEach((editor) => {
13
+ const editorId = editor.getAttribute("data-id");
14
+
15
+ // Load saved content for this editor
16
+ hyperbook.store.struktolab.get(editorId).then((result) => {
17
+ if (result) {
18
+ editor.loadJSON(JSON.parse(result.tree));
19
+ }
20
+ });
21
+
22
+ // Listen for changes in the editor
23
+ editor.addEventListener("change", (e) => {
24
+ hyperbook.store.struktolab.put({
25
+ id: editorId,
26
+ tree: JSON.stringify(e.detail.tree),
27
+ });
28
+ });
29
+ });
30
+ };
31
+
32
+ // Initialize existing elements on document load
33
+ document.addEventListener("DOMContentLoaded", () => {
34
+ init(document);
35
+ });
36
+
37
+ // Observe for new tabs added to the DOM
38
+ const observer = new MutationObserver((mutations) => {
39
+ mutations.forEach((mutation) => {
40
+ mutation.addedNodes.forEach((node) => {
41
+ if (node.nodeType === 1) {
42
+ // Element node
43
+ init(node);
44
+ }
45
+ });
46
+ });
47
+ });
48
+
49
+ observer.observe(document.body, { childList: true, subtree: true });
50
+
51
+ return {
52
+ init,
53
+ };
54
+ })();
@@ -0,0 +1,190 @@
1
+ (function(Y,it){typeof exports=="object"&&typeof module<"u"?it(exports):typeof define=="function"&&define.amd?define(["exports"],it):(Y=typeof globalThis<"u"?globalThis:Y||self,it(Y.StruktolabEditor={}))})(this,(function(Y){"use strict";const it="http://www.w3.org/2000/svg",Ot={color:o=>({TaskNode:"rgb(253, 237, 206)",InputNode:"rgb(253, 237, 206)",OutputNode:"rgb(253, 237, 206)",HeadLoopNode:"rgb(220, 239, 231)",CountLoopNode:"rgb(220, 239, 231)",FootLoopNode:"rgb(220, 239, 231)",BranchNode:"rgb(250, 218, 209)",CaseNode:"rgb(250, 218, 209)",InsertCase:"rgb(250, 218, 209)",TryCatchNode:"rgb(250, 218, 209)",FunctionNode:"rgb(255, 255, 255)"})[o],bw:o=>({TaskNode:"rgb(255, 255, 255)",InputNode:"rgb(255, 255, 255)",OutputNode:"rgb(255, 255, 255)",HeadLoopNode:"rgb(255, 255, 255)",CountLoopNode:"rgb(255, 255, 255)",FootLoopNode:"rgb(255, 255, 255)",BranchNode:"rgb(255, 255, 255)",CaseNode:"rgb(255, 255, 255)",InsertCase:"rgb(255, 255, 255)",TryCatchNode:"rgb(255, 255, 255)",FunctionNode:"rgb(255, 255, 255)"})[o]||"rgb(255, 255, 255)",greyscale:o=>({TaskNode:"rgb(250, 250, 250)",InputNode:"rgb(250, 250, 250)",OutputNode:"rgb(250, 250, 250)",HeadLoopNode:"rgb(245, 245, 245)",CountLoopNode:"rgb(245, 245, 245)",FootLoopNode:"rgb(245, 245, 245)",BranchNode:"rgb(240, 240, 240)",CaseNode:"rgb(240, 240, 240)",InsertCase:"rgb(240, 240, 240)",TryCatchNode:"rgb(240, 240, 240)",FunctionNode:"rgb(255, 255, 255)"})[o]};let Mt="color";function et(o){return Ot[Mt](o)}const yt=40,w=20,X=8,lt=6,se="#333",Wt=1.5;let wt=0;function ft(o){wt=o}let xt=null;function re(o){xt||(xt=document.createElement("canvas"));const t=xt.getContext("2d");return t.font=`${o}px sans-serif`,t}function Rt(o,t,e){if(!o)return[""];const s=re(e),r=o.split(`
2
+ `),l=[];for(const i of r){if(!i||s.measureText(i).width<=t){l.push(i||"");continue}const n=i.split(/\s+/);let u="";for(const c of n){const a=u?u+" "+c:c;s.measureText(a).width>t&&u?(l.push(u),u=c):u=a}u&&l.push(u)}return l.length?l:[""]}function B(o,t,e){const s=Rt(o,t,e),r=e*1.3;return Math.max(yt,s.length*r+lt*2)}function ht(o,t={}){const e=document.createElementNS(it,o);for(const[s,r]of Object.entries(t))e.setAttribute(s,String(r));return e}function q(o,t,e,s,r,l,i="start"){const n=l!=null?Rt(o,l,r):[o||""],u=r*1.3,c=n.length*u,a=e+(s-c)/2+u/2,m=ht("text",{x:t,"font-size":r,"font-family":"sans-serif",fill:"#333","text-anchor":i,"dominant-baseline":"central"});for(let d=0;d<n.length;d++){const p=ht("tspan",{x:t,dy:d===0?0:u});d===0&&p.setAttribute("y",a),p.textContent=n[d],m.appendChild(p)}return m}function D(o,t,e,s,r){return ht("rect",{x:o,y:t,width:e,height:s,fill:r,stroke:"none"})}function E(o,t,e,s){return ht("line",{x1:o,y1:t,x2:e,y2:s,stroke:se,"stroke-width":Wt})}function mt(o,t,e){if(e&&e.length===t)return e.map(r=>o*r);const s=o/t;return Array(t).fill(s)}function x(o,t,e){if(!o)return 0;const s=e-X*2;switch(o.type){case"InsertNode":return wt+x(o.followElement,t,e);case"Placeholder":return 0;case"TaskNode":case"InputNode":case"OutputNode":{let r=o.text||"";return o.type==="InputNode"&&(r="▶ "+r),o.type==="OutputNode"&&(r="◀ "+r),B(r,s,t)+x(o.followElement,t,e)}case"InsertCase":return B(o.text||"",s,t)+x(o.followElement,t,e);case"BranchNode":{const r=B(o.text||"",s,t),l=t*1.3+lt,i=t*1.3+lt,n=r+l+i,u=mt(e,2,o.columnWidths),c=x(o.trueChild,t,u[0]),a=x(o.falseChild,t,u[1]);return n+Math.max(c,a)+x(o.followElement,t,e)}case"CaseNode":{const r=o.cases.length+(o.defaultOn?1:0),l=mt(e,r,o.columnWidths),i=B(o.text||"",s,t),n=t*1.3+lt,u=i+n;let c=0;for(let a=0;a<o.cases.length;a++)c=Math.max(c,x(o.cases[a],t,l[a]));return o.defaultOn&&o.defaultNode&&(c=Math.max(c,x(o.defaultNode,t,l[r-1]))),u+c+x(o.followElement,t,e)}case"HeadLoopNode":case"CountLoopNode":{const r=e-w,l=B(o.text||"",s,t),i=x(o.child,t,r),n=Math.max(i,l*.5);return l+n+x(o.followElement,t,e)}case"FootLoopNode":{const r=e-w,l=B(o.text||"",s,t),i=x(o.child,t,r);return Math.max(i,l*.5)+l+x(o.followElement,t,e)}case"FunctionNode":{const r=e-w;let l=o.text||"";o.parameters&&o.parameters.length>0?l+="("+o.parameters.map(a=>a.parName||"").join(", ")+")":l+="()",l+=" {";const i=B(l,s,t),n=x(o.child,t,r),u=Math.max(n,i*.5),c=yt*.6;return i+u+c+x(o.followElement,t,e)}case"TryCatchNode":{const r=e-w,l=B("Try",s,t);let i="Catch";o.text&&(i+=" ("+o.text+")");const n=B(i,s,t),u=x(o.tryChild,t,r),c=Math.max(u,l*.5),a=x(o.catchChild,t,r),m=Math.max(a,n*.5);return l+c+n+m+x(o.followElement,t,e)}default:return 0}}function A(o,t,e,s,r,l){if(!o)return{elements:[],height:0};const i=s-X*2,n=[];switch(o.type){case"InsertNode":{const u=wt,c=A(o.followElement,t,e+u,s,r,l?l-u:void 0);return{elements:c.elements,height:u+c.height}}case"Placeholder":return{elements:[],height:0};case"TaskNode":case"InputNode":case"OutputNode":{const u=et(o.type);let c=o.text||"";o.type==="InputNode"&&(c="▶ "+c),o.type==="OutputNode"&&(c="◀ "+c);const a=B(c,i,r),m=x(o.followElement,r,s),d=a+m,p=l!=null&&l>d?a+(l-d):a;n.push(D(t,e,s,p,u)),n.push(E(t,e,t+s,e)),n.push(E(t,e,t,e+p)),n.push(q(c,t+X,e,a,r,i));const h=l!=null?l-p:void 0,f=A(o.followElement,t,e+p,s,r,h);return{elements:n.concat(f.elements),height:p+f.height}}case"InsertCase":{const u=et(o.type),c=B(o.text||"",i,r),a=x(o.followElement,r,s),m=c+a,d=l!=null&&l>m?c+(l-m):c;n.push(D(t,e,s,d,u)),n.push(E(t,e,t,e+d)),n.push(q(o.text||"",t+X,e,c,r,i));const p=l!=null?l-d:void 0,h=A(o.followElement,t,e+d,s,r,p);return{elements:n.concat(h.elements),height:d+h.height}}case"BranchNode":{const u=et(o.type),c=B(o.text||"",i,r),a=r*1.3+lt,m=r*1.3+lt,d=c+a+m,p=mt(s,2,o.columnWidths),h=t+p[0];n.push(D(t,e,s,d,u)),n.push(E(t,e,t+s,e)),n.push(E(t,e,t,e+d)),n.push(q(o.text||"",t+s/2,e,c,r,i,"middle"));const f=e+c+a;n.push(E(t,e+c,h,f)),n.push(E(t+s,e+c,h,f));const b=r*.8;n.push(q("Wahr",t+X,f,m,b,null)),n.push(q("Falsch",t+s-X,f,m,b,null,"end")),n.push(E(h,f,h,e+d));const _=x(o.trueChild,r,p[0]),g=x(o.falseChild,r,p[1]),C=x(o.followElement,r,s),L=Math.max(_,g),I=l!=null&&l>d+L+C?l-d-L-C:0,N=L+I,y=A(o.trueChild,t,e+d,p[0],r,N);n.push(...y.elements);const J=A(o.falseChild,h,e+d,p[1],r,N);n.push(...J.elements),n.push(E(h,e+d,h,e+d+N));const S=d+N,dt=l!=null?l-S:void 0,Et=A(o.followElement,t,e+S,s,r,dt);return{elements:n.concat(Et.elements),height:S+Et.height}}case"CaseNode":{const u=et(o.type),c=o.cases.length+(o.defaultOn?1:0),a=mt(s,c,o.columnWidths),m=B(o.text||"",i,r),d=r*1.3+lt,p=m+d;n.push(D(t,e,s,p,u)),n.push(E(t,e,t+s,e)),n.push(E(t,e,t,e+p)),n.push(q(o.text||"",t+s/2,e,m,r,i,"middle"));const h=[0];for(let N=0;N<c;N++)h.push(h[N]+a[N]);if(o.defaultOn){const N=t+h[c-1];n.push(E(t,e+m,N,e+p)),n.push(E(t+s,e+m,N,e+p));for(let y=1;y<c-1;y++){const J=t+h[y],S=h[y]/h[c-1],dt=e+m+d*S;n.push(E(J,dt,J,e+p))}}else{n.push(E(t,e+m,t+s,e+p));for(let N=1;N<c;N++){const y=t+h[N],J=h[N]/s,S=e+m+d*J;n.push(E(y,S,y,e+p))}}let f=0;for(let N=0;N<o.cases.length;N++){const y=x(o.cases[N],r,a[N]);f=Math.max(f,y)}if(o.defaultOn&&o.defaultNode){const N=x(o.defaultNode,r,a[c-1]);f=Math.max(f,N)}const b=x(o.followElement,r,s),_=l!=null&&l>p+f+b?l-p-f-b:0;f+=_;let g=t;for(let N=0;N<o.cases.length;N++){const y=A(o.cases[N],g,e+p,a[N],r,f);n.push(...y.elements),N>0&&n.push(E(g,e+p,g,e+p+f)),g+=a[N]}if(o.defaultOn&&o.defaultNode){const N=A(o.defaultNode,g,e+p,a[c-1],r,f);n.push(...N.elements),n.push(E(g,e+p,g,e+p+f))}const C=p+f,L=l!=null?l-C:void 0,I=A(o.followElement,t,e+C,s,r,L);return{elements:n.concat(I.elements),height:C+I.height}}case"HeadLoopNode":case"CountLoopNode":{const u=et(o.type),c=B(o.text||"",i,r);n.push(D(t,e,s,c,u)),n.push(E(t,e,t+s,e)),n.push(E(t,e,t,e+c)),n.push(q(o.text||"",t+X,e,c,r,i));const a=s-w,m=x(o.child,r,a),d=Math.max(m,c*.5);n.push(D(t,e+c,w,d,u)),n.push(E(t,e+c,t,e+c+d)),n.push(E(t+w,e+c,t+w,e+c+d));const p=A(o.child,t+w,e+c,a,r);n.push(...p.elements);const h=c+d,f=l!=null?l-h:void 0,b=A(o.followElement,t,e+h,s,r,f);return{elements:n.concat(b.elements),height:h+b.height}}case"FootLoopNode":{const u=et(o.type),c=s-w,a=B(o.text||"",i,r),m=x(o.child,r,c),d=Math.max(m,a*.5);n.push(D(t,e,w,d,u)),n.push(E(t,e,t+s,e)),n.push(E(t,e,t,e+d)),n.push(E(t+w,e,t+w,e+d));const p=A(o.child,t+w,e,c,r);n.push(...p.elements),n.push(D(t,e+d,s,a,u)),n.push(E(t+w,e+d,t+s,e+d)),n.push(E(t,e+d,t,e+d+a)),n.push(q(o.text||"",t+X,e+d,a,r,i));const h=d+a,f=l!=null?l-h:void 0,b=A(o.followElement,t,e+h,s,r,f);return{elements:n.concat(b.elements),height:h+b.height}}case"FunctionNode":{const u=et(o.type);let c=o.text||"";if(o.parameters&&o.parameters.length>0){const C=o.parameters.map(L=>L.parName||"").join(", ");c+="("+C+")"}else c+="()";c+=" {";const a=B(c,i,r);n.push(D(t,e,s,a,u)),n.push(E(t,e,t+s,e)),n.push(E(t,e,t,e+a)),n.push(q(c,t+X,e,a,r,i));const m=s-w,d=x(o.child,r,m),p=Math.max(d,a*.5);n.push(D(t,e+a,w,p,u)),n.push(E(t,e+a,t,e+a+p)),n.push(E(t+w,e+a,t+w,e+a+p));const h=A(o.child,t+w,e+a,m,r);n.push(...h.elements);const f=yt*.6;n.push(D(t,e+a+p,s,f,u)),n.push(E(t+w,e+a+p,t+s,e+a+p)),n.push(E(t,e+a+p,t,e+a+p+f)),n.push(q("}",t+X,e+a+p,f,r,null));const b=a+p+f,_=l!=null?l-b:void 0,g=A(o.followElement,t,e+b,s,r,_);return{elements:n.concat(g.elements),height:b+g.height}}case"TryCatchNode":{const u=et(o.type),c=s-w,a=B("Try",i,r);n.push(D(t,e,s,a,u)),n.push(E(t,e,t+s,e)),n.push(E(t,e,t,e+a)),n.push(q("Try",t+X,e,a,r,null));const m=x(o.tryChild,r,c),d=Math.max(m,a*.5);n.push(D(t,e+a,w,d,u)),n.push(E(t,e+a,t,e+a+d)),n.push(E(t+w,e+a,t+w,e+a+d));const p=A(o.tryChild,t+w,e+a,c,r);n.push(...p.elements);const h=e+a+d;let f="Catch";o.text&&(f+=" ("+o.text+")");const b=B(f,i,r);n.push(D(t,h,s,b,u)),n.push(E(t+w,h,t+s,h)),n.push(E(t,h,t,h+b)),n.push(q(f,t+X,h,b,r,i));const _=x(o.catchChild,r,c),g=Math.max(_,b*.5);n.push(D(t,h+b,w,g,u)),n.push(E(t,h+b,t,h+b+g)),n.push(E(t+w,h+b,t+w,h+b+g)),n.push(E(t,h+b+g,t+w,h+b+g));const C=A(o.catchChild,t+w,h+b,c,r);n.push(...C.elements);const L=a+d+b+g,I=l!=null?l-L:void 0,N=A(o.followElement,t,e+L,s,r,I);return{elements:n.concat(N.elements),height:L+N.height}}default:return{elements:[],height:0}}}function Tt(o,t={}){const e=t.width||600,s=t.fontSize||14;t.colorMode&&Ot[t.colorMode]&&(Mt=t.colorMode);const l=x(o,s,e)||40,i=Wt,n=ht("svg",{xmlns:it,viewBox:`${-i} ${-i} ${e+i*2} ${l+i*2}`,preserveAspectRatio:"xMinYMin meet"});n.style.display="block",n.style.width="100%",n.style.height="auto";const u=A(o,0,0,e,s);for(const c of u.elements)n.appendChild(c);return n.appendChild(E(e,0,e,u.height)),n.appendChild(E(0,u.height,e,u.height)),n}const gt={if:"falls",else:"sonst",repeat:"wiederhole",while:"solange",for:"für",switch:"unterscheide",case:"fall",function:"funktion",try:"versuche",catch:"fange",input:"eingabe",output:"ausgabe",true:"Wahr",false:"Falsch",default:"Sonst"},Pt={if:"if",else:"else",repeat:"repeat",while:"while",for:"for",switch:"switch",case:"case",function:"function",try:"try",catch:"catch",input:"input",output:"output",true:"True",false:"False",default:"Default"};function V(o){return o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}let Ft=0;function $(){return"__pseudo_"+ ++Ft}function M(o){return{id:$(),type:"InsertNode",followElement:o}}function bt(){return{type:"Placeholder"}}function Lt(o){const t=o.match(/^(.*?)\s*\[([0-9.,\s]+)\]\s*$/);if(t){const e=t[1].trim(),s=t[2].split(",").map(r=>parseFloat(r.trim())).filter(r=>!isNaN(r));return{text:e,columnWidths:s.length>0?s:null}}return{text:o,columnWidths:null}}function ne(o){const t=o.split(`
3
+ `),e=[];for(const s of t){const r=s.trimEnd();if(r===""||r.startsWith("#"))continue;const l=s.replace(/\t/g," "),i=l.length-l.trimStart().length;e.push({text:r.trim(),indent:i})}return e}function Dt(o,t){const e=[];let s=0;for(;s<o.length;){const r=o[s];if(r.indent<t)break;if(r.indent>t){s++;continue}const l=[];for(s++;s<o.length&&o[s].indent>t;)l.push(o[s]),s++;e.push({text:r.text,indent:r.indent,children:l})}return e}function Ht(o){return o.endsWith(":")?o.slice(0,-1).trimEnd():o}function Q(o,t,e){const s=Dt(o,t);if(s.length===0)return M(bt());const r=V(e.try),l=V(e.catch),i=V(e.if),n=V(e.else),u=V(e.repeat),c=V(e.while),a=V(e.for),m=V(e.switch),d=V(e.case),p=V(e.function),h=V(e.input),f=V(e.output),b=new RegExp(`^${r}\\s*:$`,"i"),_=new RegExp(`^${l}\\s+`,"i"),g=new RegExp(`^${i}\\s+`,"i"),C=new RegExp(`^${n}\\s*:$`,"i"),L=new RegExp(`^${u}\\s*:$`,"i"),I=new RegExp(`^${c}\\s+`,"i"),N=new RegExp(`^${u}\\s+${a}\\s+(.+)\\s*:$`,"i"),y=new RegExp(`^${u}\\s+${c}\\s+(.+)\\s*:$`,"i"),J=new RegExp(`^${p}\\s+(\\w+)\\s*\\(([^)]*)\\)\\s*:$`,"i"),S=new RegExp(`^${m}\\s+(.+)\\s*:$`,"i"),dt=new RegExp(`^${d}\\s+(.+)\\s*:$`,"i"),Et=new RegExp(`^${h}\\s*\\(\\s*"?([^"]*)"?\\s*\\)$`,"i"),ge=new RegExp(`^${f}\\s*\\(\\s*"?([^"]*)"?\\s*\\)$`,"i"),Z=[];for(const W of s)Z.push({block:W,_skip:!1});for(let W=0;W<Z.length-1;W++){const K=Z[W].block.text,O=Z[W+1].block.text;(b.test(K)&&_.test(O)||g.test(K)&&C.test(O)||L.test(K)&&I.test(O)&&!O.endsWith(":"))&&(Z[W+1]._skip=!0)}let k=M(null);for(let W=Z.length-1;W>=0;W--){const K=Z[W].block,O=K.text;if(Z[W]._skip)continue;const z=W+1<Z.length?Z[W+1].block:null;if(b.test(O)&&z&&_.test(z.text)){const R=t+4,P=Q(K.children,R,e),F=Ht(z.text.replace(_,"")),U=Q(z.children,R,e);k=M({id:$(),type:"TryCatchNode",text:F,tryChild:P,catchChild:U,followElement:k});continue}if(g.test(O)&&z&&C.test(z.text)){const R=Ht(O.replace(g,"")),{text:P,columnWidths:F}=Lt(R),U=t+4,rt=Q(K.children,U,e),nt=Q(z.children,U,e),tt={id:$(),type:"BranchNode",text:P,trueChild:rt,falseChild:nt,followElement:k};F&&(tt.columnWidths=F),k=M(tt);continue}if(g.test(O)){const R=Ht(O.replace(g,"")),{text:P,columnWidths:F}=Lt(R),U=t+4,rt=Q(K.children,U,e),nt={id:$(),type:"BranchNode",text:P,trueChild:rt,falseChild:M(bt()),followElement:k};F&&(nt.columnWidths=F),k=M(nt);continue}if(L.test(O)&&z&&I.test(z.text)&&!z.text.endsWith(":")){const R=z.text.replace(I,"").trim(),P=t+4,F=Q(K.children,P,e);k=M({id:$(),type:"FootLoopNode",text:R,child:F,followElement:k});continue}const Qt=O.match(N);if(Qt){const R=t+4,P=Q(K.children,R,e);k=M({id:$(),type:"CountLoopNode",text:Qt[1],child:P,followElement:k});continue}const Zt=O.match(y);if(Zt){const R=t+4,P=Q(K.children,R,e);k=M({id:$(),type:"HeadLoopNode",text:Zt[1],child:P,followElement:k});continue}const vt=O.match(J);if(vt){const R=vt[1],P=vt[2].trim(),F=P?P.split(",").map((tt,pt)=>({pos:String(pt*3),parName:tt.trim()})):[],U=t+4,rt=Q(K.children,U,e);k=M({id:$(),type:"FunctionNode",text:R,parameters:F,child:rt,followElement:k});continue}const St=O.match(S);if(St){const R=St[1],{text:P,columnWidths:F}=Lt(R),U=t+4,rt=Dt(K.children,U),nt=[];let tt=null,pt=!1;for(const Ct of rt){const oe=Ct.text.match(dt);if(oe){const At=U+4,Bt=Q(Ct.children,At,e);nt.push({id:$(),type:"InsertCase",text:oe[1].replace(/^"(.*)"$/,"$1"),followElement:Bt})}else if(C.test(Ct.text)){pt=!0;const At=U+4,Bt=Q(Ct.children,At,e);tt={id:$(),type:"InsertCase",text:e.default,followElement:Bt}}}tt||(tt={id:$(),type:"InsertCase",text:e.default,followElement:M(bt())});const ee={id:$(),type:"CaseNode",text:P,defaultOn:pt,defaultNode:tt,cases:nt,followElement:k};F&&(ee.columnWidths=F),k=M(ee);continue}const zt=O.match(Et);if(zt){k=M({id:$(),type:"InputNode",text:zt[1],followElement:k});continue}const te=O.match(ge);if(te){k=M({id:$(),type:"OutputNode",text:te[1],followElement:k});continue}k=M({id:$(),type:"TaskNode",text:O,followElement:k})}return k}function Nt(o,t=gt){Ft=0;const e=ne(o);if(e.length===0)return M(bt());const s=Math.min(...e.map(r=>r.indent));return Q(e,s,t)}const $t={python:{InputNode:{pre:"",post:` = input("Eingabe")
4
+ `},OutputNode:{pre:"print(",post:`)
5
+ `},TaskNode:{pre:"",post:`
6
+ `},BranchNode:{pre:"if ",post:`:
7
+ `,between:`else:
8
+ `},TryCatchNode:{pre:`try:
9
+ `,between:"except ",post:`:
10
+ `},CountLoopNode:{pre:"for ",post:`:
11
+ `},HeadLoopNode:{pre:"while ",post:`:
12
+ `},FootLoopNode:{prepre:`while True:
13
+ `,pre:" if not ",post:`:
14
+ break
15
+ `},FunctionNode:{pre:"def ",between:"(",post:`):
16
+ `},CaseNode:{pre:"if ",post:`:
17
+ `},InsertCase:{preNormal:"elif ",preDefault:"else",post:`:
18
+ `,postpost:`
19
+ `},leftBracket:"",rightBracket:"",pseudoSwitch:!0},java:{InputNode:{pre:"",post:` = System.console().readLine();
20
+ `},OutputNode:{pre:"System.out.println(",post:`);
21
+ `},TaskNode:{pre:"",post:`;
22
+ `},BranchNode:{pre:"if (",post:")",between:`} else {
23
+ `},TryCatchNode:{pre:"try",between:"catch (",post:")"},CountLoopNode:{pre:"for (",post:")"},HeadLoopNode:{pre:"while (",post:")"},FootLoopNode:{prepre:"do",pre:"while (",post:`);
24
+ `},FunctionNode:{pre:"public void ",between:"(",post:")"},CaseNode:{pre:"switch (",post:")"},InsertCase:{preNormal:"case ",preDefault:"default",post:`:
25
+ `,postpost:`break;
26
+ `},leftBracket:"{",rightBracket:"}",pseudoSwitch:!1},javascript:{InputNode:{pre:"",post:` = prompt("Eingabe");
27
+ `},OutputNode:{pre:"console.log(",post:`);
28
+ `},TaskNode:{pre:"",post:`;
29
+ `},BranchNode:{pre:"if (",post:")",between:`} else {
30
+ `},TryCatchNode:{pre:"try",between:"catch (",post:")"},CountLoopNode:{pre:"for (",post:")"},HeadLoopNode:{pre:"while (",post:")"},FootLoopNode:{prepre:"do",pre:"while (",post:`);
31
+ `},FunctionNode:{pre:"function ",between:"(",post:")"},CaseNode:{pre:"switch (",post:")"},InsertCase:{preNormal:"case ",preDefault:"default",post:`:
32
+ `,postpost:`break;
33
+ `},leftBracket:"{",rightBracket:"}",pseudoSwitch:!1}};function H(o){return" ".repeat(o)}function jt(o,t="python"){const e=$t[t.toLowerCase()];if(!e)throw new Error(`Unsupported language: ${t}. Supported: ${Object.keys($t).join(", ")}`);return j(o,0,e,t.toLowerCase()).join("")}function j(o,t,e,s){if(!o)return[];if(o.type==="InsertNode"||o.type==="Placeholder")return j(o.followElement,t,e,s);const r=o.text||"",l=[];switch(o.type){case"TaskNode":l.push(H(t)+e.TaskNode.pre+r+e.TaskNode.post);break;case"InputNode":l.push(H(t)+e.InputNode.pre+r+e.InputNode.post);break;case"OutputNode":l.push(H(t)+e.OutputNode.pre+r+e.OutputNode.post);break;case"BranchNode":{l.push(H(t)+e.BranchNode.pre+r+e.BranchNode.post+(e.leftBracket?" "+e.leftBracket+`
34
+ `:"")),l.push(...j(o.trueChild,t+1,e,s)),l.push(H(t)+e.BranchNode.between),l.push(...j(o.falseChild,t+1,e,s)),e.rightBracket&&l.push(H(t)+e.rightBracket+`
35
+ `);break}case"HeadLoopNode":l.push(H(t)+e.HeadLoopNode.pre+r+e.HeadLoopNode.post+(e.leftBracket?" "+e.leftBracket+`
36
+ `:"")),l.push(...j(o.child,t+1,e,s)),e.rightBracket&&l.push(H(t)+e.rightBracket+`
37
+ `);break;case"CountLoopNode":l.push(H(t)+e.CountLoopNode.pre+r+e.CountLoopNode.post+(e.leftBracket?" "+e.leftBracket+`
38
+ `:"")),l.push(...j(o.child,t+1,e,s)),e.rightBracket&&l.push(H(t)+e.rightBracket+`
39
+ `);break;case"FootLoopNode":{l.push(H(t)+e.FootLoopNode.prepre+(e.leftBracket?" "+e.leftBracket+`
40
+ `:"")),l.push(...j(o.child,t+1,e,s)),e.pseudoSwitch?l.push(H(t)+e.FootLoopNode.pre+r+e.FootLoopNode.post):(e.rightBracket&&l.push(H(t)+e.rightBracket+" "),l.push(e.FootLoopNode.pre+r+e.FootLoopNode.post));break}case"FunctionNode":{const i=(o.parameters||[]).map(n=>n.parName).join(", ");l.push(H(t)+e.FunctionNode.pre+r+e.FunctionNode.between+i+e.FunctionNode.post+(e.leftBracket?" "+e.leftBracket+`
41
+ `:"")),l.push(...j(o.child,t+1,e,s)),e.rightBracket&&l.push(H(t)+e.rightBracket+`
42
+ `);break}case"TryCatchNode":{l.push(H(t)+e.TryCatchNode.pre+(e.leftBracket?" "+e.leftBracket+`
43
+ `:"")),l.push(...j(o.tryChild,t+1,e,s)),l.push(H(t)+(e.rightBracket?e.rightBracket+" ":"")+e.TryCatchNode.between+r+e.TryCatchNode.post+(e.leftBracket?" "+e.leftBracket+`
44
+ `:"")),l.push(...j(o.catchChild,t+1,e,s)),e.rightBracket&&l.push(H(t)+e.rightBracket+`
45
+ `);break}case"CaseNode":{if(e.pseudoSwitch){let i=!0;for(const n of o.cases||[])if(n.type==="InsertCase"){const u=i?"if ":e.InsertCase.preNormal;l.push(H(t)+u+r+" == "+n.text+e.InsertCase.post),l.push(...j(n.followElement,t+1,e,s)),i=!1}o.defaultOn&&o.defaultNode&&(l.push(H(t)+e.InsertCase.preDefault+e.InsertCase.post),l.push(...j(o.defaultNode.followElement,t+1,e,s)))}else{l.push(H(t)+e.CaseNode.pre+r+e.CaseNode.post+(e.leftBracket?" "+e.leftBracket+`
46
+ `:""));for(const i of o.cases||[])i.type==="InsertCase"&&(l.push(H(t+1)+e.InsertCase.preNormal+i.text+e.InsertCase.post),l.push(...j(i.followElement,t+2,e,s)),e.InsertCase.postpost&&l.push(H(t+2)+e.InsertCase.postpost));o.defaultOn&&o.defaultNode&&(l.push(H(t+1)+e.InsertCase.preDefault+e.InsertCase.post),l.push(...j(o.defaultNode.followElement,t+2,e,s)),s==="java"&&l.push(H(t+2)+e.InsertCase.postpost)),e.rightBracket&&l.push(H(t)+e.rightBracket+`
47
+ `)}break}}return l.push(...j(o.followElement,t,e,s)),l}const le=" ";function Gt(o,t=gt){const e=[];return v(o,0,e,t),e.join(`
48
+ `)}function G(o){return le.repeat(o)}function Jt(o){return o.columnWidths&&o.columnWidths.length>0?" ["+o.columnWidths.join(", ")+"]":""}function v(o,t,e,s){if(o)switch(o.type){case"InsertNode":v(o.followElement,t,e,s);return;case"Placeholder":return;case"TaskNode":e.push(G(t)+(o.text||"")),v(o.followElement,t,e,s);return;case"InputNode":e.push(G(t)+s.input+'("'+(o.text||"")+'")'),v(o.followElement,t,e,s);return;case"OutputNode":e.push(G(t)+s.output+'("'+(o.text||"")+'")'),v(o.followElement,t,e,s);return;case"BranchNode":e.push(G(t)+s.if+" "+(o.text||"")+Jt(o)+":"),v(o.trueChild,t+1,e,s),e.push(G(t)+s.else+":"),v(o.falseChild,t+1,e,s),v(o.followElement,t,e,s);return;case"CaseNode":{if(e.push(G(t)+s.switch+" "+(o.text||"")+Jt(o)+":"),o.cases)for(const r of o.cases)e.push(G(t+1)+s.case+" "+r.text+":"),v(r.followElement,t+2,e,s);o.defaultOn&&o.defaultNode&&(e.push(G(t+1)+s.else+":"),v(o.defaultNode.followElement,t+2,e,s)),v(o.followElement,t,e,s);return}case"HeadLoopNode":e.push(G(t)+s.repeat+" "+s.while+" "+(o.text||"")+":"),v(o.child,t+1,e,s),v(o.followElement,t,e,s);return;case"CountLoopNode":e.push(G(t)+s.repeat+" "+s.for+" "+(o.text||"")+":"),v(o.child,t+1,e,s),v(o.followElement,t,e,s);return;case"FootLoopNode":e.push(G(t)+s.repeat+":"),v(o.child,t+1,e,s),e.push(G(t)+s.while+" "+(o.text||"")),v(o.followElement,t,e,s);return;case"FunctionNode":{const r=(o.parameters||[]).map(l=>l.parName).join(", ");e.push(G(t)+s.function+" "+(o.text||"")+"("+r+"):"),v(o.child,t+1,e,s),v(o.followElement,t,e,s);return}case"TryCatchNode":e.push(G(t)+s.try+":"),v(o.tryChild,t+1,e,s),e.push(G(t)+s.catch+" "+(o.text||"")+":"),v(o.catchChild,t+1,e,s),v(o.followElement,t,e,s);return;default:return}}let ce=0;function T(){return"__ed_"+Date.now().toString(36)+"_"+ ++ce}function ot(o){return o?JSON.parse(JSON.stringify(o)):null}function ct(o,t){if(!o)return null;if(o.id===t)return o;for(const e of["followElement","trueChild","falseChild","child","tryChild","catchChild"])if(o[e]){const s=ct(o[e],t);if(s)return s}if(o.cases)for(const e of o.cases){const s=ct(e,t);if(s)return s}if(o.defaultNode){const e=ct(o.defaultNode,t);if(e)return e}return null}function ut(o,t,e=null,s=null,r=null){if(!o)return null;if(o.id===t)return{parent:e,key:s,index:r};for(const l of["followElement","trueChild","falseChild","child","tryChild","catchChild"])if(o[l]){const i=ut(o[l],t,o,l);if(i)return i}if(o.cases)for(let l=0;l<o.cases.length;l++){const i=ut(o.cases[l],t,o,"cases",l);if(i)return i}if(o.defaultNode){const l=ut(o.defaultNode,t,o,"defaultNode");if(l)return l}return null}function ae(o){const t=T(),e={id:T(),type:"InsertNode",followElement:{type:"Placeholder"}};switch(o){case"TaskNode":return{id:t,type:"TaskNode",text:"Anweisung",followElement:null};case"InputNode":return{id:t,type:"InputNode",text:"Eingabe",followElement:null};case"OutputNode":return{id:t,type:"OutputNode",text:"Ausgabe",followElement:null};case"BranchNode":return{id:t,type:"BranchNode",text:"Bedingung",trueChild:{...e,id:T()},falseChild:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}},followElement:null};case"CaseNode":return{id:t,type:"CaseNode",text:"Variable",defaultOn:!0,defaultNode:{id:T(),type:"InsertCase",text:"Sonst",followElement:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}}},cases:[{id:T(),type:"InsertCase",text:"Fall 1",followElement:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}}},{id:T(),type:"InsertCase",text:"Fall 2",followElement:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}}}],followElement:null};case"HeadLoopNode":return{id:t,type:"HeadLoopNode",text:"Bedingung",child:{...e,id:T()},followElement:null};case"CountLoopNode":return{id:t,type:"CountLoopNode",text:"i = 1 bis 10",child:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}},followElement:null};case"FootLoopNode":return{id:t,type:"FootLoopNode",text:"Bedingung",child:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}},followElement:null};case"FunctionNode":return{id:t,type:"FunctionNode",text:"funktion",parameters:[{pos:"0",parName:"param"}],child:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}},followElement:null};case"TryCatchNode":return{id:t,type:"TryCatchNode",text:"Exception e",tryChild:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}},catchChild:{id:T(),type:"InsertNode",followElement:{type:"Placeholder"}},followElement:null};default:return{id:t,type:"TaskNode",text:o,followElement:null}}}function Yt(o,t,e){const s=ot(o),r=ct(s,t);if(!r)return s;const l=ae(e);if(r.type==="InsertNode")l.followElement=r.followElement,r.followElement=l;else if(r.type==="Placeholder"){const i=ut(s,t);if(i){const n={id:T(),type:"InsertNode",followElement:l};l.followElement={id:T(),type:"InsertNode",followElement:{type:"Placeholder"}},i.index!=null?i.parent[i.key][i.index]=n:i.parent[i.key]=n}}return s}function ie(o,t){const e=ot(o),s=ut(e,t);if(!s)return e;const r=s.index!=null?s.parent[s.key][s.index]:s.parent[s.key];if(!r)return e;const l=r.followElement||{type:"Placeholder"};return s.index!=null?s.parent[s.key][s.index]=l:s.parent[s.key]=l,e}function ue(o,t,e){const s=ot(o),r=ct(s,t);return r&&(r.text=e),s}function he(o,t,e){if(t===e)return o;let s=ot(o);const r=ut(s,t);if(!r)return s;const l=r.index!=null?r.parent[r.key][r.index]:r.parent[r.key];if(!l)return s;const i=l.followElement||{type:"Placeholder"};r.index!=null?r.parent[r.key][r.index]=i:r.parent[r.key]=i;const n=ct(s,e);return n&&(l.followElement=null,n.type==="InsertNode"&&(l.followElement=n.followElement,n.followElement=l)),s}function de(o){if(!o)return{id:T(),type:"InsertNode",followElement:{id:T(),type:"Placeholder"}};const t=ot(o);return at(t)}function at(o){if(!o)return{id:T(),type:"InsertNode",followElement:{id:T(),type:"Placeholder"}};if(o.type==="InsertNode")return o.followElement=o.followElement?It(o.followElement):{id:T(),type:"Placeholder"},o;if(o.type==="Placeholder")return{id:T(),type:"InsertNode",followElement:o};const t=It(o);return{id:T(),type:"InsertNode",followElement:t}}function It(o){if(!o)return{id:T(),type:"Placeholder"};if(o.type==="InsertNode")return o.followElement=o.followElement?It(o.followElement):{id:T(),type:"Placeholder"},o;if(o.type==="Placeholder")return o;for(const t of["trueChild","falseChild","child","tryChild","catchChild"])o[t]&&(o[t]=at(o[t]));if(o.cases)for(let t=0;t<o.cases.length;t++)o.cases[t]&&o.cases[t].type==="InsertCase"?o.cases[t].followElement=at(o.cases[t].followElement):o.cases[t]=at(o.cases[t]);return o.defaultNode&&(o.defaultNode.type==="InsertCase"?o.defaultNode.followElement=at(o.defaultNode.followElement):o.defaultNode=at(o.defaultNode)),o.followElement?o.followElement=at(o.followElement):o.followElement={id:T(),type:"InsertNode",followElement:{id:T(),type:"Placeholder"}},o}function Kt(o){if(!o)return o;const t=ot(o);function e(s){if(s){s.id||(s.id=T());for(const r of["followElement","trueChild","falseChild","child","tryChild","catchChild"])s[r]&&e(s[r]);s.cases&&s.cases.forEach(e),s.defaultNode&&e(s.defaultNode)}}return e(t),t}function Ut(o){if(!o)return null;const t=ot(o);return st(t)}function st(o){if(!o||o.type==="Placeholder")return null;if(o.type==="InsertNode")return st(o.followElement);for(const t of["trueChild","falseChild","child","tryChild","catchChild"])o[t]&&(o[t]=st(o[t]));return o.cases&&(o.cases=o.cases.map(t=>t&&t.type==="InsertCase"?(t.followElement=st(t.followElement),t):st(t)).filter(Boolean)),o.defaultNode&&(o.defaultNode.type==="InsertCase"?o.defaultNode.followElement=st(o.defaultNode.followElement):o.defaultNode=st(o.defaultNode)),o.followElement=st(o.followElement),delete o.id,_t(o),o}function _t(o){if(o){delete o.id;for(const t of["followElement","trueChild","falseChild","child","tryChild","catchChild"])o[t]&&_t(o[t]);o.cases&&o.cases.forEach(_t),o.defaultNode&&_t(o.defaultNode)}}const kt="rgba(1, 116, 96, 0.2)",Xt="rgba(1, 116, 96, 0.45)",qt=20,pe="rgba(192, 57, 43, 0.25)",fe=[{type:"TaskNode",label:"Task",icon:"▭"},{type:"InputNode",label:"Input",icon:"▶"},{type:"OutputNode",label:"Output",icon:"◀"},{type:"BranchNode",label:"If/Else",icon:"◇"},{type:"CaseNode",label:"Switch",icon:"⊞"},{type:"HeadLoopNode",label:"While",icon:"↻"},{type:"FootLoopNode",label:"Do-While",icon:"↺"},{type:"CountLoopNode",label:"For",icon:"#"},{type:"FunctionNode",label:"Function",icon:"ƒ"},{type:"TryCatchNode",label:"Try/Catch",icon:"⚡"}],me=`
49
+ :host {
50
+ display: block;
51
+ width: 100%;
52
+ font-family: sans-serif;
53
+ --toolbar-bg: #f5f5f5;
54
+ --toolbar-border: #d6d6d6;
55
+ --btn-bg: #fff;
56
+ --btn-hover: #b5e3d9;
57
+ --btn-active: #b5e3d9;
58
+ --danger: #c0392b;
59
+ }
60
+
61
+ .toolbar {
62
+ display: flex;
63
+ flex-wrap: wrap;
64
+ gap: 4px;
65
+ padding: 6px 8px;
66
+ background: var(--toolbar-bg);
67
+ border: 1px solid var(--toolbar-border);
68
+ border-radius: 4px 4px 0 0;
69
+ align-items: center;
70
+ }
71
+
72
+ .toolbar button {
73
+ display: inline-flex;
74
+ align-items: center;
75
+ gap: 4px;
76
+ padding: 4px 10px;
77
+ border: 1px solid var(--toolbar-border);
78
+ border-radius: 3px;
79
+ background: var(--btn-bg);
80
+ cursor: pointer;
81
+ font-size: 13px;
82
+ font-family: inherit;
83
+ white-space: nowrap;
84
+ user-select: none;
85
+ }
86
+ .toolbar button:hover { background: var(--btn-hover); }
87
+ .toolbar button.active { background: var(--btn-active); border-color: #017460; }
88
+ .toolbar button.danger { color: var(--danger); border-color: var(--danger); }
89
+ .toolbar button.danger:hover { background: #fde; }
90
+ .toolbar button.danger.active { background: #fcc; }
91
+
92
+ .toolbar .sep {
93
+ width: 1px;
94
+ height: 24px;
95
+ background: var(--toolbar-border);
96
+ margin: 0 4px;
97
+ }
98
+
99
+ .toolbar select, .toolbar input[type="number"] {
100
+ padding: 3px 6px;
101
+ border: 1px solid var(--toolbar-border);
102
+ border-radius: 3px;
103
+ background: var(--btn-bg);
104
+ font-size: 13px;
105
+ font-family: inherit;
106
+ }
107
+ .toolbar label {
108
+ font-size: 12px;
109
+ color: #3c3c3c;
110
+ display: inline-flex;
111
+ align-items: center;
112
+ gap: 4px;
113
+ white-space: nowrap;
114
+ }
115
+ .toolbar input[type="number"] {
116
+ width: 52px;
117
+ }
118
+
119
+ .editor-area {
120
+ position: relative;
121
+ border-left: 1px solid var(--toolbar-border);
122
+ border-right: 1px solid var(--toolbar-border);
123
+ min-height: 60px;
124
+ overflow: hidden;
125
+ }
126
+
127
+ .editor-area svg { cursor: default; }
128
+ .editor-area.mode-insert svg { cursor: crosshair; }
129
+ .editor-area.mode-delete svg { cursor: not-allowed; }
130
+
131
+ .text-overlay {
132
+ position: absolute;
133
+ display: flex;
134
+ gap: 4px;
135
+ padding: 2px;
136
+ z-index: 10;
137
+ }
138
+ .text-overlay textarea {
139
+ flex: 1;
140
+ font-size: 14px;
141
+ font-family: inherit;
142
+ padding: 2px 6px;
143
+ border: 2px solid #017460;
144
+ border-radius: 3px;
145
+ outline: none;
146
+ min-width: 60px;
147
+ resize: none;
148
+ line-height: 1.4;
149
+ }
150
+ .text-overlay button {
151
+ padding: 2px 8px;
152
+ border: 1px solid #d6d6d6;
153
+ border-radius: 3px;
154
+ cursor: pointer;
155
+ font-size: 14px;
156
+ }
157
+ .text-overlay .ok { background: #b5e3d9; }
158
+ .text-overlay .cancel { background: #f8d7da; }
159
+
160
+ .pseudocode-area {
161
+ border: 1px solid var(--toolbar-border);
162
+ border-radius: 0 0 4px 4px;
163
+ }
164
+ .pseudocode-area textarea {
165
+ display: block;
166
+ width: 100%;
167
+ min-height: 120px;
168
+ max-height: 400px;
169
+ padding: 8px;
170
+ border: none;
171
+ font-family: "Fira Code", "Consolas", monospace;
172
+ font-size: 13px;
173
+ line-height: 1.5;
174
+ resize: vertical;
175
+ box-sizing: border-box;
176
+ outline: none;
177
+ tab-size: 4;
178
+ }
179
+ .pseudocode-area textarea:focus {
180
+ box-shadow: inset 0 0 0 2px rgba(1, 116, 96, 0.3);
181
+ }
182
+ .pseudocode-area .error {
183
+ color: var(--danger);
184
+ font-size: 12px;
185
+ padding: 2px 8px;
186
+ }
187
+ `;class Vt extends HTMLElement{static get observedAttributes(){return["scale","font-size","src","lang","color-mode"]}constructor(){super(),this._tree=null,this._keywords=null,this._mode=null,this._syncing=!1,this._debounceTimer=null,this._shadow=this.attachShadow({mode:"open"});const t=document.createElement("style");t.textContent=me,this._shadow.appendChild(t),this._toolbar=document.createElement("div"),this._toolbar.className="toolbar",this._buildToolbar(),this._shadow.appendChild(this._toolbar),this._editorArea=document.createElement("div"),this._editorArea.className="editor-area",this._shadow.appendChild(this._editorArea),this._pseudoArea=document.createElement("div"),this._pseudoArea.className="pseudocode-area",this._textarea=document.createElement("textarea"),this._textarea.spellcheck=!1,this._textarea.placeholder="Pseudocode...",this._pseudoArea.appendChild(this._textarea),this._errorEl=document.createElement("div"),this._errorEl.className="error",this._errorEl.style.display="none",this._pseudoArea.appendChild(this._errorEl),this._shadow.appendChild(this._pseudoArea),this._overlay=document.createElement("div"),this._overlay.className="text-overlay",this._overlay.style.display="none",this._editorArea.appendChild(this._overlay),this._textarea.addEventListener("input",()=>this._onPseudocodeInput()),this._textarea.addEventListener("blur",()=>this._syncPseudocodeToTree()),this._textarea.addEventListener("keydown",e=>{if(e.key==="Tab"){e.preventDefault();const s=this._textarea,r=s.selectionStart,l=s.selectionEnd;if(e.shiftKey){const n=s.value.substring(0,r).lastIndexOf(`
188
+ `)+1,u=s.value.substring(n);u.startsWith(" ")?(s.value=s.value.substring(0,n)+s.value.substring(n+4),s.selectionStart=s.selectionEnd=Math.max(n,r-4)):u.startsWith(" ")&&(s.value=s.value.substring(0,n)+s.value.substring(n+1),s.selectionStart=s.selectionEnd=Math.max(n,r-1))}else s.value=s.value.substring(0,r)+" "+s.value.substring(l),s.selectionStart=s.selectionEnd=r+4;s.dispatchEvent(new Event("input"))}})}connectedCallback(){requestAnimationFrame(()=>this._initialize())}_getKeywords(){return this._keywords?this._keywords:(this.getAttribute("lang")||"de").toLowerCase()==="en"?Pt:gt}_prepTree(t){return Kt(de(t))}_initialize(){if(!this._tree){const t=this.querySelector('script[type="text/pseudocode"]');if(t)try{this._tree=this._prepTree(Nt(t.textContent,this._getKeywords())),this._emitChange()}catch(e){console.error("struktolab-editor: failed to parse pseudocode",e)}}if(!this._tree){const t=this.querySelector('script[type="application/json"]');if(t)try{this._tree=this._prepTree(JSON.parse(t.textContent)),this._emitChange()}catch(e){console.error("struktolab-editor: invalid inline JSON",e)}}if(!this._tree&&this.hasAttribute("src")){this._fetchTree(this.getAttribute("src"));return}this._tree||(this._tree=Kt({id:"__root",type:"InsertNode",followElement:{type:"Placeholder"}})),this._render(),this._syncTreeToPseudocode()}attributeChangedCallback(t,e,s){t==="src"&&s&&s!==e?this._fetchTree(s):(t==="lang"&&this._langSelect&&(this._langSelect.value=s||"de"),t==="font-size"&&this._fsInput&&(this._fsInput.value=s||"14"),t==="scale"&&this._scaleInput&&(this._scaleInput.value=s||"1"),t==="color-mode"&&this._colorModeSelect&&(this._colorModeSelect.value=s||"color"),this._render())}set tree(t){this._tree=this._prepTree(t),this._render(),this._syncTreeToPseudocode()}get tree(){return this._tree}set keywords(t){this._keywords=t}get keywords(){return this._getKeywords()}set pseudocode(t){this._tree=this._prepTree(Nt(t,this._getKeywords())),this._render(),this._syncTreeToPseudocode()}toCode(t){return this._tree?jt(this._tree,t):""}async _fetchTree(t){try{const e=await fetch(t);this._tree=this._prepTree(await e.json()),this._render(),this._syncTreeToPseudocode()}catch(e){console.error("struktolab-editor: failed to fetch tree from",t,e)}}_buildToolbar(){for(const _ of fe){const g=document.createElement("button");g.textContent=_.icon+" "+_.label,g.dataset.type=_.type,g.title=`Insert ${_.label}`,g.draggable=!0,g.addEventListener("click",()=>this._toggleInsertMode(_.type)),g.addEventListener("dragstart",C=>{C.dataTransfer.setData("text/plain",_.type),C.dataTransfer.effectAllowed="copy",this._setMode("insert:"+_.type)}),g.addEventListener("dragend",()=>{this._mode&&this._mode.startsWith("insert:")&&this._setMode(null)}),this._toolbar.appendChild(g)}const t=document.createElement("span");t.className="sep",this._toolbar.appendChild(t);const e=document.createElement("button");e.textContent="🗑 Delete",e.className="danger",e.title="Delete mode — click a node to remove it",e.addEventListener("click",()=>this._toggleDeleteMode()),this._toolbar.appendChild(e),this._deleteBtn=e;const s=document.createElement("span");s.className="sep",this._toolbar.appendChild(s);const r=document.createElement("label");r.textContent="Lang ";const l=document.createElement("select");l.innerHTML='<option value="de">Deutsch</option><option value="en">English</option>',l.value=(this.getAttribute("lang")||"de").toLowerCase(),l.addEventListener("change",()=>{this.setAttribute("lang",l.value),this._keywords=null,this._onTreeChange()}),r.appendChild(l),this._toolbar.appendChild(r),this._langSelect=l;const i=document.createElement("label");i.textContent="Size ";const n=document.createElement("input");n.type="number",n.min="8",n.max="32",n.value=this.getAttribute("font-size")||"14",n.addEventListener("change",()=>{this.setAttribute("font-size",n.value),this._onTreeChange()}),i.appendChild(n),this._toolbar.appendChild(i),this._fsInput=n;const u=document.createElement("label");u.textContent="Scale ";const c=document.createElement("input");c.type="number",c.min="0.25",c.max="3",c.step="0.25",c.value=this.getAttribute("scale")||"1",c.addEventListener("change",()=>{const _=parseFloat(c.value);_>0?this.setAttribute("scale",String(_)):c.value=this.getAttribute("scale")||"1",this._onTreeChange()}),u.appendChild(c),this._toolbar.appendChild(u),this._scaleInput=c;const a=document.createElement("label");a.textContent="Color Mode ";const m=document.createElement("select");m.innerHTML='<option value="color">Color</option><option value="greyscale">Greyscale</option><option value="bw">Black & White</option>',m.value=this.getAttribute("color-mode")||"color",m.addEventListener("change",()=>{this.setAttribute("color-mode",m.value),this._onTreeChange()}),a.appendChild(m),this._toolbar.appendChild(a),this._colorModeSelect=m;const d=document.createElement("span");d.className="sep",this._toolbar.appendChild(d);const p=document.createElement("button");p.textContent="💾 Save",p.title="Save structogram as JSON",p.addEventListener("click",()=>this._downloadJSON()),this._toolbar.appendChild(p);const h=document.createElement("button");h.textContent="📂 Load",h.title="Load structogram from JSON file",h.addEventListener("click",()=>this._triggerLoadJSON()),this._toolbar.appendChild(h),this._fileInput=document.createElement("input"),this._fileInput.type="file",this._fileInput.accept=".json,application/json",this._fileInput.style.display="none",this._fileInput.addEventListener("change",_=>this._handleFileLoad(_)),this._toolbar.appendChild(this._fileInput);const f=document.createElement("button");f.textContent="🖼 PNG",f.title="Export as PNG image",f.addEventListener("click",()=>this._downloadImage("png")),this._toolbar.appendChild(f);const b=document.createElement("button");b.textContent="📐 SVG",b.title="Export as SVG image",b.addEventListener("click",()=>this._downloadImage("svg")),this._toolbar.appendChild(b)}_toggleInsertMode(t){const e="insert:"+t;this._setMode(this._mode===e?null:e)}_toggleDeleteMode(){this._setMode(this._mode==="delete"?null:"delete")}_setMode(t){this._mode=t;for(const e of this._toolbar.querySelectorAll("button[data-type]"))e.classList.toggle("active",t==="insert:"+e.dataset.type);this._deleteBtn.classList.toggle("active",t==="delete"),this._editorArea.classList.remove("mode-insert","mode-delete"),t&&t.startsWith("insert:")?this._editorArea.classList.add("mode-insert"):t==="delete"&&this._editorArea.classList.add("mode-delete"),this._render()}_resolveWidth(){const t=parseFloat(this.getAttribute("scale"))||1,e=this._editorArea.clientWidth||this.clientWidth||this.getBoundingClientRect().width||600;return Math.round(e/t)}_render(){if(!this._tree)return;const t=parseInt(this.getAttribute("font-size"),10)||14,e=this._resolveWidth(),s=this.getAttribute("color-mode"),r=this._editorArea.querySelector("svg");r&&r.remove();const l=this._mode&&this._mode.startsWith("insert:")||this._mode&&this._mode.startsWith("move:");ft(l?qt:0);const i=Tt(this._tree,{width:e,fontSize:t,colorMode:s});ft(0),this._addInteractivity(i,e,t),this._editorArea.insertBefore(i,this._overlay)}_addInteractivity(t,e,s){const r=this._mode&&this._mode.startsWith("insert:"),l=this._mode==="delete",i=this._mode&&this._mode.startsWith("move:");(r||i)&&this._addInsertTargets(t,e,s,i?this._mode.replace("move:",""):null),l&&this._addDeleteTargets(t,e,s),!r&&!l&&!i&&(this._addEditTargets(t,e,s),this._addDragTargets(t,e,s)),i&&t.addEventListener("click",()=>this._setMode(null))}_computeLayout(t,e,s,r,l,i){const n=new Map;return this._layoutNode(t,e,s,r,l,n,i),n}_layoutNode(t,e,s,r,l,i,n){if(!t)return 0;const u=r-16,c=6,a=20,m=40,d=n?qt:0,p=h=>{const f=this._wrapText(h||"",u,l),b=l*1.3;return Math.max(m,f.length*b+c*2)};switch(t.type){case"InsertNode":{t.id&&i.set(t.id,{x:e,y:s,w:r,h:Math.max(d,4),type:"InsertNode"});const h=this._layoutNode(t.followElement,e,s+d,r,l,i,n);return d+h}case"Placeholder":return 0;case"TaskNode":case"InputNode":case"OutputNode":{let h=t.text||"";t.type==="InputNode"&&(h="▶ "+h),t.type==="OutputNode"&&(h="◀ "+h);const f=p(h);t.id&&i.set(t.id,{x:e,y:s,w:r,h:f,type:t.type,text:t.text});const b=this._layoutNode(t.followElement,e,s+f,r,l,i,n);return f+b}case"InsertCase":{const h=p(t.text||"");t.id&&i.set(t.id,{x:e,y:s,w:r,h,type:"InsertCase",text:t.text});const f=this._layoutNode(t.followElement,e,s+h,r,l,i,n);return h+f}case"BranchNode":{const h=p(t.text||""),f=l*1.3+c,b=l*1.3+c,_=h+f+b,g=2,C=t.columnWidths&&t.columnWidths.length===g?t.columnWidths.map(S=>r*S):[r/g,r/g],L=this._layoutNode(t.trueChild,e,s+_,C[0],l,i,n),I=this._layoutNode(t.falseChild,e+C[0],s+_,C[1],l,i,n),N=Math.max(L,I),y=_+N;t.id&&i.set(t.id,{x:e,y:s,w:r,h:y,type:"BranchNode",text:t.text});const J=this._layoutNode(t.followElement,e,s+y,r,l,i,n);return y+J}case"CaseNode":{const h=(t.cases?t.cases.length:0)+(t.defaultOn?1:0),f=t.columnWidths&&t.columnWidths.length===h?t.columnWidths.map(y=>r*y):Array(h).fill(r/h),b=p(t.text||""),_=l*1.3+c,g=b+_;let C=0,L=e;for(let y=0;y<(t.cases||[]).length;y++){const J=this._layoutNode(t.cases[y],L,s+g,f[y],l,i,n);C=Math.max(C,J),L+=f[y]}if(t.defaultOn&&t.defaultNode){const y=this._layoutNode(t.defaultNode,L,s+g,f[h-1],l,i,n);C=Math.max(C,y)}const I=g+C;t.id&&i.set(t.id,{x:e,y:s,w:r,h:I,type:"CaseNode",text:t.text});const N=this._layoutNode(t.followElement,e,s+I,r,l,i,n);return I+N}case"HeadLoopNode":case"CountLoopNode":{const h=p(t.text||""),f=r-a,b=this._layoutNode(t.child,e+a,s+h,f,l,i,n),_=Math.max(b,h*.5),g=h+_;t.id&&i.set(t.id,{x:e,y:s,w:r,h:g,type:t.type,text:t.text});const C=this._layoutNode(t.followElement,e,s+g,r,l,i,n);return g+C}case"FootLoopNode":{const h=r-a,f=p(t.text||""),b=this._layoutNode(t.child,e+a,s,h,l,i,n),g=Math.max(b,f*.5)+f;t.id&&i.set(t.id,{x:e,y:s,w:r,h:g,type:"FootLoopNode",text:t.text});const C=this._layoutNode(t.followElement,e,s+g,r,l,i,n);return g+C}case"FunctionNode":{let h=t.text||"";t.parameters&&t.parameters.length?h+="("+t.parameters.map(N=>N.parName||"").join(", ")+")":h+="()",h+=" {";const f=p(h),b=r-a,_=this._layoutNode(t.child,e+a,s+f,b,l,i,n),g=Math.max(_,f*.5),C=m*.6,L=f+g+C;t.id&&i.set(t.id,{x:e,y:s,w:r,h:L,type:"FunctionNode",text:t.text});const I=this._layoutNode(t.followElement,e,s+L,r,l,i,n);return L+I}case"TryCatchNode":{const h=r-a,f=p("Try"),b=this._layoutNode(t.tryChild,e+a,s+f,h,l,i,n),_=Math.max(b,f*.5);let g="Catch";t.text&&(g+=" ("+t.text+")");const C=p(g),L=s+f+_,I=this._layoutNode(t.catchChild,e+a,L+C,h,l,i,n),N=Math.max(I,C*.5),y=f+_+C+N;t.id&&i.set(t.id,{x:e,y:s,w:r,h:y,type:"TryCatchNode",text:t.text});const J=this._layoutNode(t.followElement,e,s+y,r,l,i,n);return y+J}default:return 0}}_wrapText(t,e,s){if(!t)return[""];const l=document.createElement("canvas").getContext("2d");l.font=`${s}px sans-serif`;const i=t.split(`
189
+ `),n=[];for(const u of i){if(!u||l.measureText(u).width<=e){n.push(u||"");continue}const c=u.split(/\s+/);let a="";for(const m of c){const d=a?a+" "+m:m;l.measureText(d).width<=e?a=d:(a&&n.push(a),a=m)}a&&n.push(a)}return n.length?n:[""]}_annotatePositions(t,e,s,r,l,i){}_addInsertTargets(t,e,s,r){const l=this._computeLayout(this._tree,0,0,e,s,!0),i="http://www.w3.org/2000/svg",n=[...l.entries()].filter(([,u])=>u.type==="InsertNode").sort(([,u],[,c])=>c.w*c.h-u.w*u.h);for(const[u,c]of n){const a=document.createElementNS(i,"rect");a.setAttribute("x",c.x+2),a.setAttribute("y",c.y),a.setAttribute("width",c.w-4),a.setAttribute("height",c.h),a.setAttribute("fill",kt),a.setAttribute("stroke","none"),a.setAttribute("rx","3"),a.style.cursor="pointer",a.style.transition="fill 0.15s",a.addEventListener("mouseenter",()=>a.setAttribute("fill",Xt)),a.addEventListener("mouseleave",()=>a.setAttribute("fill",kt)),a.addEventListener("click",d=>{d.stopPropagation(),r?(this._tree=this._prepTree(he(this._tree,r,u)),this._setMode(null),this._onTreeChange()):this._handleInsert(u)}),a.addEventListener("dragover",d=>{d.preventDefault(),d.dataTransfer.dropEffect="copy",a.setAttribute("fill",Xt)}),a.addEventListener("dragleave",()=>a.setAttribute("fill",kt)),a.addEventListener("drop",d=>{d.preventDefault();const p=d.dataTransfer.getData("text/plain");p&&(this._tree=this._prepTree(Yt(this._tree,u,p)),this._setMode(null),this._onTreeChange())});const m=document.createElementNS(i,"text");m.setAttribute("x",c.x+c.w/2),m.setAttribute("y",c.y+c.h/2),m.setAttribute("text-anchor","middle"),m.setAttribute("dominant-baseline","central"),m.setAttribute("fill","rgba(1, 116, 96, 0.8)"),m.setAttribute("font-size","16"),m.setAttribute("font-weight","bold"),m.setAttribute("pointer-events","none"),m.textContent="+",t.appendChild(a),t.appendChild(m)}}_handleInsert(t){if(!this._mode||!this._mode.startsWith("insert:"))return;const e=this._mode.replace("insert:","");this._tree=this._prepTree(Yt(this._tree,t,e)),this._setMode(null),this._onTreeChange()}_addDeleteTargets(t,e,s){const r=this._computeLayout(this._tree,0,0,e,s,!1),l="http://www.w3.org/2000/svg",i=[...r.entries()].filter(([,n])=>n.type!=="InsertNode"&&n.type!=="Placeholder"&&n.type!=="InsertCase").sort(([,n],[,u])=>u.w*u.h-n.w*n.h);for(const[n,u]of i){const c=document.createElementNS(l,"rect");c.setAttribute("x",u.x),c.setAttribute("y",u.y),c.setAttribute("width",u.w),c.setAttribute("height",u.h),c.setAttribute("fill","transparent"),c.setAttribute("stroke","none"),c.style.cursor="pointer",c.style.transition="fill 0.15s",c.addEventListener("mouseenter",()=>c.setAttribute("fill",pe)),c.addEventListener("mouseleave",()=>c.setAttribute("fill","transparent")),c.addEventListener("click",a=>{a.stopPropagation(),this._tree=this._prepTree(ie(this._tree,n)),this._setMode(null),this._onTreeChange()}),t.appendChild(c)}}_addEditTargets(t,e,s){const r=this._computeLayout(this._tree,0,0,e,s,!1),l="http://www.w3.org/2000/svg",i=[...r.entries()].filter(([,n])=>n.type!=="InsertNode"&&n.type!=="Placeholder").sort(([,n],[,u])=>u.w*u.h-n.w*n.h);for(const[n,u]of i){const c=document.createElementNS(l,"rect");c.setAttribute("x",u.x),c.setAttribute("y",u.y),c.setAttribute("width",u.w),c.setAttribute("height",u.h),c.setAttribute("fill","transparent"),c.setAttribute("stroke","none"),c.style.cursor="pointer",c.addEventListener("dblclick",a=>{a.stopPropagation(),this._showEditOverlay(n,u)}),t.appendChild(c)}}_showEditOverlay(t,e){const s=ct(this._tree,t);if(!s)return;this._hideEditOverlay();const r=this._editorArea.querySelector("svg");if(!r)return;const l=r.getBoundingClientRect(),i=this._editorArea.getBoundingClientRect(),n=r.viewBox.baseVal,u=l.width/n.width,c=l.height/n.height,a=Math.min(u,c),m=parseInt(this.getAttribute("font-size"),10)||14,d=this._nodeTextHeight(s,e.w,m),p=(e.x-n.x)*a+l.left-i.left,h=(e.y-n.y)*a+l.top-i.top,f=e.w*a,b=d*a;this._overlay.style.display="flex",this._overlay.style.left=p+"px",this._overlay.style.top=h+"px",this._overlay.style.width=f+"px",this._overlay.style.height=b+"px",this._overlay.innerHTML="";const _=document.createElement("textarea");_.value=s.text||"",_.rows=Math.max(2,(s.text||"").split(`
190
+ `).length),_.style.height="100%",_.style.resize="none";const g=document.createElement("button");g.className="ok",g.textContent="✓";const C=document.createElement("button");C.className="cancel",C.textContent="✗";const L=()=>{this._tree=this._prepTree(ue(this._tree,t,_.value)),this._hideEditOverlay(),this._onTreeChange()},I=()=>this._hideEditOverlay();_.addEventListener("keydown",N=>{N.key==="Enter"&&!N.shiftKey&&(N.preventDefault(),L()),N.key==="Escape"&&(N.preventDefault(),I())}),g.addEventListener("click",L),C.addEventListener("click",I),this._overlay.appendChild(_),this._overlay.appendChild(g),this._overlay.appendChild(C),requestAnimationFrame(()=>{_.focus(),_.select()})}_hideEditOverlay(){this._overlay.style.display="none",this._overlay.innerHTML=""}_nodeTextHeight(t,e,s){const r=e-16,l=6,i=40,n=u=>{const c=this._wrapText(u||"",r,s),a=s*1.3;return Math.max(i,c.length*a+l*2)};switch(t.type){case"BranchNode":return n(t.text||"");case"CaseNode":return n(t.text||"");case"HeadLoopNode":case"CountLoopNode":return n(t.text||"");case"FootLoopNode":return n(t.text||"");case"FunctionNode":{let u=t.text||"";return t.parameters&&t.parameters.length?u+="("+t.parameters.map(c=>c.parName||"").join(", ")+")":u+="()",n(u)}case"TryCatchNode":return n(t.text||"");default:return n(t.text||"")}}_addDragTargets(t,e,s){const r=this._computeLayout(this._tree,0,0,e,s,!1),l="http://www.w3.org/2000/svg";for(const[i,n]of r){if(n.type==="InsertNode"||n.type==="Placeholder"||n.type==="InsertCase")continue;const u=14,c=n.x+n.w-u-4,a=n.y+4,m=document.createElementNS(l,"text");m.setAttribute("x",c+u/2),m.setAttribute("y",a+u/2),m.setAttribute("text-anchor","middle"),m.setAttribute("dominant-baseline","central"),m.setAttribute("font-size","12"),m.setAttribute("fill","rgba(0,0,0,0.3)"),m.textContent="⠿",m.style.cursor="grab",m.style.userSelect="none";const d=document.createElementNS(l,"rect");d.setAttribute("x",c),d.setAttribute("y",a),d.setAttribute("width",u),d.setAttribute("height",u),d.setAttribute("fill","transparent"),d.style.cursor="grab";const p=document.createElementNS(l,"g");p.appendChild(d),p.appendChild(m);const h=document.createElementNS(l,"rect");h.setAttribute("x",n.x),h.setAttribute("y",n.y),h.setAttribute("width",n.w),h.setAttribute("height",n.h),h.setAttribute("fill","transparent"),h.setAttribute("stroke","none"),p.addEventListener("mousedown",f=>{f.stopPropagation(),this._startDrag(i)}),t.appendChild(p)}}_startDrag(t){this._setMode("move:"+t)}_syncTreeToPseudocode(){if(!this._syncing){this._syncing=!0;try{const t=Gt(this._tree,this._getKeywords());this._textarea.value=t,this._errorEl.style.display="none"}catch{}this._syncing=!1}}_onPseudocodeInput(){clearTimeout(this._debounceTimer),this._debounceTimer=setTimeout(()=>this._syncPseudocodeToTree(),600)}_syncPseudocodeToTree(){if(!this._syncing){this._syncing=!0,clearTimeout(this._debounceTimer);try{const t=this._textarea.value;if(!t.trim()){this._syncing=!1;return}const e=this._prepTree(Nt(t,this._getKeywords()));this._tree=e,this._errorEl.style.display="none",this._render(),this._emitChange()}catch(t){this._errorEl.textContent="Parse error: "+t.message,this._errorEl.style.display="block"}this._syncing=!1}}_onTreeChange(){this._render(),this._syncTreeToPseudocode(),this._emitChange()}_emitChange(){this.dispatchEvent(new CustomEvent("change",{detail:{tree:ot(this._tree)},bubbles:!0}))}saveJSON(){return this._tree?JSON.stringify(Ut(this._tree),null,2):"{}"}loadJSON(t){const e=typeof t=="string"?JSON.parse(t):t;this._tree=this._prepTree(e),this._render(),this._syncTreeToPseudocode(),this._emitChange()}async exportImage(t="png"){const e=parseInt(this.getAttribute("font-size"),10)||14,s=this._resolveWidth(),r=this.getAttribute("color-mode");ft(0);const l=Tt(this._tree,{width:s,fontSize:e,colorMode:r});ft(0);const n=new XMLSerializer().serializeToString(l);return t==="svg"?new Blob([n],{type:"image/svg+xml"}):new Promise((u,c)=>{const a=new Image,m=new Blob([n],{type:"image/svg+xml"}),d=URL.createObjectURL(m);a.onload=()=>{const p=document.createElement("canvas"),h=8;p.width=a.width*h,p.height=a.height*h;const f=p.getContext("2d");f.fillStyle="#fff",f.fillRect(0,0,a.width,a.height),f.drawImage(a,0,0,p.width,p.height),URL.revokeObjectURL(d),p.toBlob(b=>b?u(b):c(new Error("canvas.toBlob failed")),"image/png")},a.onerror=()=>{URL.revokeObjectURL(d),c(new Error("SVG load failed"))},a.src=d})}change(t){this._tree=this._prepTree(t),this._render(),this._syncTreeToPseudocode(),this._emitChange()}_downloadJSON(){const t=this.saveJSON(),e=new Blob([t],{type:"application/json"});this._downloadBlob(e,"structogram.json")}_triggerLoadJSON(){this._fileInput.value="",this._fileInput.click()}_handleFileLoad(t){const e=t.target.files&&t.target.files[0];if(!e)return;const s=new FileReader;s.onload=()=>{try{this.loadJSON(s.result)}catch(r){console.error("struktolab-editor: invalid JSON file",r)}},s.readAsText(e)}async _downloadImage(t){try{const e=await this.exportImage(t),s=t==="svg"?"svg":"png";this._downloadBlob(e,`structogram.${s}`)}catch(e){console.error("struktolab-editor: export failed",e)}}_downloadBlob(t,e){const s=URL.createObjectURL(t),r=document.createElement("a");r.href=s,r.download=e,r.style.display="none",document.body.appendChild(r),r.click(),setTimeout(()=>{document.body.removeChild(r),URL.revokeObjectURL(s)},100)}}customElements.get("struktolab-editor")||customElements.define("struktolab-editor",Vt),Y.KEYWORDS_DE=gt,Y.KEYWORDS_EN=Pt,Y.StruktolabEditor=Vt,Y.generateCode=jt,Y.parsePseudocode=Nt,Y.renderStructogramSVG=Tt,Y.stripInsertNodes=Ut,Y.treeToPseudocode=Gt,Object.defineProperty(Y,Symbol.toStringTag,{value:"Module"})}));