Small design changes, introducing CSS colorization, doubleclick to edit markdown

This commit is contained in:
Magnus Åhall 2026-05-30 09:28:36 +02:00
parent 43212a4487
commit 5bd5ef1f02
9 changed files with 455 additions and 100 deletions

View file

@ -52,51 +52,51 @@ html {
#tree {
grid-area: tree;
display: grid;
background-color: #fafafa;
background-color: #ffffff;
color: #444;
z-index: 100;
border-right: 1px solid #ddd;
n2-tree {
/*border: 2px solid #f8f8f8;*/
padding: 16px 48px 16px 24px;
}
&:focus-within {
n2-tree {
/*
border: 2px solid #fe5f55;
*/
}
}
border-right: 2px solid #ddd;
#logo {
display: grid;
position: relative;
justify-items: center;
margin-top: 8px;
margin-bottom: 8px;
margin-left: 24px;
margin-right: 24px;
grid-template-columns: min-content 1fr min-content;
align-items: center;
justify-items: start;
cursor: pointer;
padding: 16px;
border-bottom: 1px solid #ccc;
img {
width: 128px;
left: -20px;
.el-search {
justify-self: end;
}
img:first-child {
height: 24px;
margin-right: 8px;
}
}
.icons {
display: flex;
justify-content: center;
margin-bottom: 32px;
margin: 16px 0px 32px 0px;
gap: 8px;
}
n2-tree {
.el-treenodes {
margin: 32px;
}
}
&:focus-within {
n2-tree {
}
}
.node {
display: grid;
grid-template-columns: 40px min-content;
@ -145,16 +145,6 @@ html {
}
}
#tree-nodes {
padding: 16px 32px;
/*
border-radius: 8px;
*/
/*
box-shadow: 5px 5px 10px -5px rgba(0, 0, 0, 0.75);
*/
}
#crumbs {
grid-area: crumbs;
display: grid;

View file

@ -8,7 +8,7 @@
version="1.1"
id="svg1"
sodipodi:docname="collapsed.svg"
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
@ -23,13 +23,13 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="4.8373092"
inkscape:cx="6.201795"
inkscape:cy="-12.40359"
inkscape:window-width="1916"
inkscape:window-height="1161"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:zoom="19.349237"
inkscape:cx="11.809251"
inkscape:cy="6.3051583"
inkscape:window-width="1093"
inkscape:window-height="1401"
inkscape:window-x="2560"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="false" />
@ -42,9 +42,13 @@
transform="translate(-102.39375,-146.31458)">
<title
id="title1">folder-outline</title>
<path
style="opacity:1;fill:#ffffff;stroke-width:0.264583"
d="m 102.7356,147.34014 h 5.83884 v 3.91079 h -5.86619 z"
id="path2" />
<path
d="m 108.34687,150.94479 h -5.29166 v -3.30729 h 5.29166 m 0,-0.66146 h -2.64584 l -0.66145,-0.66146 h -1.98437 c -0.36711,0 -0.66146,0.29435 -0.66146,0.66146 v 3.96875 a 0.66145729,0.66145729 0 0 0 0.66146,0.66146 h 5.29166 a 0.66145729,0.66145729 0 0 0 0.66146,-0.66146 v -3.30729 c 0,-0.36711 -0.29767,-0.66146 -0.66146,-0.66146 z"
id="path1"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#71c837;fill-opacity:1;stroke-width:0.330728;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" />
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#ffcc00;fill-opacity:1;stroke-width:0.330728;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

