WEBSITE - WIP - icon shapes - editor fixes + fit to top left quarter

This commit is contained in:
Alex Sim committed 2026-10-10 23:37:36 +02:00
1 parent e5623ed823
commit ea5f458a95
3 files changed
+23 -16

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@ description: >-
url: "https://alesimula.github.io"
baseurl: ""
version: "1.0.16" #Website version - increase to invalidate cached resources
version: "1.0.17" #Website version - increase to invalidate cached resources
github_repo: "alesimula/Murine-launcher"
icon_url: "https://raw.githubusercontent.com/alesimula/Murine-launcher/refs/heads/main/image/app_icon/icon_browser_fullsize.svg"
icon_freeform_url: "https://raw.githubusercontent.com/alesimula/Murine-launcher/refs/heads/main/image/app_icon/icon_freeform.svg"
+13 -11
View File
@@ -152,12 +152,6 @@
const k = Math.min(kx, ky), cx = lockX ? 50 : bb.x + bb.width / 2, cy = lockY ? 50 : bb.y + bb.height / 2;
return list.map((s) => transform(s, k, cx, cy, 50, 50));
}
// Puts `sh` where `old` was: same centre, longest side as long as old's.
function placeLike(sh, old) {
const a = measure(shapePath(old)), b = measure(shapePath(sh));
const k = Math.max(a.width, a.height) / Math.max(b.width, b.height, 0.01);
return { ...transform(sh, k, b.x + b.width / 2, b.y + b.height / 2, a.x + a.width / 2, a.y + a.height / 2), mirror: old.mirror };
}
const presetShapes = (name) => (Object.hasOwn(FIXED, name)
? [{ fixed: name, a: 1, x: 0, y: 0, mirror: 'none' }]
: [{ pts: fit(PRESETS[name]()), mirror: 'none' }]);
@@ -400,7 +394,7 @@
h.setAttribute('x', c.x - 2); h.setAttribute('y', c.y - 2);
});
}
$('se-fit-shape').hidden = $('se-mirror').hidden = !moving;
$('se-fit-tools').hidden = $('se-mirror').hidden = !moving;
for (const b of $('se-mirror').querySelectorAll('button')) b.setAttribute('aria-pressed', b.dataset.mirror === shapes[active].mirror);
renderCards();
@@ -642,6 +636,14 @@
// Move mode's buttons on the canvas: fit this shape, and mirroring.
$('se-fit-shape').onclick = () => { checkpoint(); shapes[active] = fitted([shapes[active]])[0]; sync(); render(); };
// The shape's own outline (not its mirrored copies) into the top-left quarter, centred there: an ear
// mirrored left/right or both ways then fills the other quarters.
$('se-fit-quarter').onclick = () => {
const bb = measure(shapePath(shapes[active])), k = 50 / Math.max(bb.width, bb.height, 0.01);
checkpoint();
shapes[active] = transform(shapes[active], k, bb.x + bb.width / 2, bb.y + bb.height / 2, 25, 25);
sync(); render();
};
$('se-mirror').addEventListener('click', (e) => {
const b = e.target.closest('[data-mirror]');
if (!b || b.dataset.mirror === shapes[active].mirror) return;
@@ -691,8 +693,8 @@
$('se-gen').onclick = () => {
const n = clamp(+$('se-sides').value || 6, 3, 24), inner = clamp(+$('se-inner').value || 100, 10, 100) / 100;
const num = (id, max) => clamp(+$(id).value || 0, 0, max); // empty or junk input counts as 0
const made = { pts: fit(polygon(n, inner, +$('se-rot').value || 0, num('se-gen-r', 50), num('se-gen-ir', 50), num('se-gen-s', 100) / 100)), mirror: 'none' };
replaceShape(placeLike(made, shapes[active])); // takes the selected shape's place and size
const pts = fit(polygon(n, inner, +$('se-rot').value || 0, num('se-gen-r', 50), num('se-gen-ir', 50), num('se-gen-s', 100) / 100));
replaceShape({ pts, mirror: 'none' }); // a fresh full-size shape: no leftover mirroring either
};
$('se-show-grid').onchange = (e) => { grid.show = e.target.checked; drawGrid(); };
@@ -750,7 +752,7 @@
// ---------- presets ----------
// A preset in the grid replaces the whole icon; picked from the "+" card it is added as new shapes,
// at half size in the middle and in Move mode, ready to be placed.
// full canvas size and in Move mode, ready to be resized and placed.
const tile = (name) => {
const b = document.createElement('button');
b.type = 'button';
@@ -765,7 +767,7 @@
const a = tile(name);
a.onclick = () => {
checkpoint();
shapes.push(...presetShapes(name).map((s) => transform(s, 0.5, 50, 50, 50, 50)));
shapes.push(...presetShapes(name));
active = shapes.length - 1; sel = -1; mode = 'move';
sync(); addDialog.close(); render();
};
+9 -4
View File
@@ -111,7 +111,8 @@ noindex: true # and out of search engines
@media (prefers-reduced-motion: reduce) { .se-shake { animation: none; } }
.se-stage-btn, .se-mirror button { display: inline-flex; align-items: center; justify-content: center; padding: 0;
border-radius: var(--md-shape-full); color: var(--md-primary); cursor: pointer; }
.se-stage-btn { position: absolute; top: 10px; right: 10px; width: 40px; height: 40px;
.se-stage-tools { position: absolute; top: 10px; right: 10px; display: flex; gap: 6px; }
.se-stage-btn { width: 40px; height: 40px;
border: 1px solid var(--md-outline-variant); background: var(--md-surface-container-low); box-shadow: var(--md-shadow); }
.se-mirror { position: absolute; left: 10px; bottom: 10px; display: flex; gap: 2px; padding: 3px;
border-radius: var(--md-shape-full); border: 1px solid var(--md-outline-variant);
@@ -120,7 +121,8 @@ noindex: true # and out of search engines
.se-mirror button[aria-pressed="true"] { background: var(--md-primary-container); color: var(--md-on-primary-container); }
.se-mirror svg { width: 20px; height: 20px; }
.se-stage-btn:hover, .se-mirror button:hover { background: var(--md-surface-container-high); }
.se-stage-btn[hidden], .se-mirror[hidden] { display: none; }
.se-stage-tools[hidden], .se-mirror[hidden] { display: none; }
.se-stage-btn svg { width: 22px; height: 22px; }
/* Shape cards under the canvas. */
.se-cards { display: flex; gap: 10px; overflow-x: auto; padding: 12px 2px 6px; }
.se-card { position: relative; flex: 0 0 104px; display: flex; flex-direction: column; align-items: center;
@@ -180,7 +182,10 @@ noindex: true # and out of search engines
<rect class="se-resize" width="4" height="4" style="cursor: nesw-resize"/>
</g>
</svg>
<button id="se-fit-shape" class="se-stage-btn" type="button" title="Fit this shape to the box" aria-label="Fit this shape to the box" hidden><span class="material-symbols-outlined" aria-hidden="true">&#xea10;</span></button>
<div id="se-fit-tools" class="se-stage-tools" hidden>
<button id="se-fit-quarter" class="se-stage-btn" type="button" title="Fit this shape into the top-left quarter" aria-label="Fit this shape into the top-left quarter"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3.5" y="3.5" width="17" height="17" rx="2" fill="none" stroke="currentColor" stroke-width="1.8" stroke-dasharray="3 2"/><rect x="5.5" y="5.5" width="6.5" height="6.5" rx="1.5" fill="currentColor"/></svg></button>
<button id="se-fit-shape" class="se-stage-btn" type="button" title="Fit this shape to the box" aria-label="Fit this shape to the box"><span class="material-symbols-outlined" aria-hidden="true">&#xea10;</span></button>
</div>
<div id="se-mirror" class="se-mirror" role="group" aria-label="Mirror" hidden>
<button type="button" data-mirror="none" title="No mirroring" aria-label="No mirroring"><svg viewBox="0 0 20 20" fill="currentColor"><path d="M6 4l6 6-6 6z"/></svg></button>
<button type="button" data-mirror="h" title="Mirror left and right" aria-label="Mirror left and right"><svg viewBox="0 0 20 20" fill="currentColor"><path d="M10 2v16" stroke="currentColor" stroke-width="1.2" stroke-dasharray="2 1.6" fill="none"/><path d="M2 5l5 5-5 5zM18 5l-5 5 5 5z"/></svg></button>
@@ -303,7 +308,7 @@ noindex: true # and out of search engines
<span class="material-symbols-outlined">&#xE5CD;</span> <!-- close -->
</button>
</div>
<p class="contact-modal-desc">Pick a starting point. It's added at half size in the middle, ready to move and resize.</p>
<p class="contact-modal-desc">Pick a starting point. It's added at full size, ready to move and resize.</p>
<div id="se-add-grid" class="se-presets"></div>
</div>
</div>