rapier-markdown-kit 1.1.80 → 1.1.81
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.
- package/dist/kit/render.mjs +10 -12
- package/package.json +1 -1
- package/style.css +56 -63
package/dist/kit/render.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// The document's renderer. DOM, codecs and host state are explicit inputs.
|
|
3
3
|
function createRenderer(runtime) {
|
|
4
|
-
const {RAPIER_COLOR_CLOSE, RapierLedgerCarried, RapierPageReturnAddress, RapierTextCodec, _rapierArtifactHighlight, _rapierArtifactLexerScript, _rapierArtifactMarkLexed, _rapierArtifactPreference, _rapierArtifactStyles, _rapierBlobDataUrl, _rapierBuildInterchangeContext, _rapierDocumentNameIsAdmissible, _rapierLedgerParts, _rapierDrawReadSVGRecipe, _rapierDrawShapeProfileFor, _rapierFillDiagram, _rapierFormatColorOpen, _rapierLanguageClass, _rapierLineStartOffsets, _rapierMarkdownEnvironment, _rapierPortableHtml, _rapierPrepareInterchangeContext, _rapierProjectPortableRoot, _rapierSourceCharEscaped, _rapierSourceLineSpan, crypto, document, escapeRapierHtmlText, globalThis, rapierConfirm, sanitizeRapierHtml} = runtime;
|
|
4
|
+
const {RAPIER_COLOR_CLOSE, RapierLedgerCarried, RapierPageReturnAddress, RapierTextCodec, _rapierArtifactHighlight, _rapierArtifactLexerScript, _rapierArtifactMarkLexed, _rapierArtifactPreference, _rapierArtifactStyles, _rapierBlobDataUrl, _rapierBuildInterchangeContext, _rapierDocumentNameIsAdmissible, _rapierLedgerParts, _rapierDrawReadSVGRecipe, _rapierDrawShapeProfileFor, _rapierFillDiagram, _rapierFormatColorOpen, _rapierLanguageClass, _rapierLineStartOffsets, _rapierMarkdownEnvironment, _rapierPortableHtml, _rapierPrepareInterchangeContext, _rapierProjectPortableRoot, _rapierProviders, _rapierSourceCharEscaped, _rapierSourceLineSpan, crypto, document, escapeRapierHtmlText, globalThis, rapierConfirm, sanitizeRapierHtml} = runtime;
|
|
5
5
|
let md = runtime.md;
|
|
6
6
|
async function render(source, options = {}) {
|
|
7
7
|
if (typeof source !== 'string') throw new TypeError('Document source must be a string');
|
|
@@ -68,20 +68,18 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
68
68
|
// .md-render set and the highlight rules, so a numbered list, a checkbox, a callout and a
|
|
69
69
|
// table are drawn in an exported page exactly as they are drawn here. The one caller with
|
|
70
70
|
// rules of its own -- Share, for the no-script float it alone writes -- appends them.
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// The print page carries its faces as data: faces of its own, and a Will's carriers' font among them (the secure-runtime host prints this
|
|
78
|
-
// page, not the one it was made in). Its policy alone has font-src data:, which reaches no network, whether or not the page has a Will:
|
|
79
|
-
// with it only for a Will the same document would print in its faces with the Will and in the reader's own without (measured).
|
|
71
|
+
// Diagram geometry was measured with these exact fonts. Carry the same data fonts into
|
|
72
|
+
// offline pages; a fallback face would change labels inside fixed renderer geometry.
|
|
73
|
+
const diagramFonts = !!styledRoot.querySelector('svg.rapier-diagram,svg.rapier-native-flowchart');
|
|
74
|
+
const includeFonts = !!opts.print || diagramFonts;
|
|
75
|
+
const zenFontCss = styledRoot.querySelector('svg[aria-roledescription="zenuml"]')
|
|
76
|
+
? (_rapierProviders?.mermaid?.exportFontCss?.() || '') : '';
|
|
80
77
|
const willFont = opts.print && opts.willFont ? opts.willFont : null;
|
|
81
78
|
const settings = _rapierDocumentSettingsOf(context.canonical);
|
|
82
79
|
const settingsCss = _rapierDocumentSettingsCss(settings);
|
|
83
80
|
const pageTitle = settings && settings.title ? globalThis.RapierMarkdownSpec.documentTitle(settings) : metadata.filename;
|
|
84
|
-
const css = _rapierArtifactStyles(theme,
|
|
81
|
+
const css = _rapierArtifactStyles(theme, includeFonts, !!opts.print || opts.kind === 'standalone', opts.kind === 'page')
|
|
82
|
+
+ (zenFontCss ? '\n\n' + zenFontCss : '')
|
|
85
83
|
+ (opts.extraCss ? '\n\n' + opts.extraCss : '')
|
|
86
84
|
+ (willFont ? '\n\n@font-face{font-family:' + willFont.family + ';src:url(' + await _rapierBlobDataUrl(new Blob([willFont.bytes], { type: 'font/ttf' })) + ') format("truetype")}' : '')
|
|
87
85
|
+ (settingsCss ? '\n' + settingsCss : '');
|
|
@@ -106,7 +104,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
106
104
|
// someone's page shows whole, with no inner scroll. Any origin may read the height: the page is the
|
|
107
105
|
// document, published by the person who exported it.
|
|
108
106
|
+ '<meta name="responsive-embedded-sizing" content="allow-origins=*">\n'
|
|
109
|
-
+ '<meta http-equiv="Content-Security-Policy" content="' + _rapierExportedPageCsp(layoutScript || lexerScript || inkScript ? nonce : '',
|
|
107
|
+
+ '<meta http-equiv="Content-Security-Policy" content="' + _rapierExportedPageCsp(layoutScript || lexerScript || inkScript ? nonce : '', includeFonts) + '">\n'
|
|
110
108
|
+ '<meta name="referrer" content="no-referrer">\n'
|
|
111
109
|
+ '<meta name="generator" content="Rapier ' + escapeRapierHtmlText(version) + '">\n'
|
|
112
110
|
+ '<title>' + escapeRapierHtmlText(pageTitle) + '</title>\n'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rapier-markdown-kit",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.81",
|
|
4
4
|
"description": "Read, render, write and style Self-contained Markdown, and carry its optional edit ledger: the shared HTML writer, semantic renderer, stylesheet, layout, marks, pictures, Will/1, replay, authorship and offline merge. MIT, no npm dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
package/style.css
CHANGED
|
@@ -87,6 +87,36 @@
|
|
|
87
87
|
--md-diagram-c7:light-dark(#5eac56,#002104);
|
|
88
88
|
--md-diagram-c8:light-dark(#d485fc,#330033);
|
|
89
89
|
--md-diagram-c9:light-dark(#c9b49b,#4f3626);
|
|
90
|
+
|
|
91
|
+
--md-mermaid-surface:light-dark(#f2f1ee,#171b1e);
|
|
92
|
+
--md-mermaid-border:light-dark(#757b80,#929ba1);
|
|
93
|
+
--md-mermaid-grid:light-dark(#d0d4d6,#424a4f);
|
|
94
|
+
--md-mermaid-muted:light-dark(#525a60,#b7c0c5);
|
|
95
|
+
--md-mermaid-on-series:light-dark(#fff8ea,#11181c);
|
|
96
|
+
--md-mermaid-series-1:light-dark(#176e85,#6bc2d5);
|
|
97
|
+
--md-mermaid-series-2:light-dark(#b55b2a,#f1a172);
|
|
98
|
+
--md-mermaid-series-3:light-dark(#7652a0,#bda0e1);
|
|
99
|
+
--md-mermaid-series-4:light-dark(#34775c,#7fc4a4);
|
|
100
|
+
--md-mermaid-series-5:light-dark(#a34f68,#e79eb4);
|
|
101
|
+
--md-mermaid-series-6:light-dark(#896e23,#d8be68);
|
|
102
|
+
--md-mermaid-series-7:light-dark(#426caa,#92b7ed);
|
|
103
|
+
--md-mermaid-series-8:light-dark(#876144,#cba886);
|
|
104
|
+
--md-mermaid-series-9:light-dark(#55703a,#afc88e);
|
|
105
|
+
--md-mermaid-series-10:light-dark(#94633c,#e4b185);
|
|
106
|
+
--md-mermaid-series-11:light-dark(#8a5186,#d5a0cf);
|
|
107
|
+
--md-mermaid-series-12:light-dark(#476d70,#91b9ba);
|
|
108
|
+
--md-mermaid-tint-1:light-dark(#e0edf1,#1b3941);
|
|
109
|
+
--md-mermaid-tint-2:light-dark(#f5e6dc,#472e21);
|
|
110
|
+
--md-mermaid-tint-3:light-dark(#ebe4f2,#342941);
|
|
111
|
+
--md-mermaid-tint-4:light-dark(#e2eee7,#223b2e);
|
|
112
|
+
--md-mermaid-tint-5:light-dark(#f3e1e8,#432a33);
|
|
113
|
+
--md-mermaid-tint-6:light-dark(#f3edd9,#40391f);
|
|
114
|
+
--md-mermaid-tint-7:light-dark(#e2eaf6,#26354a);
|
|
115
|
+
--md-mermaid-tint-8:light-dark(#eee5dd,#3b3129);
|
|
116
|
+
--md-mermaid-tint-9:light-dark(#e8eddf,#303921);
|
|
117
|
+
--md-mermaid-tint-10:light-dark(#f1e5d9,#433122);
|
|
118
|
+
--md-mermaid-tint-11:light-dark(#f0e2ed,#3e2a3b);
|
|
119
|
+
--md-mermaid-tint-12:light-dark(#e2ecec,#263a3b);
|
|
90
120
|
}
|
|
91
121
|
.md-render{color-scheme:light}
|
|
92
122
|
/* Geist and Geist Mono both have a 530/1000 x-height. Preserve their size and match a
|
|
@@ -135,6 +165,10 @@
|
|
|
135
165
|
margin: 0 0 var(--md-line);
|
|
136
166
|
max-width: 100%;
|
|
137
167
|
overflow: auto;
|
|
168
|
+
padding-block: 0.55rem 0.85rem;
|
|
169
|
+
background:
|
|
170
|
+
linear-gradient(var(--md-diagram-node-line), var(--md-diagram-node-line)) top / 100% 1px no-repeat,
|
|
171
|
+
linear-gradient(var(--md-diagram-node-line), var(--md-diagram-node-line)) bottom / 100% 1px no-repeat;
|
|
138
172
|
}
|
|
139
173
|
:where(.md-render) .diagram-block .diagram-source {
|
|
140
174
|
font-family: var(--md-font-mono);
|
|
@@ -145,7 +179,12 @@
|
|
|
145
179
|
margin: 0;
|
|
146
180
|
white-space: pre-wrap;
|
|
147
181
|
}
|
|
148
|
-
|
|
182
|
+
/* A drawing in progress keeps its source for the reader and for export, but does not flash
|
|
183
|
+
the fence across the page. The measured height, once known, holds the place. */
|
|
184
|
+
:where(.md-render) .diagram-block:is([data-diagram-state="idle"],[data-diagram-state="pending"]) {
|
|
185
|
+
min-height: 5.25rem;
|
|
186
|
+
}
|
|
187
|
+
:where(.md-render) .diagram-block:is([data-diagram-state="ready"],[data-diagram-state="idle"],[data-diagram-state="pending"]) .diagram-source {
|
|
149
188
|
position: absolute;
|
|
150
189
|
width: 1px;
|
|
151
190
|
height: 1px;
|
|
@@ -159,70 +198,21 @@
|
|
|
159
198
|
:where(.md-render) .diagram-block .diagram-cache {
|
|
160
199
|
max-width: 100%;
|
|
161
200
|
}
|
|
162
|
-
/* Mermaid owns its geometry and typography.
|
|
163
|
-
|
|
201
|
+
/* Mermaid owns its geometry and typography. Its reference palette is projected onto SVG paint
|
|
202
|
+
at admission; only renderer paint omissions are corrected here. */
|
|
164
203
|
:where(.md-render) svg.rapier-native-flowchart{display:block;margin-inline:auto;height:auto}
|
|
165
|
-
:where(.md-render) svg.rapier-diagram{display:block;margin-inline:auto;height:auto}
|
|
166
|
-
|
|
167
|
-
/* Recolour connectors without changing widths, dashes, marker contours or hollow interiors. */
|
|
168
|
-
:where(.md-render) svg.rapier-diagram:is([aria-roledescription^="flowchart"],[aria-roledescription="classDiagram"],[aria-roledescription="stateDiagram"],[aria-roledescription="requirement"],[aria-roledescription="er"]) :is(path.flowchart-link,path.relation,path.relationshipLine,path.transition){stroke:var(--md-diagram-line)!important}
|
|
169
|
-
:where(.md-render) svg.rapier-diagram:is([aria-roledescription^="flowchart"],[aria-roledescription="classDiagram"],[aria-roledescription="stateDiagram"],[aria-roledescription="requirement"],[aria-roledescription="er"],[aria-roledescription="sequence"],[aria-roledescription="c4"],[aria-roledescription="journey"]) marker{fill:var(--md-diagram-arrow)!important}
|
|
170
|
-
|
|
171
|
-
/* Sequence surfaces and their labels move together between paper colours. */
|
|
172
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(rect.actor,rect.activation0,rect.activation1,rect.activation2,polygon.labelBox){fill:var(--md-diagram-node-fill)!important;stroke:var(--md-diagram-node-line)!important}
|
|
173
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(text.actor,text.labelText,text.loopText,text.messageText,text.sectionTitle),
|
|
174
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(text.actor,text.labelText,text.loopText,text.messageText,text.sectionTitle) tspan{fill:var(--md-color-text)!important}
|
|
175
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(text.sequenceNumber,text.sequenceNumber tspan){fill:var(--md-color-bg)!important}
|
|
176
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] rect.note{fill:var(--md-color-inset)!important;stroke:var(--md-diagram-node-line)!important}
|
|
177
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(text.noteText,text.noteText tspan){fill:var(--md-color-text)!important}
|
|
178
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(line.messageLine0,line.messageLine1,path.messageLine0,path.messageLine1){stroke:var(--md-diagram-line)!important}
|
|
179
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(line.actor-line,line.loopLine){stroke:var(--md-diagram-inner-line)!important}
|
|
180
|
-
|
|
181
|
-
/* A state begins with a dot, ends with a ring, and forks through a bar. */
|
|
182
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] :is(circle.state-start,rect.fork-join,circle.end-state-inner){fill:var(--md-color-text)!important;stroke:var(--md-color-text)!important}
|
|
183
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] circle.state-end{fill:var(--md-color-bg)!important;stroke:var(--md-color-text)!important}
|
|
184
|
-
/* Compound-state frames follow the paper; their child states keep their own surfaces and ink. */
|
|
185
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] g.statediagram-cluster>rect.inner{fill:var(--md-color-bg)!important}
|
|
186
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] g.statediagram-cluster>g>rect.outer{fill:var(--md-diagram-node-fill)!important}
|
|
187
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] g.statediagram-cluster>.cluster-label :is(text,tspan){fill:var(--md-color-text)!important}
|
|
188
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] g.statediagram-cluster>.cluster-label :is(foreignObject,.nodeLabel){color:var(--md-color-text)!important}
|
|
204
|
+
:where(.md-render) svg.rapier-diagram{display:block;margin-inline:auto;height:auto;text-rendering:optimizeLegibility}
|
|
189
205
|
|
|
190
|
-
/*
|
|
191
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="
|
|
192
|
-
|
|
193
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="
|
|
197
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="
|
|
198
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="
|
|
199
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="
|
|
200
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="journey"] text.legend{fill:var(--md-color-text)!important}
|
|
201
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="journey"]>line{stroke:var(--md-diagram-line)!important}
|
|
202
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="journey"] line.task-line{stroke:var(--md-diagram-inner-line)!important}
|
|
203
|
-
|
|
204
|
-
/* Gantt paint selectors name their element type: journey also uses the class "task" on text. */
|
|
205
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.section0,.section2){fill:var(--md-diagram-node-fill)!important}
|
|
206
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.section1,.section3){fill:var(--md-color-bg)!important}
|
|
207
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect.task{fill:var(--md-diagram-c1)!important;stroke:var(--md-diagram-c1)!important}
|
|
208
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.active0,.active1,.active2,.active3){fill:var(--md-diagram-c3)!important;stroke:var(--md-diagram-c3)!important}
|
|
209
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.done0,.done1,.done2,.done3){fill:var(--md-diagram-node-line)!important;stroke:var(--md-diagram-node-line)!important}
|
|
210
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.crit0,.crit1,.crit2,.crit3,.activeCrit0,.activeCrit1,.activeCrit2,.activeCrit3,.doneCrit0,.doneCrit1,.doneCrit2,.doneCrit3){fill:var(--md-diagram-c6)!important;stroke:var(--md-diagram-c6)!important}
|
|
211
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] text.taskText{fill:var(--md-diagram-on-colour)!important}
|
|
212
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] text:is(.taskTextOutsideRight,.taskTextOutsideLeft,.sectionTitle,.titleText){fill:var(--md-color-text)!important}
|
|
213
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] .grid .tick line{stroke:var(--md-diagram-inner-line)!important}
|
|
214
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] .grid .tick text{fill:var(--md-diagram-quiet)!important}
|
|
215
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] line.today{stroke:var(--md-diagram-c6)!important}
|
|
216
|
-
|
|
217
|
-
/* Outside labels never acquire the ink intended for a coloured slice or branch. */
|
|
218
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="pie"] text.pieTitleText,
|
|
219
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="pie"] g.legend text,
|
|
220
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="timeline"]>text{fill:var(--md-color-text)!important}
|
|
221
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="timeline"] g.lineWrapper>line{stroke:var(--md-diagram-line)!important}
|
|
222
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gitGraph"] rect.commit-label-bkg{fill:var(--md-color-bg)!important}
|
|
223
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gitGraph"] text.commit-label{fill:var(--md-diagram-quiet)!important}
|
|
224
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gitGraph"] polygon.tag-label-bkg{fill:var(--md-diagram-node-fill)!important;stroke:var(--md-diagram-node-line)!important}
|
|
225
|
-
:where(.md-render) svg.rapier-diagram[aria-roledescription="gitGraph"] text.tag-label{fill:var(--md-color-text)!important}
|
|
206
|
+
/* Radar gives these SVG labels a color declaration; SVG text needs a fill. */
|
|
207
|
+
:where(.md-render) svg.rapier-diagram[aria-roledescription="radar"] :is(.radarTitle,.radarAxisLabel){fill:currentColor}
|
|
208
|
+
/* Multiplying a ribbon by black paper makes its entire flow disappear. */
|
|
209
|
+
:where(.md-render) svg.rapier-diagram[aria-roledescription="sankey"] .link{mix-blend-mode:normal!important}
|
|
210
|
+
/* Captions keep their baseline while masking chart marks behind their letters. */
|
|
211
|
+
:where(.md-render) svg.rapier-diagram[aria-roledescription="c4"] text:not(.c4-shape *),
|
|
212
|
+
:where(.md-render) svg.rapier-diagram[aria-roledescription="architecture"] .architecture-service text,
|
|
213
|
+
:where(.md-render) svg.rapier-diagram[aria-roledescription="wardley"] .wardley-node-label,
|
|
214
|
+
:where(.md-render) svg.rapier-diagram[aria-roledescription="xychart"] [class^="line-plot-"]>.labels>text,
|
|
215
|
+
:where(.md-render) svg.rapier-diagram[aria-roledescription="cynefin"] :is(text.cynefinSubtitle,text.cynefinArrowLabel,text.cynefinDomainLabel){paint-order:stroke fill;stroke:var(--md-color-bg)!important;stroke-width:2.5px;stroke-linejoin:round}
|
|
226
216
|
|
|
227
217
|
/* Generator styles are removed at admission; these owned keyframes animate only dash offset. */
|
|
228
218
|
@keyframes rapier-mermaid-dash{from{stroke-dashoffset:900}to{stroke-dashoffset:0}}
|
|
@@ -243,6 +233,9 @@
|
|
|
243
233
|
font-size: 0.88em;
|
|
244
234
|
opacity: .78;
|
|
245
235
|
}
|
|
236
|
+
:where(.md-render) .diagram-block--error .diagram-source {
|
|
237
|
+
max-height: 9rem;
|
|
238
|
+
}
|
|
246
239
|
|
|
247
240
|
/* The line model (the way a Word document reads). One line, --md-line, is the body's line box: 1.75rem, 1.6 lines
|
|
248
241
|
of the 1.1rem body, a whole 28 px at the browser's 16 px root so baselines land on pixels. Every block ends one
|