@ -8,7 +8,7 @@
version="1.1"
id="svg1"
sodipodi:docname="expanded.svg"
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
@ -23,13 +23,13 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="11.17754"
inkscape:cx="20.845374"
inkscape:cy="26.929003"
inkscape:window-width="1916"
inkscape:window-height="1161"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:zoom="15.807429"
inkscape:cx="10.533022"
inkscape:cy="16.384701"
inkscape:window-width="1093"
inkscape:window-height="1401"
inkscape:window-x="1463"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs1" /><g
@ -39,6 +39,9 @@
transform="translate(-101.33542,-147.10833)"><title
id="title1">folder-open</title><title
id="title1-1">folder-open-outline</title><path
style="opacity:1;fill:#ffffff;stroke-width:0.264583;fill-opacity:1"
d="m 101.61996,148.02892 5.99218,0.36823 v 1.12144 l 0.16738,0.33476 -0.703,2.32657 -5.22222,-0.11717 z"
id="path2" /><path
d="m 102.69141,149.0927 -0.69454,2.64584 v -3.30729 h 5.62239 a 0.6614573,0.6614573 0 0 0 -0.66146,-0.66146 h -2.3151 l -0.66146,-0.66146 h -1.98437 a 0.6614573,0.6614573 0 0 0 -0.66145,0.66146 v 3.96875 a 0.6614573,0.6614573 0 0 0 0.66145,0.66146 h 4.96093 c 0.29766,0 0.56224,-0.19844 0.62839,-0.4961 l 0.76067,-2.8112 h -5.65545 m 4.26639,2.64584 h -4.29947 l 0.52916,-1.98438 h 4.29948 z"
id="path1"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#71c837;fill-opacity:1;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" /></g></svg>
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#ffcc00;fill-opacity:1;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" /></g></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="23.350698"
height="23.999699"
viewBox="0 0 6.1782055 6.3499202"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="icon_settings.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="1"
inkscape:cx="-144.5"
inkscape:cy="-132"
inkscape:window-width="2190"
inkscape:window-height="1401"
inkscape:window-x="1463"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-146.12156,-110.33135)">
<title
id="title1">cog-outline</title>
<path
d="m 149.21069,112.23625 a 1.2700039,1.2700039 0 0 1 1.27001,1.27 1.2700039,1.2700039 0 0 1 -1.27001,1.27001 1.2700039,1.2700039 0 0 1 -1.27,-1.27001 1.2700039,1.2700039 0 0 1 1.27,-1.27 m 0,0.63501 a 0.63500199,0.63500199 0 0 0 -0.635,0.63499 0.63500199,0.63500199 0 0 0 0.635,0.63501 0.63500199,0.63500199 0 0 0 0.635,-0.63501 0.63500199,0.63500199 0 0 0 -0.635,-0.63499 m -0.635,3.81001 c -0.0794,0 -0.14605,-0.0571 -0.15875,-0.13336 l -0.11748,-0.84137 c -0.20002,-0.0793 -0.37147,-0.18733 -0.53658,-0.31433 l -0.79057,0.32068 c -0.0698,0.0254 -0.15557,0 -0.19367,-0.0698 l -0.63501,-1.09855 c -0.0413,-0.0699 -0.0222,-0.15557 0.038,-0.2032 l 0.66993,-0.52706 -0.0222,-0.30798 0.0222,-0.31749 -0.66993,-0.51753 c -0.0604,-0.0476 -0.0793,-0.13336 -0.038,-0.2032 l 0.63501,-1.09855 c 0.0382,-0.0698 0.12382,-0.0984 0.19367,-0.0698 l 0.79057,0.3175 c 0.16511,-0.12382 0.33656,-0.23177 0.53658,-0.31115 l 0.11748,-0.84137 c 0.0127,-0.0762 0.0793,-0.13336 0.15875,-0.13336 h 1.27 c 0.0793,0 0.14606,0.0571 0.15875,0.13336 l 0.11748,0.84137 c 0.20003,0.0794 0.37148,0.18733 0.53657,0.31115 l 0.79059,-0.3175 c 0.0698,-0.0286 0.15557,0 0.19367,0.0698 l 0.635,1.09855 c 0.0413,0.0698 0.0222,0.15557 -0.0382,0.2032 l -0.66992,0.51753 0.0222,0.31749 -0.0222,0.31751 0.66992,0.51753 c 0.0604,0.0476 0.0793,0.13334 0.0382,0.2032 l -0.635,1.09855 c -0.0382,0.0698 -0.12383,0.0984 -0.19367,0.0698 l -0.79059,-0.31751 c -0.16509,0.12383 -0.33654,0.23178 -0.53657,0.31116 l -0.11748,0.84137 c -0.0127,0.0762 -0.0793,0.13336 -0.15875,0.13336 h -1.27 m 0.39688,-5.71502 -0.11748,0.82868 c -0.381,0.0794 -0.71755,0.28257 -0.96203,0.56515 l -0.76517,-0.3302 -0.23813,0.41275 0.66993,0.49212 c -0.127,0.37149 -0.127,0.77471 0,1.143 l -0.67311,0.49531 0.23813,0.41275 0.77153,-0.33019 c 0.24448,0.27939 0.57785,0.48259 0.95567,0.55879 l 0.11748,0.83185 h 0.48261 l 0.11748,-0.82867 c 0.37783,-0.0793 0.7112,-0.28258 0.95568,-0.56197 l 0.77153,0.33019 0.23812,-0.41275 -0.6731,-0.49213 c 0.127,-0.37147 0.127,-0.77469 0,-1.14618 l 0.66993,-0.49212 -0.23813,-0.41275 -0.76518,0.3302 c -0.24447,-0.28258 -0.58102,-0.48577 -0.96202,-0.56197 l -0.11748,-0.83186 z"
id="path1"
style="stroke-width:0.317501;fill:#000000;fill-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 6 KiB

