diff --git a/lib/sequence-diagram.js b/lib/sequence-diagram.js index dc7b7e0..b156df0 100644 --- a/lib/sequence-diagram.js +++ b/lib/sequence-diagram.js @@ -2736,6 +2736,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { blockType, tag: this.textFormatter(tag), label: this.textFormatter(label), + canHide: true, left: leftGAgent.id, right: rightGAgent.id, ln, @@ -2881,6 +2882,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { type: 'block begin', blockType, tag: this.textFormatter(tag), + canHide: false, label: this.textFormatter(label), left: details.leftGAgent.id, right: details.rightGAgent.id, @@ -3898,6 +3900,7 @@ define('sequence/components/BaseComponent',[],() => { } separationPre(/*stage, { + renderer, theme, agentInfos, visibleAgentIDs, @@ -3911,6 +3914,7 @@ define('sequence/components/BaseComponent',[],() => { } separation(/*stage, { + renderer, theme, agentInfos, visibleAgentIDs, @@ -3924,6 +3928,7 @@ define('sequence/components/BaseComponent',[],() => { } renderPre(/*stage, { + renderer, theme, agentInfos, textSizer, @@ -3934,6 +3939,7 @@ define('sequence/components/BaseComponent',[],() => { } render(/*stage, { + renderer, topY, primaryY, fillLayer, @@ -3949,6 +3955,22 @@ define('sequence/components/BaseComponent',[],() => { }*/) { // return bottom Y coordinate } + + renderHidden(/*stage, { + (same args as render, with primaryY = topY) + }*/) { + } + + shouldHide(/*stage, { + renderer, + theme, + agentInfos, + textSizer, + state, + components, + }*/) { + // return {self, nest} + } } BaseComponent.cleanRenderPreResult = ({ @@ -4070,6 +4092,12 @@ define('sequence/components/Block',[ 'x2': agentInfoR.x, 'y2': y, })); + } else if(blockInfo.canHide) { + clickable.setAttribute( + 'class', + clickable.getAttribute('class') + + (blockInfo.hide ? ' collapsed' : ' expanded') + ); } return y + labelHeight + config.section.padding.top; @@ -4086,8 +4114,11 @@ define('sequence/components/Block',[ } storeBlockInfo(stage, env) { + const canHide = stage.canHide; const blockInfo = { type: stage.blockType, + canHide, + hide: canHide && env.renderer.isCollapsed(stage.ln), hold: null, startY: null, }; @@ -4125,6 +4156,14 @@ define('sequence/components/Block',[ return super.render(stage, env, true); } + + shouldHide({left}, env) { + const blockInfo = env.state.blocks.get(left); + return { + self: false, + nest: blockInfo.hide ? 1 : 0, + }; + } } class BlockEnd extends BaseComponent { @@ -4148,7 +4187,12 @@ define('sequence/components/Block',[ const agentInfoL = env.agentInfos.get(left); const agentInfoR = env.agentInfos.get(right); - let shapes = config.boxRenderer({ + let renderFn = config.boxRenderer; + if(blockInfo.hide) { + renderFn = config.collapsedBoxRenderer || renderFn; + } + + let shapes = renderFn({ x: agentInfoL.x, y: blockInfo.startY, width: agentInfoR.x - agentInfoL.x, @@ -4168,6 +4212,14 @@ define('sequence/components/Block',[ return env.primaryY + config.margin.bottom + env.theme.actionMargin; } + + shouldHide({left}, env) { + const blockInfo = env.state.blocks.get(left); + return { + self: false, + nest: blockInfo.hide ? -1 : 0, + }; + } } BaseComponent.register('block begin', new BlockBegin()); @@ -4254,6 +4306,27 @@ define('sequence/components/Parallel',[ env.makeRegion = originalMakeRegion; return bottomY; } + + renderHidden(stage, env) { + stage.stages.forEach((subStage) => { + const component = env.components.get(subStage.type); + component.renderHidden(subStage, env); + }); + } + + shouldHide(stage, env) { + const result = { + self: false, + nest: 0, + }; + stage.stages.forEach((subStage) => { + const component = env.components.get(subStage.type); + const hide = component.shouldHide(subStage, env) || {}; + result.self = (result.self || Boolean(hide.self)); + result.nest += (hide.nest || 0); + }); + return result; + } } BaseComponent.register('parallel', new Parallel()); @@ -4276,6 +4349,10 @@ define('sequence/components/Marker',['./BaseComponent'], (BaseComponent) => { render({name}, {topY, state}) { state.marks.set(name, topY); } + + renderHidden(stage, env) { + this.render(stage, env); + } } class Async extends BaseComponent { @@ -4633,6 +4710,12 @@ define('sequence/components/AgentCap',[ }); return maxEnd + env.theme.actionMargin; } + + renderHidden({agentIDs}, env) { + agentIDs.forEach((id) => { + env.drawAgentLine(id, env.topY, !this.begin); + }); + } } BaseComponent.register('agent begin', new AgentCap(true)); @@ -4674,6 +4757,10 @@ define('sequence/components/AgentHighlight',['./BaseComponent'], (BaseComponent) }); return env.primaryY + env.theme.actionMargin; } + + renderHidden(stage, env) { + this.render(stage, env); + } } BaseComponent.register('agent highlight', new AgentHighlight()); @@ -5164,6 +5251,10 @@ define('sequence/components/Connect',[ }); return env.primaryY + env.theme.actionMargin; } + + renderHidden(stage, env) { + this.render(stage, env); + } } class ConnectDelayEnd extends Connect { @@ -5669,6 +5760,7 @@ define('sequence/Renderer',[ this.knownThemeDefs = new Set(); this.knownDefs = new Set(); this.highlights = new Map(); + this.collapsed = new Set(); this.currentHighlight = -1; this.buildStaticElements(); this.components.forEach((component) => { @@ -5679,7 +5771,6 @@ define('sequence/Renderer',[ _bindMethods() { this.separationStage = this.separationStage.bind(this); this.renderStage = this.renderStage.bind(this); - this.addSeparation = this.addSeparation.bind(this); this.addThemeDef = this.addThemeDef.bind(this); this.addDef = this.addDef.bind(this); } @@ -5775,9 +5866,37 @@ define('sequence/Renderer',[ info2.separations.set(agentID1, Math.max(d2, dist)); } + checkHidden(stage) { + const component = this.components.get(stage.type); + const env = { + renderer: this, + theme: this.theme, + agentInfos: this.agentInfos, + textSizer: this.sizer, + state: this.state, + components: this.components, + }; + + const hide = component.shouldHide(stage, env) || {}; + + const wasHidden = (this.hideNest > 0); + this.hideNest += hide.nest || 0; + const isHidden = (this.hideNest > 0); + + if(this.hideNest < 0) { + throw new Error('Unexpected nesting in ' + stage.type); + } + if(wasHidden === isHidden) { + return isHidden; + } else { + return Boolean(hide.self); + } + } + separationStage(stage) { const agentSpaces = new Map(); const agentIDs = this.visibleAgentIDs.slice(); + const seps = []; const addSpacing = (agentID, {left, right}) => { const current = agentSpaces.get(agentID); @@ -5785,30 +5904,47 @@ define('sequence/Renderer',[ current.right = Math.max(current.right, right); }; + const addSeparation = (agentID1, agentID2, dist) => { + seps.push({agentID1, agentID2, dist}); + }; + this.agentInfos.forEach((agentInfo) => { const rad = agentInfo.currentRad; agentInfo.currentMaxRad = rad; agentSpaces.set(agentInfo.id, {left: rad, right: rad}); }); + const env = { + renderer: this, theme: this.theme, agentInfos: this.agentInfos, visibleAgentIDs: this.visibleAgentIDs, momentaryAgentIDs: agentIDs, textSizer: this.sizer, addSpacing, - addSeparation: this.addSeparation, + addSeparation, state: this.state, components: this.components, }; + const component = this.components.get(stage.type); if(!component) { throw new Error('Unknown component: ' + stage.type); } + component.separationPre(stage, env); component.separation(stage, env); + + if(this.checkHidden(stage)) { + return; + } + array.mergeSets(agentIDs, this.visibleAgentIDs); + seps.forEach(({agentID1, agentID2, dist}) => { + this.addSeparation(agentID1, agentID2, dist); + }); + agentIDs.forEach((agentIDR) => { const infoR = this.agentInfos.get(agentIDR); const sepR = agentSpaces.get(agentIDR); @@ -5885,6 +6021,13 @@ define('sequence/Renderer',[ list.push(o); } + forwardEvent(source, sourceEvent, forwardEvent, forwardArgs) { + source.addEventListener( + sourceEvent, + this.trigger.bind(this, forwardEvent, forwardArgs) + ); + } + renderStage(stage) { this.agentInfos.forEach((agentInfo) => { const rad = agentInfo.currentRad; @@ -5892,6 +6035,7 @@ define('sequence/Renderer',[ }); const envPre = { + renderer: this, theme: this.theme, agentInfos: this.agentInfos, textSizer: this.sizer, @@ -5905,10 +6049,6 @@ define('sequence/Renderer',[ const topY = this.checkAgentRange(agentIDs, asynchronousY); - const eventOut = () => { - this.trigger('mouseout'); - }; - const makeRegion = ({ stageOverride = null, unmasked = false, @@ -5917,18 +6057,16 @@ define('sequence/Renderer',[ const targetStage = (stageOverride || stage); this.addHighlightObject(targetStage.ln, o); o.setAttribute('class', 'region'); - o.addEventListener('mouseenter', () => { - this.trigger('mouseover', [targetStage]); - }); - o.addEventListener('mouseleave', eventOut); - o.addEventListener('click', () => { - this.trigger('click', [targetStage]); - }); + this.forwardEvent(o, 'mouseenter', 'mouseover', [targetStage]); + this.forwardEvent(o, 'mouseleave', 'mouseout', [targetStage]); + this.forwardEvent(o, 'click', 'click', [targetStage]); + this.forwardEvent(o, 'dblclick', 'dblclick', [targetStage]); (unmasked ? this.unmaskedShapes : this.shapes).appendChild(o); return o; }; const env = { + renderer: this, topY, primaryY: topY + topShift, fillLayer: this.backgroundFills, @@ -5950,9 +6088,15 @@ define('sequence/Renderer',[ components: this.components, }; - const bottomY = Math.max(topY, component.render(stage, env) || 0); - this.markAgentRange(agentIDs, bottomY); + let bottomY = topY; + if(this.checkHidden(stage)) { + env.primaryY = topY; + component.renderHidden(stage, env); + } else { + bottomY = Math.max(bottomY, component.render(stage, env) || 0); + } + this.markAgentRange(agentIDs, bottomY); this.currentY = bottomY; } @@ -6057,6 +6201,7 @@ define('sequence/Renderer',[ component.resetState(this.state); }); this.currentY = 0; + this.hideNest = 0; } _reset(theme) { @@ -6103,6 +6248,49 @@ define('sequence/Renderer',[ this.currentHighlight = line; } + isCollapsed(line) { + return this.collapsed.has(line); + } + + setCollapseAll(collapsed) { + if(collapsed) { + throw new Error('Cannot collapse all'); + } else { + if(this.collapsed.size === 0) { + return false; + } + this.collapsed.clear(); + } + return true; + } + + _setCollapsed(line, collapsed) { + if(typeof line !== 'number') { + return false; + } + if(collapsed === this.isCollapsed(line)) { + return false; + } + if(collapsed) { + this.collapsed.add(line); + } else { + this.collapsed.delete(line); + } + return true; + } + + setCollapsed(line, collapsed = true) { + if(line === null) { + return this.setCollapseAll(collapsed); + } + if(Array.isArray(line)) { + return line + .map((ln) => this._setCollapsed(ln, collapsed)) + .some((changed) => changed); + } + return this._setCollapsed(line, collapsed); + } + render(sequence) { const prevHighlight = this.currentHighlight; const oldTheme = this.theme; @@ -7143,6 +7331,13 @@ define('sequence/themes/Basic',[ 'rx': 2, 'ry': 2, }), + collapsedBoxRenderer: BaseTheme.renderRef.bind(null, { + 'fill': '#FFFFFF', + 'stroke': '#000000', + 'stroke-width': 1.5, + 'rx': 2, + 'ry': 2, + }), section: SHARED_BLOCK_SECTION, sepRenderer: SVGShapes.renderLine.bind(null, { 'stroke': '#000000', @@ -7527,6 +7722,11 @@ define('sequence/themes/Monospace',[ 'stroke': '#000000', 'stroke-width': 2, }), + collapsedBoxRenderer: BaseTheme.renderRef.bind(null, { + 'fill': '#FFFFFF', + 'stroke': '#000000', + 'stroke-width': 2, + }), section: SHARED_BLOCK_SECTION, sepRenderer: SVGShapes.renderLine.bind(null, { 'stroke': '#000000', @@ -7919,6 +8119,13 @@ define('sequence/themes/Chunky',[ 'rx': 5, 'ry': 5, }), + collapsedBoxRenderer: BaseTheme.renderRef.bind(null, { + 'fill': '#FFFFFF', + 'stroke': '#000000', + 'stroke-width': 4, + 'rx': 5, + 'ry': 5, + }), section: SHARED_BLOCK_SECTION, sepRenderer: SVGShapes.renderLine.bind(null, { 'stroke': '#000000', @@ -8712,6 +8919,7 @@ define('sequence/themes/Sketch',[ bottom: 0, }, boxRenderer: null, + collapsedBoxRenderer: null, section: SHARED_BLOCK_SECTION, sepRenderer: null, }, @@ -8921,6 +9129,8 @@ define('sequence/themes/Sketch',[ this.blocks.ref.boxRenderer = this.renderRefBlock.bind(this); this.blocks[''].boxRenderer = this.renderBlock.bind(this); + this.blocks[''].collapsedBoxRenderer = + this.renderCollapsedBlock.bind(this); this.blocks.ref.section.tag.boxRenderer = this.renderTag; this.blocks[''].section.tag.boxRenderer = this.renderTag; this.blocks[''].sepRenderer = this.renderSeparator.bind(this); @@ -9318,6 +9528,10 @@ define('sequence/themes/Sketch',[ return this.renderBox(position, {fill: 'none', thick: true}); } + renderCollapsedBlock(position) { + return this.renderRefBlock(position); + } + renderTag({x, y, width, height}) { const x2 = x + width; const y2 = y + height; @@ -9486,9 +9700,14 @@ define('sequence/SequenceDiagram',[ this.renderer = new Renderer(Object.assign({themes}, options)); this.exporter = new Exporter(); this.renderer.addEventForwarding(this); + this.latestProcessed = null; + this.isInteractive = false; if(options.container) { options.container.appendChild(this.dom()); } + if(options.interactive) { + this.addInteractivity(); + } if(typeof this.code === 'string') { this.render(); } @@ -9501,6 +9720,7 @@ define('sequence/SequenceDiagram',[ themes: this.renderer.getThemes(), namespace: null, components: this.renderer.components, + interactive: this.isInteractive, SVGTextBlockClass: this.renderer.SVGTextBlockClass, }, options)); } @@ -9523,10 +9743,40 @@ define('sequence/SequenceDiagram',[ this.renderer.addTheme(theme); } - setHighlight(line = null) { + setHighlight(line) { this.renderer.setHighlight(line); } + isCollapsed(line) { + return this.renderer.isCollapsed(line); + } + + setCollapsed(line, collapsed = true, {render = true} = {}) { + if(!this.renderer.setCollapsed(line, collapsed)) { + return false; + } + if(render && this.latestProcessed) { + this.render(this.latestProcessed); + } + return true; + } + + collapse(line, options) { + return this.setCollapsed(line, true, options); + } + + expand(line, options) { + return this.setCollapsed(line, false, options); + } + + toggleCollapsed(line, options) { + return this.setCollapsed(line, !this.isCollapsed(line), options); + } + + expandAll(options) { + return this.setCollapsed(null, false, options); + } + getThemeNames() { return this.renderer.getThemeNames(); } @@ -9593,6 +9843,8 @@ define('sequence/SequenceDiagram',[ processed = this.process(this.code); } this.renderer.render(processed); + this.latestProcessed = processed; + this.trigger('render', [this]); } finally { if(dom.parentNode !== originalParent) { document.body.removeChild(dom); @@ -9613,6 +9865,17 @@ define('sequence/SequenceDiagram',[ } } + addInteractivity() { + if(this.isInteractive) { + return; + } + this.isInteractive = true; + + this.addEventListener('click', (element) => { + this.toggleCollapsed(element.ln); + }); + } + extractCodeFromSVG(svg) { return extractCodeFromSVG(svg); } @@ -9622,6 +9885,17 @@ define('sequence/SequenceDiagram',[ } } + function datasetBoolean(value) { + return value !== undefined && value !== 'false'; + } + + function parseTagOptions(element) { + return { + namespace: element.dataset.sdNamespace || null, + interactive: datasetBoolean(element.dataset.sdInteractive), + }; + } + function convert(element, code = null, options = {}) { if(element.tagName === 'svg') { return null; @@ -9633,7 +9907,13 @@ define('sequence/SequenceDiagram',[ options = code; code = options.code; } - const diagram = new SequenceDiagram(code, options); + + const tagOptions = parseTagOptions(element); + + const diagram = new SequenceDiagram( + code, + Object.assign(tagOptions, options) + ); const newElement = diagram.dom(); element.parentNode.insertBefore(newElement, element); element.parentNode.removeChild(element); diff --git a/lib/sequence-diagram.min.js b/lib/sequence-diagram.min.js index 419f6ec..7d14cbb 100644 --- a/lib/sequence-diagram.min.js +++ b/lib/sequence-diagram.min.js @@ -1 +1 @@ -!function(){var e,t,n;!function(r){function s(e,t){return x.call(e,t)}function i(e,t){var n,r,s,i,a,o,l,h,d,g,c,u=t&&t.split("/"),p=b.map,f=p&&p["*"]||{};if(e){for(a=(e=e.split("/")).length-1,b.nodeIdCompat&&w.test(e[a])&&(e[a]=e[a].replace(w,"")),"."===e[0].charAt(0)&&u&&(e=u.slice(0,u.length-1).concat(e)),d=0;d0&&(e.splice(d-1,2),d-=2)}e=e.join("/")}if((u||f)&&p){for(d=(n=e.split("/")).length;d>0;d-=1){if(r=n.slice(0,d).join("/"),u)for(g=u.length;g>0;g-=1)if((s=p[u.slice(0,g).join("/")])&&(s=s[r])){i=s,o=d;break}if(i)break;!l&&f&&f[r]&&(l=f[r],h=d)}!i&&l&&(i=l,o=h),i&&(n.splice(0,o,i),e=n.join("/"))}return e}function a(e,t){return function(){var n=k.call(arguments,0);return"string"!=typeof n[0]&&1===n.length&&n.push(null),c.apply(r,n.concat([e,t]))}}function o(e){return function(t){f[e]=t}}function l(e){if(s(m,e)){var t=m[e];delete m[e],y[e]=!0,g.apply(r,t)}if(!s(f,e)&&!s(y,e))throw new Error("No "+e);return f[e]}function h(e){var t,n=e?e.indexOf("!"):-1;return n>-1&&(t=e.substring(0,n),e=e.substring(n+1,e.length)),[t,e]}function d(e){return e?h(e):[]}var g,c,u,p,f={},m={},b={},y={},x=Object.prototype.hasOwnProperty,k=[].slice,w=/\.js$/;u=function(e,t){var n,r=h(e),s=r[0],a=t[1];return e=r[1],s&&(n=l(s=i(s,a))),s?e=n&&n.normalize?n.normalize(e,function(e){return function(t){return i(t,e)}}(a)):i(e,a):(s=(r=h(e=i(e,a)))[0],e=r[1],s&&(n=l(s))),{f:s?s+"!"+e:e,n:e,pr:s,p:n}},p={require:function(e){return a(e)},exports:function(e){var t=f[e];return void 0!==t?t:f[e]={}},module:function(e){return{id:e,uri:"",exports:f[e],config:function(e){return function(){return b&&b.config&&b.config[e]||{}}}(e)}}},g=function(e,t,n,i){var h,g,c,b,x,k,w,v=[],A=typeof n;if(i=i||e,k=d(i),"undefined"===A||"function"===A){for(t=!t.length&&n.length?["require","exports","module"]:t,x=0;x{"use strict";return class{constructor(){this.listeners=new Map,this.forwards=new Set}addEventListener(e,t){const n=this.listeners.get(e);n?n.push(t):this.listeners.set(e,[t])}removeEventListener(e,t){const n=this.listeners.get(e);if(!n)return;const r=n.indexOf(t);-1!==r&&n.splice(r,1)}countEventListeners(e){return(this.listeners.get(e)||[]).length}removeAllEventListeners(e){e?this.listeners.delete(e):this.listeners.clear()}addEventForwarding(e){this.forwards.add(e)}removeEventForwarding(e){this.forwards.delete(e)}removeAllEventForwardings(){this.forwards.clear()}trigger(e,t=[]){(this.listeners.get(e)||[]).forEach(e=>e.apply(null,t)),this.forwards.forEach(n=>n.trigger(e,t))}}}),n("core/ArrayUtilities",[],()=>{"use strict";function e(e,t,n=null){if(null===n)return e.indexOf(t);for(let r=0;r=e.length)return void s.push(r.slice());const i=e[n];if(!Array.isArray(i))return r.push(i),t(e,n+1,r,s),void r.pop();for(let a=0;a{n.push(...t(e))}),n}}}),n("sequence/CodeMirrorMode",["core/ArrayUtilities"],e=>{"use strict";function t(e,t){return e.v===t.v&&e.prefix===t.prefix&&e.suffix===t.suffix&&e.q===t.q}function n(t,n,r){let s=r.suggest;return Array.isArray(s)||(s=[s]),e.flatMap(s,e=>!0===e?[function(e,t){return Object.keys(t.then).length>0?{v:e,suffix:" ",q:!1}:{v:e,suffix:"\n",q:!1}}(n,r)]:"object"==typeof e?e.known?t["known"+e.known]||[]:[e]:"string"==typeof e&&e?[{v:e,q:""===n}]:[])}function r(r,s){const i=[],a=e.last(s);return Object.keys(a.then).forEach(o=>{let l=a.then[o];"number"==typeof l&&(l=s[s.length-l-1]),e.mergeSets(i,n(r,o,l),t)}),i}function s(n,r,s,{suggest:i,override:a}){let o=null;"object"==typeof i&&i.known&&(o=i.known),r.type&&o!==r.type&&(a&&(r.type=a),e.mergeSets(n["known"+r.type],[{v:r.value,suffix:" ",q:!0}],t),r.type="",r.value=""),o&&(r.type=o,r.value&&(r.value+=s.s),r.value+=s.v)}function i(t,n,i){const a={type:"",value:""};let l=i;const h=[l];return t.line.forEach((n,i)=>{i===t.line.length-1&&(t.completions=r(t,h));const d=n.q?"":n.v;let g=l.then[d];void 0===g?(g=l.then[""],t.isVar=!0):t.isVar=n.q,"number"==typeof g?h.length-=g:h.push(g||o),l=e.last(h),s(t,a,n,l)}),n&&s(t,a,null,{}),t.nextCompletions=r(t,h),t.valid=Boolean(l.then["\n"])||0===Object.keys(l.then).length,l.type}function a(e){const t=e.baseToken||{};return{value:t.v||"",quoted:t.q||!1}}const o={type:"error line-error",then:{"":0}},l=(()=>{function e(e,t){return{type:"string",suggest:t,then:Object.assign({"":0},e)}}function t(e){return{type:"variable",suggest:{known:"Agent"},then:Object.assign({},e,{"":0,",":{type:"operator",suggest:!0,then:{"":1}}})}}function n(e){return{type:"keyword",suggest:[e+" of ",e+": "],then:{of:{type:"keyword",suggest:!0,then:{"":d}},":":{type:"operator",suggest:!0,then:{"":a}},"":d}}}function r(e,t){const n={type:"operator",suggest:!0,then:{"+":o,"-":o,"*":o,"!":o,"":e}};return{"+":{type:"operator",suggest:!0,then:{"+":o,"-":o,"*":n,"!":o,"":e}},"-":{type:"operator",suggest:!0,then:{"+":o,"-":o,"*":n,"!":{type:"operator",then:{"+":o,"-":o,"*":o,"!":o,"":e}},"":e}},"*":{type:"operator",suggest:!0,then:Object.assign({"+":n,"-":n,"*":o,"!":o,"":e},t)},"!":n,"":e}}const s={type:"",suggest:"\n",then:{}},i={type:"",then:{}},a=e({"\n":s}),l={type:"variable",suggest:{known:"Agent"},then:{"":0,"\n":s,",":{type:"operator",suggest:!0,then:{"":1}},as:{type:"keyword",suggest:!0,then:{"":{type:"variable",suggest:{known:"Agent"},then:{"":0,",":{type:"operator",suggest:!0,then:{"":3}},"\n":s}}}}}},h={type:"operator",suggest:!0,then:{"":a,"\n":i}},d=t({":":h}),g={type:"variable",suggest:{known:"Agent"},then:{"":0,",":{type:"operator",suggest:!0,then:{"":d}},":":o}},c={type:"variable",suggest:{known:"Agent"},then:{"":0,",":o,":":h}},u={type:"variable",suggest:{known:"Agent"},then:{"":0,":":{type:"operator",suggest:!0,then:{"":a,"\n":i}},"\n":s}},p={":":{type:"operator",suggest:!0,then:{"":e({as:{type:"keyword",suggest:!0,then:{"":{type:"variable",suggest:{known:"Agent"},then:{"":0,"\n":s}}}}})}}},f={type:"keyword",suggest:!0,then:Object.assign({over:{type:"keyword",suggest:!0,then:{"":t(p)}}},p)},m={"\n":s,":":{type:"operator",suggest:!0,then:{"":a,"\n":i}},with:{type:"keyword",suggest:["with height "],then:{height:{type:"keyword",suggest:!0,then:{"":{type:"number",suggest:["6 ","30 "],then:{"\n":s,":":{type:"operator",suggest:!0,then:{"":a,"\n":i}}}}}}}}},b={title:{type:"keyword",suggest:!0,then:{"":a}},theme:{type:"keyword",suggest:!0,then:{"":{type:"string",suggest:{global:"themes",suffix:"\n"},then:{"":0,"\n":s}}}},headers:{type:"keyword",suggest:!0,then:{none:{type:"keyword",suggest:!0,then:{}},cross:{type:"keyword",suggest:!0,then:{}},box:{type:"keyword",suggest:!0,then:{}},fade:{type:"keyword",suggest:!0,then:{}},bar:{type:"keyword",suggest:!0,then:{}}}},terminators:{type:"keyword",suggest:!0,then:{none:{type:"keyword",suggest:!0,then:{}},cross:{type:"keyword",suggest:!0,then:{}},box:{type:"keyword",suggest:!0,then:{}},fade:{type:"keyword",suggest:!0,then:{}},bar:{type:"keyword",suggest:!0,then:{}}}},divider:{type:"keyword",suggest:!0,then:Object.assign({line:{type:"keyword",suggest:!0,then:m},space:{type:"keyword",suggest:!0,then:m},delay:{type:"keyword",suggest:!0,then:m},tear:{type:"keyword",suggest:!0,then:m}},m)},define:{type:"keyword",suggest:!0,then:{"":l,as:o}},begin:{type:"keyword",suggest:!0,then:{"":l,reference:f,as:o}},end:{type:"keyword",suggest:!0,then:{"":l,as:o,"\n":s}},if:{type:"keyword",suggest:!0,then:{"":a,":":{type:"operator",suggest:!0,then:{"":a}},"\n":s}},else:{type:"keyword",suggest:["else\n","else if: "],then:{if:{type:"keyword",suggest:"if: ",then:{"":a,":":{type:"operator",suggest:!0,then:{"":a}}}},"\n":s}},repeat:{type:"keyword",suggest:!0,then:{"":a,":":{type:"operator",suggest:!0,then:{"":a}},"\n":s}},note:{type:"keyword",suggest:!0,then:{over:{type:"keyword",suggest:!0,then:{"":d}},left:n("left"),right:n("right"),between:{type:"keyword",suggest:!0,then:{"":g}}}},state:{type:"keyword",suggest:"state over ",then:{over:{type:"keyword",suggest:!0,then:{"":c}}}},text:{type:"keyword",suggest:!0,then:{left:n("left"),right:n("right")}},autolabel:{type:"keyword",suggest:!0,then:{off:{type:"keyword",suggest:!0,then:{}},"":e({"\n":s},[{v:"