Added File shard, improved shard icons.
|
@ -1,107 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 4.2333333 4.2333333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="login_credentials_shard_node_icon.svg"
|
||||
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\nl\andrewlalis\crystalkeep\ui\images\login_credentials_shard_node_icon.png"
|
||||
inkscape:export-xdpi="192"
|
||||
inkscape:export-ydpi="192">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="89.600001"
|
||||
inkscape:cx="10.229603"
|
||||
inkscape:cy="7.4126105"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
units="px" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-292.76667)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#72deff;fill-opacity:1;stroke:none;stroke-width:0.07803907;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="path817"
|
||||
sodipodi:sides="9"
|
||||
sodipodi:cx="2.1166666"
|
||||
sodipodi:cy="294.93815"
|
||||
sodipodi:r1="1.8178079"
|
||||
sodipodi:r2="1.2179312"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="0.87266463"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 3.6909344,295.84704 -0.7913968,0.0241 -0.1611441,0.77519 -0.6217269,-0.49025 -0.621727,0.49025 -0.1611441,-0.77519 -0.79139678,-0.0241 0.37483979,-0.69741 -0.59076328,-0.52715 0.73543197,-0.2933 -0.11370504,-0.78356 0.75190744,0.24804 0.416557,-0.67333 0.4165569,0.67333 0.7519075,-0.24804 -0.1137051,0.78356 0.735432,0.2933 -0.5907633,0.52715 z"
|
||||
inkscape:transform-center-x="0.06028767"
|
||||
inkscape:transform-center-y="0.030212626" />
|
||||
<g
|
||||
id="g828">
|
||||
<rect
|
||||
y="294.87439"
|
||||
x="1.5420023"
|
||||
height="0.95215333"
|
||||
width="1.1493286"
|
||||
id="rect816"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.05291667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path818"
|
||||
d="m 1.8074821,294.89943 v -0.43431 c 0,0 -0.016704,-0.32365 0.3236467,-0.32365 0.340351,0 0.292326,0.29233 0.292326,0.29233 v 0.47816"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<rect
|
||||
y="295.05811"
|
||||
x="1.7157624"
|
||||
height="0.17957026"
|
||||
width="0.8018086"
|
||||
id="rect820"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.05291667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.05291667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="rect822"
|
||||
width="0.8018086"
|
||||
height="0.17957026"
|
||||
x="1.7157624"
|
||||
y="295.41309" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 4.4 KiB |
|
@ -16,9 +16,9 @@
|
|||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="shard_node_icon.svg"
|
||||
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\ui\images\shard_node_icon.png"
|
||||
inkscape:export-xdpi="192"
|
||||
inkscape:export-ydpi="192">
|
||||
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\nl\andrewlalis\crystalkeep\ui\images\shard_node_icon.png"
|
||||
inkscape:export-xdpi="1536"
|
||||
inkscape:export-ydpi="1536">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
|
@ -28,9 +28,9 @@
|
|||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="31.678384"
|
||||
inkscape:cx="4.3963569"
|
||||
inkscape:cy="7.5940447"
|
||||
inkscape:zoom="44.8"
|
||||
inkscape:cx="8.5805581"
|
||||
inkscape:cy="7.3490264"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
|
@ -39,7 +39,9 @@
|
|||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
units="px" />
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
|
@ -58,21 +60,9 @@
|
|||
id="layer1"
|
||||
transform="translate(0,-292.76667)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#72deff;fill-opacity:1;stroke:none;stroke-width:0.07803907;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="path817"
|
||||
sodipodi:sides="9"
|
||||
sodipodi:cx="2.1166666"
|
||||
sodipodi:cy="294.93815"
|
||||
sodipodi:r1="1.8178079"
|
||||
sodipodi:r2="1.2179312"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="0.87266463"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 3.6909344,295.84704 -0.7913968,0.0241 -0.1611441,0.77519 -0.6217269,-0.49025 -0.621727,0.49025 -0.1611441,-0.77519 -0.79139678,-0.0241 0.37483979,-0.69741 -0.59076328,-0.52715 0.73543197,-0.2933 -0.11370504,-0.78356 0.75190744,0.24804 0.416557,-0.67333 0.4165569,0.67333 0.7519075,-0.24804 -0.1137051,0.78356 0.735432,0.2933 -0.5907633,0.52715 z"
|
||||
inkscape:transform-center-x="0.06028767"
|
||||
inkscape:transform-center-y="0.030212626" />
|
||||
style="fill:#00f8c5;stroke:none;stroke-width:0.26458333;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1"
|
||||
d="m 2.1143043,292.83045 1.1824699,2.0481 -1.1824699,2.0481 -1.18246994,-2.0481 1.18246994,-2.0481"
|
||||
id="path820"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 4.2333333 4.2333333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="file.svg"
|
||||
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\nl\andrewlalis\crystalkeep\ui\images\file_shard_node_icon.png"
|
||||
inkscape:export-xdpi="1536"
|
||||
inkscape:export-ydpi="1536">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="10.333173"
|
||||
inkscape:cy="9.4475152"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-292.76667)">
|
||||
<g
|
||||
id="g832"
|
||||
transform="matrix(2.2606665,0,0,2.2606665,-2.7169913,-371.69624)">
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path826"
|
||||
d="m 2.4538923,294.00575 v 0.19782 H 2.664343 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.01322917;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path824"
|
||||
d="m 1.6119693,295.71377 v -1.70802 h 0.841923 v 0.19782 H 2.664343 v 1.5102 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.02645833;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 4.2333333 4.2333333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="login_credentials.svg"
|
||||
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\nl\andrewlalis\crystalkeep\ui\images\login_credentials_shard_node_icon.png"
|
||||
inkscape:export-xdpi="1536"
|
||||
inkscape:export-ydpi="1536">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="45.254834"
|
||||
inkscape:cx="12.226157"
|
||||
inkscape:cy="8.1661844"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:lockguides="true">
|
||||
<sodipodi:guide
|
||||
position="0.52618688,3.2974378"
|
||||
orientation="1,0"
|
||||
id="guide815"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="1.0348342,3.7066942"
|
||||
orientation="0,1"
|
||||
id="guide817"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="3.7008477,2.8238696"
|
||||
orientation="1,0"
|
||||
id="guide819"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="-0.59634513,0.5320334"
|
||||
orientation="0,1"
|
||||
id="guide821"
|
||||
inkscape:locked="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-292.76667)">
|
||||
<path
|
||||
style="fill:#edf9fd;stroke:#000000;stroke-width:0.396875;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1"
|
||||
d="m 0.52618687,294.18783 v -0.89452 H 3.7008477 v 3.17466 H 0.52618687 v -1.07576"
|
||||
id="path823"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.396875;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="M 0.22324218,294.76758 H 2.5879557 l -0.72347,-0.72347 0.72347,0.72347 -0.7420736,0.74207"
|
||||
id="path825"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
|
@ -15,10 +15,10 @@
|
|||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="text_shard_node_icon.svg"
|
||||
sodipodi:docname="text.svg"
|
||||
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\nl\andrewlalis\crystalkeep\ui\images\text_shard_node_icon.png"
|
||||
inkscape:export-xdpi="192"
|
||||
inkscape:export-ydpi="192">
|
||||
inkscape:export-xdpi="1536"
|
||||
inkscape:export-ydpi="1536">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
|
@ -29,8 +29,8 @@
|
|||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="44.8"
|
||||
inkscape:cx="8.2633984"
|
||||
inkscape:cy="6.8552682"
|
||||
inkscape:cx="5.8060872"
|
||||
inkscape:cy="8.9176258"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
|
@ -39,7 +39,9 @@
|
|||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
units="px" />
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
|
@ -57,33 +59,16 @@
|
|||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-292.76667)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#72deff;fill-opacity:1;stroke:none;stroke-width:0.07803907;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="path817"
|
||||
sodipodi:sides="9"
|
||||
sodipodi:cx="2.1166666"
|
||||
sodipodi:cy="294.93815"
|
||||
sodipodi:r1="1.8178079"
|
||||
sodipodi:r2="1.2179312"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="0.87266463"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 3.6909344,295.84704 -0.7913968,0.0241 -0.1611441,0.77519 -0.6217269,-0.49025 -0.621727,0.49025 -0.1611441,-0.77519 -0.79139678,-0.0241 0.37483979,-0.69741 -0.59076328,-0.52715 0.73543197,-0.2933 -0.11370504,-0.78356 0.75190744,0.24804 0.416557,-0.67333 0.4165569,0.67333 0.7519075,-0.24804 -0.1137051,0.78356 0.735432,0.2933 -0.5907633,0.52715 z"
|
||||
inkscape:transform-center-x="0.06028767"
|
||||
inkscape:transform-center-y="0.030212626" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:1.9992249px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.12495156"
|
||||
x="1.4333378"
|
||||
y="295.61206"
|
||||
id="text818"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.69617891px;line-height:1.25;font-family:'Times New Roman';-inkscape-font-specification:'Times New Roman, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.35601118"
|
||||
x="0.36164278"
|
||||
y="296.76907"
|
||||
id="text817"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan816"
|
||||
x="1.4333378"
|
||||
y="295.61206"
|
||||
style="stroke-width:0.12495156">A</tspan></text>
|
||||
id="tspan815"
|
||||
x="0.36164278"
|
||||
y="296.76907"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.69617891px;font-family:'Times New Roman';-inkscape-font-specification:'Times New Roman, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.35601118">T</tspan></text>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.0 KiB |
|
@ -53,7 +53,7 @@ public class AddShardHandler implements EventHandler<ActionEvent> {
|
|||
switch (type) {
|
||||
case TEXT: return new TextShard(name);
|
||||
case LOGIN_CREDENTIALS: return new LoginCredentialsShard(name);
|
||||
case FILE: return new FileShard(name, "", "", new byte[0]);
|
||||
case FILE: return new FileShard(name, "", new byte[0]);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import javafx.scene.control.TreeItem;
|
|||
import javafx.scene.layout.VBox;
|
||||
import nl.andrewlalis.crystalkeep.model.CrystalItem;
|
||||
import nl.andrewlalis.crystalkeep.view.ShardTreeItem;
|
||||
import nl.andrewlalis.crystalkeep.view.shard_details.ViewModels;
|
||||
import nl.andrewlalis.crystalkeep.view.shards.ViewModels;
|
||||
|
||||
/**
|
||||
* This listener will update the shard detail container pane (the main center
|
||||
|
|
|
@ -23,6 +23,8 @@ public class MainViewController implements ModelListener {
|
|||
public TreeView<CrystalItem> clusterTreeView;
|
||||
@FXML
|
||||
public VBox shardDetailContainer;
|
||||
@FXML
|
||||
public Menu fileMenu;
|
||||
|
||||
public void init(Model model) {
|
||||
this.model = model;
|
||||
|
@ -91,7 +93,6 @@ public class MainViewController implements ModelListener {
|
|||
@FXML
|
||||
public void save() {
|
||||
if (model.getActiveCluster() == null) return;
|
||||
ClusterIO loader = new ClusterIO();
|
||||
Path path = model.getActiveClusterPath();
|
||||
if (path == null) {
|
||||
FileChooser chooser = new FileChooser();
|
||||
|
@ -102,27 +103,12 @@ public class MainViewController implements ModelListener {
|
|||
if (file == null) return;
|
||||
path = file.toPath();
|
||||
}
|
||||
char[] pw = this.model.getActiveClusterPassword();
|
||||
if (pw == null) {
|
||||
pw = this.promptPassword().orElse(new char[0]);
|
||||
}
|
||||
try {
|
||||
if (pw.length == 0) {
|
||||
loader.saveUnencrypted(model.getActiveCluster(), path);
|
||||
} else {
|
||||
loader.save(model.getActiveCluster(), path, pw);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
var alert = new Alert(Alert.AlertType.ERROR, "Could not save cluster.");
|
||||
alert.showAndWait();
|
||||
}
|
||||
this.saveCluster(path);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void saveAs() {
|
||||
if (model.getActiveCluster() == null) return;
|
||||
ClusterIO clusterIO = new ClusterIO();
|
||||
Path path = model.getActiveClusterPath();
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setTitle("Save Cluster");
|
||||
|
@ -133,21 +119,7 @@ public class MainViewController implements ModelListener {
|
|||
File file = chooser.showSaveDialog(this.clusterTreeView.getScene().getWindow());
|
||||
if (file == null) return;
|
||||
path = file.toPath();
|
||||
char[] pw = this.model.getActiveClusterPassword();
|
||||
if (pw == null) {
|
||||
pw = this.promptPassword().orElse(new char[0]);
|
||||
}
|
||||
try {
|
||||
if (pw.length == 0) {
|
||||
clusterIO.saveUnencrypted(model.getActiveCluster(), path);
|
||||
} else {
|
||||
clusterIO.save(model.getActiveCluster(), path, pw);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
var alert = new Alert(Alert.AlertType.ERROR, "Could not save cluster.");
|
||||
alert.showAndWait();
|
||||
}
|
||||
this.saveCluster(path);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -175,4 +147,23 @@ public class MainViewController implements ModelListener {
|
|||
});
|
||||
return d.showAndWait();
|
||||
}
|
||||
|
||||
private void saveCluster(Path path) {
|
||||
char[] pw = this.model.getActiveClusterPassword();
|
||||
if (pw == null) {
|
||||
pw = this.promptPassword().orElse(new char[0]);
|
||||
}
|
||||
ClusterIO loader = new ClusterIO();
|
||||
try {
|
||||
if (pw.length == 0) {
|
||||
loader.saveUnencrypted(model.getActiveCluster(), path);
|
||||
} else {
|
||||
loader.save(model.getActiveCluster(), path, pw);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
var alert = new Alert(Alert.AlertType.ERROR, "Could not save cluster.");
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package nl.andrewlalis.crystalkeep.io.serialization;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -25,6 +24,32 @@ public class ByteUtils {
|
|||
os.write(toBytes(value));
|
||||
}
|
||||
|
||||
public static byte[] longToBytes(long l) {
|
||||
byte[] result = new byte[8];
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
result[i] = (byte)(l & 0xFF);
|
||||
l >>= 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long bytesToLong(final byte[] b) {
|
||||
long result = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
result <<= 8;
|
||||
result |= (b[i] & 0xFF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void writeLong(long value, OutputStream os) throws IOException {
|
||||
os.write(longToBytes(value));
|
||||
}
|
||||
|
||||
public static long readLong(InputStream is) throws IOException {
|
||||
return bytesToLong(is.readNBytes(Long.BYTES));
|
||||
}
|
||||
|
||||
public static void writeLengthPrefixed(byte[] bytes, OutputStream os) throws IOException {
|
||||
os.write(toBytes(bytes.length));
|
||||
os.write(bytes);
|
||||
|
@ -34,12 +59,10 @@ public class ByteUtils {
|
|||
writeLengthPrefixed(s.getBytes(StandardCharsets.UTF_8), os);
|
||||
}
|
||||
|
||||
public static byte[] writeLengthPrefixedStrings(String[] strings) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
public static void writeLengthPrefixedStrings(String[] strings, OutputStream os) throws IOException {
|
||||
for (String s : strings) {
|
||||
writeLengthPrefixed(s, bos);
|
||||
writeLengthPrefixed(s, os);
|
||||
}
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] readLengthPrefixed(InputStream is) throws IOException {
|
||||
|
|
|
@ -2,8 +2,9 @@ package nl.andrewlalis.crystalkeep.io.serialization;
|
|||
|
||||
import nl.andrewlalis.crystalkeep.model.Cluster;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
|
||||
import nl.andrewlalis.crystalkeep.model.ShardType;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.FileShard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -27,6 +28,7 @@ public class ClusterSerializer {
|
|||
static {
|
||||
serializers.put(ShardType.TEXT, new TextShard.Serializer());
|
||||
serializers.put(ShardType.LOGIN_CREDENTIALS, new LoginCredentialsShard.Serializer());
|
||||
serializers.put(ShardType.FILE, new FileShard.Serializer());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,7 +84,7 @@ public class ClusterSerializer {
|
|||
ByteUtils.writeLengthPrefixed(shard.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME).getBytes(StandardCharsets.UTF_8), os);
|
||||
os.write(ByteUtils.toBytes(shard.getType().getValue()));
|
||||
ShardSerializer serializer = serializers.get(shard.getType());
|
||||
os.write(serializer.serialize(shard));
|
||||
serializer.serialize(shard, os);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,10 +4,16 @@ import nl.andrewlalis.crystalkeep.model.Shard;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* This interface should be implemented to provide the ability to serialize and
|
||||
* deserialize a shard to and from a stream of bytes.
|
||||
* @param <T> The shard type.
|
||||
*/
|
||||
public interface ShardSerializer<T extends Shard> {
|
||||
byte[] serialize(T shard) throws IOException;
|
||||
void serialize(T shard, OutputStream os) throws IOException;
|
||||
|
||||
T deserialize(InputStream is, String name, LocalDateTime createdAt) throws IOException;
|
||||
}
|
||||
|
|
|
@ -1,35 +1,57 @@
|
|||
package nl.andrewlalis.crystalkeep.model.shards;
|
||||
|
||||
import nl.andrewlalis.crystalkeep.io.serialization.ByteUtils;
|
||||
import nl.andrewlalis.crystalkeep.io.serialization.ShardSerializer;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.ShardType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class FileShard extends Shard {
|
||||
private String fileName;
|
||||
private String mimeType;
|
||||
private byte[] contents;
|
||||
|
||||
public FileShard(String name, LocalDateTime createdAt, String fileName, String mimeType, byte[] contents) {
|
||||
public FileShard(String name, LocalDateTime createdAt, String fileName, byte[] contents) {
|
||||
super(name, createdAt, ShardType.FILE);
|
||||
this.fileName = fileName;
|
||||
this.mimeType = mimeType;
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
public FileShard(String name, String fileName, String mimeType, byte[] contents) {
|
||||
this(name, LocalDateTime.now(), fileName, mimeType, contents);
|
||||
public FileShard(String name, String fileName, byte[] contents) {
|
||||
this(name, LocalDateTime.now(), fileName, contents);
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public byte[] getContents() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
public void setContents(byte[] contents) {
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
public static final class Serializer implements ShardSerializer<FileShard> {
|
||||
@Override
|
||||
public void serialize(FileShard shard, OutputStream os) throws IOException {
|
||||
ByteUtils.writeLengthPrefixed(shard.getFileName(), os);
|
||||
ByteUtils.writeLengthPrefixed(shard.getContents(), os);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileShard deserialize(InputStream is, String name, LocalDateTime createdAt) throws IOException {
|
||||
String fileName = ByteUtils.readLengthPrefixedString(is);
|
||||
byte[] contents = ByteUtils.readLengthPrefixed(is);
|
||||
return new FileShard(name, createdAt, fileName, contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package nl.andrewlalis.crystalkeep.model.shards;
|
||||
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.ShardType;
|
||||
import nl.andrewlalis.crystalkeep.io.serialization.ByteUtils;
|
||||
import nl.andrewlalis.crystalkeep.io.serialization.ShardSerializer;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.ShardType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class LoginCredentialsShard extends Shard {
|
||||
|
@ -44,11 +45,11 @@ public class LoginCredentialsShard extends Shard {
|
|||
return super.toString() + ", username=\"" + this.username + "\", password=\"" + this.password + "\"";
|
||||
}
|
||||
|
||||
public static class Serializer implements ShardSerializer<LoginCredentialsShard> {
|
||||
public static final class Serializer implements ShardSerializer<LoginCredentialsShard> {
|
||||
|
||||
@Override
|
||||
public byte[] serialize(LoginCredentialsShard shard) throws IOException {
|
||||
return ByteUtils.writeLengthPrefixedStrings(new String[]{shard.getUsername(), shard.getPassword()});
|
||||
public void serialize(LoginCredentialsShard shard, OutputStream os) throws IOException {
|
||||
ByteUtils.writeLengthPrefixedStrings(new String[]{shard.getUsername(), shard.getPassword()}, os);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package nl.andrewlalis.crystalkeep.model.shards;
|
||||
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.ShardType;
|
||||
import nl.andrewlalis.crystalkeep.io.serialization.ByteUtils;
|
||||
import nl.andrewlalis.crystalkeep.io.serialization.ShardSerializer;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.ShardType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class TextShard extends Shard {
|
||||
|
@ -34,10 +35,10 @@ public class TextShard extends Shard {
|
|||
return super.toString() + ", text=\"" + this.text + "\"";
|
||||
}
|
||||
|
||||
public static class Serializer implements ShardSerializer<TextShard> {
|
||||
public static final class Serializer implements ShardSerializer<TextShard> {
|
||||
@Override
|
||||
public byte[] serialize(TextShard shard) throws IOException {
|
||||
return ByteUtils.writeLengthPrefixedStrings(new String[]{shard.getText()});
|
||||
public void serialize(TextShard shard, OutputStream os) throws IOException {
|
||||
ByteUtils.writeLengthPrefixedStrings(new String[]{shard.getText()}, os);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package nl.andrewlalis.crystalkeep.util;
|
||||
|
||||
public class StringUtils {
|
||||
public static boolean endsWithAny(String s, String... suffixes) {
|
||||
for (String suffix : suffixes) {
|
||||
if (s.endsWith(suffix)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import nl.andrewlalis.crystalkeep.model.CrystalItem;
|
|||
import nl.andrewlalis.crystalkeep.model.Model;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.util.ImageCache;
|
||||
import nl.andrewlalis.crystalkeep.view.shard_details.ViewModels;
|
||||
import nl.andrewlalis.crystalkeep.view.shards.ViewModels;
|
||||
|
||||
public class CrystalItemTreeCell extends TreeCell<CrystalItem> {
|
||||
private static final String CLUSTER_ICON = "/nl/andrewlalis/crystalkeep/ui/images/cluster_node_icon.png";
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package nl.andrewlalis.crystalkeep.view.shards;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.FileChooser;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.FileShard;
|
||||
import nl.andrewlalis.crystalkeep.util.StringUtils;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class FileShardViewModel extends ShardViewModel<FileShard> {
|
||||
public FileShardViewModel(FileShard shard) {
|
||||
super(shard);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node getContent(FileShard shard) {
|
||||
VBox container = new VBox(10);
|
||||
container.setPadding(new Insets(5));
|
||||
GridPane gp = new GridPane();
|
||||
gp.setVgap(5);
|
||||
gp.setHgap(5);
|
||||
|
||||
gp.add(new Label("File Name"), 0, 0);
|
||||
TextField nameField = new TextField(shard.getFileName());
|
||||
nameField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
shard.setFileName(newValue);
|
||||
});
|
||||
gp.add(nameField, 1, 0);
|
||||
gp.add(new Label("File Size"), 0, 1);
|
||||
TextField sizeField = new TextField(shard.getContents().length + " bytes");
|
||||
sizeField.setEditable(false);
|
||||
gp.add(sizeField, 1, 1);
|
||||
|
||||
Button setFileButton = new Button("Set File");
|
||||
setFileButton.setOnAction(event -> {
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setTitle("Choose a File");
|
||||
File file = chooser.showOpenDialog(gp.getScene().getWindow());
|
||||
if (file != null) {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
byte[] contents = fis.readAllBytes();
|
||||
shard.setFileName(file.getName());
|
||||
shard.setContents(contents);
|
||||
nameField.setText(shard.getFileName());
|
||||
sizeField.setText(shard.getContents().length + " bytes");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
gp.add(setFileButton, 0, 2);
|
||||
|
||||
Button extractFileButton = new Button("Extract File");
|
||||
extractFileButton.setOnAction(event -> {
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setTitle("Extract File");
|
||||
chooser.setInitialFileName(shard.getFileName());
|
||||
File file = chooser.showSaveDialog(gp.getScene().getWindow());
|
||||
if (file != null) {
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(shard.getContents());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
gp.add(extractFileButton, 1, 2);
|
||||
container.getChildren().add(gp);
|
||||
|
||||
if (StringUtils.endsWithAny(shard.getFileName().toLowerCase(), ".png", ".jpg", ".jpeg", ".gif")) {
|
||||
container.getChildren().add(new Separator(Orientation.HORIZONTAL));
|
||||
ImageView imageView = new ImageView(new Image(new ByteArrayInputStream(shard.getContents())));
|
||||
imageView.setPreserveRatio(true);
|
||||
ScrollPane scrollPane = new ScrollPane(imageView);
|
||||
container.getChildren().add(scrollPane);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconPath() {
|
||||
return "/nl/andrewlalis/crystalkeep/ui/images/file_shard_node_icon.png";
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewlalis.crystalkeep.view.shard_details;
|
||||
package nl.andrewlalis.crystalkeep.view.shards;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewlalis.crystalkeep.view.shard_details;
|
||||
package nl.andrewlalis.crystalkeep.view.shards;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Orientation;
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewlalis.crystalkeep.view.shard_details;
|
||||
package nl.andrewlalis.crystalkeep.view.shards;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.TextArea;
|
|
@ -1,6 +1,7 @@
|
|||
package nl.andrewlalis.crystalkeep.view.shard_details;
|
||||
package nl.andrewlalis.crystalkeep.view.shards;
|
||||
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.FileShard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
|
||||
|
||||
|
@ -17,6 +18,7 @@ public class ViewModels {
|
|||
static {
|
||||
shardViewModels.put(TextShard.class, TextShardViewModel.class);
|
||||
shardViewModels.put(LoginCredentialsShard.class, LoginCredentialsViewModel.class);
|
||||
shardViewModels.put(FileShard.class, FileShardViewModel.class);
|
||||
}
|
||||
|
||||
public static Optional<ShardViewModel<?>> get(Shard shard) {
|
|
@ -4,7 +4,7 @@
|
|||
<?import javafx.scene.layout.*?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="nl.andrewlalis.crystalkeep.control.MainViewController">
|
||||
<MenuBar>
|
||||
<Menu text="File">
|
||||
<Menu fx:id="fileMenu" text="File">
|
||||
<MenuItem text="New" onAction="#newCluster" />
|
||||
<MenuItem text="Load" onAction="#load" />
|
||||
<MenuItem text="Save" onAction="#save" />
|
||||
|
|
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 795 B After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.3 KiB |