Before After
Before After

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="28.593159"
height="20"
viewBox="0 0 7.5652731 5.2916666"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="logo_small.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="16.477217"
inkscape:cx="48.460855"
inkscape:cy="5.2193281"
inkscape:window-width="2190"
inkscape:window-height="1401"
inkscape:window-x="1463"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-126.73541,-178.59375)">
<rect
style="fill:#a02c2c;stroke:none;stroke-width:0.113818"
id="rect5"
width="7.5652733"
height="5.2916665"
x="126.73541"
y="178.59375"
ry="1.0060203" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.82235px;font-family:'Forgotten Futurist';-inkscape-font-specification:'Forgotten Futurist, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;writing-mode:lr-tb;direction:ltr;fill:#ffffff;stroke:none;stroke-width:0.40186"
x="112.25369"
y="207.99469"
id="text5"
transform="scale(1.1392149,0.87779751)"><tspan
sodipodi:role="line"
id="tspan5"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.82235px;font-family:'Forgotten Futurist';-inkscape-font-specification:'Forgotten Futurist, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff;stroke:none;stroke-width:0.40186"
x="112.25369"
y="207.99469">N2</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,207 @@
export class Color {
constructor(r, g, b) { this.set(r, g, b); }
toString() { return `rgb(${Math.round(this.r)}, ${Math.round(this.g)}, ${Math.round(this.b)})`; }
set(r, g, b) {
this.r = this.clamp(r);
this.g = this.clamp(g);
this.b = this.clamp(b);
}
hueRotate(angle = 0) {
angle = angle / 180 * Math.PI;
let sin = Math.sin(angle);
let cos = Math.cos(angle);
this.multiply([
0.213 + cos * 0.787 - sin * 0.213, 0.715 - cos * 0.715 - sin * 0.715, 0.072 - cos * 0.072 + sin * 0.928,
0.213 - cos * 0.213 + sin * 0.143, 0.715 + cos * 0.285 + sin * 0.140, 0.072 - cos * 0.072 - sin * 0.283,
0.213 - cos * 0.213 - sin * 0.787, 0.715 - cos * 0.715 + sin * 0.715, 0.072 + cos * 0.928 + sin * 0.072
]);
}
grayscale(value = 1) {
this.multiply([
0.2126 + 0.7874 * (1 - value), 0.7152 - 0.7152 * (1 - value), 0.0722 - 0.0722 * (1 - value),
0.2126 - 0.2126 * (1 - value), 0.7152 + 0.2848 * (1 - value), 0.0722 - 0.0722 * (1 - value),
0.2126 - 0.2126 * (1 - value), 0.7152 - 0.7152 * (1 - value), 0.0722 + 0.9278 * (1 - value)
]);
}
sepia(value = 1) {
this.multiply([
0.393 + 0.607 * (1 - value), 0.769 - 0.769 * (1 - value), 0.189 - 0.189 * (1 - value),
0.349 - 0.349 * (1 - value), 0.686 + 0.314 * (1 - value), 0.168 - 0.168 * (1 - value),
0.272 - 0.272 * (1 - value), 0.534 - 0.534 * (1 - value), 0.131 + 0.869 * (1 - value)
]);
}
saturate(value = 1) {
this.multiply([
0.213 + 0.787 * value, 0.715 - 0.715 * value, 0.072 - 0.072 * value,
0.213 - 0.213 * value, 0.715 + 0.285 * value, 0.072 - 0.072 * value,
0.213 - 0.213 * value, 0.715 - 0.715 * value, 0.072 + 0.928 * value
]);
}
multiply(matrix) {
let newR = this.clamp(this.r * matrix[0] + this.g * matrix[1] + this.b * matrix[2]);
let newG = this.clamp(this.r * matrix[3] + this.g * matrix[4] + this.b * matrix[5]);
let newB = this.clamp(this.r * matrix[6] + this.g * matrix[7] + this.b * matrix[8]);
this.r = newR; this.g = newG; this.b = newB;
}
brightness(value = 1) { this.linear(value); }
contrast(value = 1) { this.linear(value, -(0.5 * value) + 0.5); }
linear(slope = 1, intercept = 0) {
this.r = this.clamp(this.r * slope + intercept * 255);
this.g = this.clamp(this.g * slope + intercept * 255);
this.b = this.clamp(this.b * slope + intercept * 255);
}
invert(value = 1) {
this.r = this.clamp((value + (this.r / 255) * (1 - 2 * value)) * 255);
this.g = this.clamp((value + (this.g / 255) * (1 - 2 * value)) * 255);
this.b = this.clamp((value + (this.b / 255) * (1 - 2 * value)) * 255);
}
hsl() { // Code taken from https://stackoverflow.com/a/9493060/2688027, licensed under CC BY-SA.
let r = this.r / 255;
let g = this.g / 255;
let b = this.b / 255;
let max = Math.max(r, g, b);
let min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
let d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
} h /= 6;
}
return {
h: h * 100,
s: s * 100,
l: l * 100
};
}
clamp(value) {
if (value > 255) { value = 255; }
else if (value < 0) { value = 0; }
return value;
}
}
export class Solver {
constructor(target) {
this.target = target;
this.targetHSL = target.hsl();
this.reusedColor = new Color(0, 0, 0); // Object pool
}
solve() {
let result = this.solveNarrow(this.solveWide());
return {
values: result.values,
loss: result.loss,
filter: this.css(result.values)
};
}
solveWide() {
const A = 5;
const c = 15;
const a = [60, 180, 18000, 600, 1.2, 1.2];
let best = { loss: Infinity };
for (let i = 0; best.loss > 25 && i < 3; i++) {
let initial = [50, 20, 3750, 50, 100, 100];
let result = this.spsa(A, a, c, initial, 1000);
if (result.loss < best.loss) { best = result; }
} return best;
}
solveNarrow(wide) {
const A = wide.loss;
const c = 2;
const A1 = A + 1;
const a = [0.25 * A1, 0.25 * A1, A1, 0.25 * A1, 0.2 * A1, 0.2 * A1];
return this.spsa(A, a, c, wide.values, 500);
}
spsa(A, a, c, values, iters) {
const alpha = 1;
const gamma = 0.16666666666666666;
let best = null;
let bestLoss = Infinity;
let deltas = new Array(6);
let highArgs = new Array(6);
let lowArgs = new Array(6);
for (let k = 0; k < iters; k++) {
let ck = c / Math.pow(k + 1, gamma);
for (let i = 0; i < 6; i++) {
deltas[i] = Math.random() > 0.5 ? 1 : -1;
highArgs[i] = values[i] + ck * deltas[i];
lowArgs[i] = values[i] - ck * deltas[i];
}
let lossDiff = this.loss(highArgs) - this.loss(lowArgs);
for (let i = 0; i < 6; i++) {
let g = lossDiff / (2 * ck) * deltas[i];
let ak = a[i] / Math.pow(A + k + 1, alpha);
values[i] = fix(values[i] - ak * g, i);
}
let loss = this.loss(values);
if (loss < bestLoss) { best = values.slice(0); bestLoss = loss; }
} return { values: best, loss: bestLoss };
function fix(value, idx) {
let max = 100;
if (idx === 2 /* saturate */) { max = 7500; }
else if (idx === 4 /* brightness */ || idx === 5 /* contrast */) { max = 200; }
if (idx === 3 /* hue-rotate */) {
if (value > max) { value = value % max; }
else if (value < 0) { value = max + value % max; }
} else if (value < 0) { value = 0; }
else if (value > max) { value = max; }
return value;
}
}
loss(filters) { // Argument is array of percentages.
let color = this.reusedColor;
color.set(0, 0, 0);
color.invert(filters[0] / 100);
color.sepia(filters[1] / 100);
color.saturate(filters[2] / 100);
color.hueRotate(filters[3] * 3.6);
color.brightness(filters[4] / 100);
color.contrast(filters[5] / 100);
let colorHSL = color.hsl();
return Math.abs(color.r - this.target.r)
+ Math.abs(color.g - this.target.g)
+ Math.abs(color.b - this.target.b)
+ Math.abs(colorHSL.h - this.targetHSL.h)
+ Math.abs(colorHSL.s - this.targetHSL.s)
+ Math.abs(colorHSL.l - this.targetHSL.l);
}
css(filters) {
function fmt(idx, multiplier = 1) { return Math.round(filters[idx] * multiplier); }
return `invert(${fmt(0)}%) sepia(${fmt(1)}%) saturate(${fmt(2)}%) hue-rotate(${fmt(3, 3.6)}deg) brightness(${fmt(4)}%) contrast(${fmt(5)}%)`;
}
}

View file

@ -110,12 +110,12 @@ export class MarkedPosition {
renderer: {
heading(token) {
const content = this.parser.parseInline(token.tokens)
return `<h${token.depth} onclick="setpos(event)" onclick="setpos(this)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${content}</h${token.depth}>\n`
return `<h${token.depth} ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${content}</h${token.depth}>\n`
},
paragraph(token) {
const content = this.parser.parseInline(token.tokens)
return `<p onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${content}</p>\n`
return `<p ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${content}</p>\n`
},
list(token) {
@ -134,7 +134,7 @@ export class MarkedPosition {
},
listitem(token) {
return `<li onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${this.parser.parse(token.tokens)}</li>\n`
return `<li ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${this.parser.parse(token.tokens)}</li>\n`
},
code(token) {
@ -143,12 +143,12 @@ export class MarkedPosition {
const code = token.text.replace(other.endingNewline, '') + '\n'
if (!langString) {
return `<pre onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}"><code>`
return `<pre ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}"><code>`
+ (token.escaped ? code : escapeHtmlEntities(code, true))
+ '</code></pre>\n'
}
return `<pre onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}"><code class="language-`
return `<pre ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}"><code class="language-`
+ escapeHtmlEntities(langString)
+ '">'
+ (token.escaped ? code : escapeHtmlEntities(code, true))
@ -157,7 +157,7 @@ export class MarkedPosition {
blockquote(token) {
const body = this.parser.parse(token.tokens)
return `<blockquote onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">\n${body}</blockquote>\n`
return `<blockquote ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">\n${body}</blockquote>\n`
},
html(token) {
@ -169,11 +169,11 @@ export class MarkedPosition {
},
hr(token) {
return `<hr onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">\n`
return `<hr ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">\n`
},
checkbox(token) {
return `<input onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}"`
return `<input ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}"`
+ (token.checked ? 'checked="" ' : '')
+ 'disabled="" type="checkbox"> '
},
@ -218,7 +218,7 @@ export class MarkedPosition {
if (token.tokens.length > 0) {
const start = token.tokens[0].position.start.offset
const end = token.tokens[0].position.end.offset
ofs = `onclick="setpos(event)" data-offset-start="${start}" data-offset-end="${end}"`
ofs = `ondblclick="setpos(event)" data-offset-start="${start}" data-offset-end="${end}"`
}
const content = this.parser.parseInline(token.tokens);
@ -230,23 +230,23 @@ export class MarkedPosition {
},
strong(token) {
return `<strong onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${this.parser.parseInline(token.tokens)}</strong>`
return `<strong ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${this.parser.parseInline(token.tokens)}</strong>`
},
em(token) {
return `<em onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${this.parser.parseInline(token.tokens)}</em>`
return `<em ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${this.parser.parseInline(token.tokens)}</em>`
},
codespan(token) {
return `<code onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${escapeHtmlEntities(token.text, true)}</code>`
return `<code ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${escapeHtmlEntities(token.text, true)}</code>`
},
br(token) {
return `<br onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">`
return `<br ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">`
},
del(token) {
return `<del onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${this.parser.parseInline(token.tokens)}</del>`
return `<del ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}">${this.parser.parseInline(token.tokens)}</del>`
},
link(token) {
@ -256,7 +256,7 @@ export class MarkedPosition {
return text
}
token.href = cleanHref
let out = '<a onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}" href="' + token.href + '"'
let out = '<a ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}" href="' + token.href + '"'
if (token.title) {
out += ' title="' + (escapeHtmlEntities(token.title)) + '"'
}
@ -275,7 +275,7 @@ export class MarkedPosition {
}
token.href = cleanHref
let out = `<img onclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}" src="${token.href}" alt="${escapeHtmlEntities(token.text)}"`
let out = `<img ondblclick="setpos(event)" data-offset-start="${token.position.start.offset}" data-offset-end="${token.position.end.offset}" src="${token.href}" alt="${escapeHtmlEntities(token.text)}"`
if (token.title) {
out += ` title="${escapeHtmlEntities(token.title)}"`
}

View file

@ -1,14 +1,19 @@
import { ROOT_NODE } from 'node_store'
import { CustomHTMLElement } from './lib/custom_html_element.mjs'
import { Color, Solver } from './lib/css_colorize.mjs'
export class N2Tree extends CustomHTMLElement {
static {// {{{
this.tmpl = document.createElement('template')
this.tmpl.innerHTML = `
<div data-el="logo" id="logo"><img src="/images/${_VERSION}/logo.svg" /></div>
<div class="icons">
<div data-el="logo" id="logo">
<img src="/images/${_VERSION}/logo_small.svg" />
<img src="/images/${_VERSION}/logo.svg" />
<img data-el="search" class='search' src="/images/${_VERSION}/icon_search.svg" style="height: 22px" />
<img data-el="sync" class='sync' src="/images/${_VERSION}/icon_refresh.svg" />
</div>
<div class="icons">
<img data-el="sync" class='sync' src="/images/${_VERSION}/icon_refresh.svg" />
<img data-el="settings" class='settings' src="/images/${_VERSION}/icon_settings.svg" />
</div>
<div data-el="treenodes"></div>
`
@ -31,7 +36,7 @@ export class N2Tree extends CustomHTMLElement {
this.elSync.addEventListener('click', () => _sync.run())
this.elLogo.addEventListener('click', () => _app.goToNode(ROOT_NODE, false, false))
_mbus.subscribe('NODE_MODIFIED', ({ detail })=>{
_mbus.subscribe('NODE_MODIFIED', ({ detail }) => {
const node = detail.data.node
const treenode = this.treeNodeComponents[node.get('UUID')]
@ -43,6 +48,12 @@ export class N2Tree extends CustomHTMLElement {
})
this.populateFirstLevel()
/* XXX - set color */
let color = new Color(255, 96, 80)
let solver = new Solver(color)
let result = solver.solve()
this.elSettings.style.filter = result.filter
}// }}}
render() {// {{{
if (this.rendered)