cybermax / glintpane /app.js
CyberMax-tools's picture
Deploy glintpane to the hub
6eed94a verified
Raw History Blame Contribute Delete
8.14 kB
// Glintpane hub page: live 9-slice builder, icon browser and UI piece gallery. © 2026 CyberMax.
(function () {
'use strict';
var $ = function (s) { return document.querySelector(s); };
var KIT = null;
var SCALE = 0.25; // same SliceScale the Luau module uses
var COLORS = ['green', 'blue', 'red', 'gold', 'purple', 'orange', 'pink', 'cyan', 'grey'];
var SWATCH = { green: '#52D66A', blue: '#3FA9FF', red: '#FF4D5E', gold: '#FFC531', purple: '#A66BFF', orange: '#FF8A33', pink: '#FF6FB5', cyan: '#39E0E0', grey: '#C3CAD8' };
var state = { color: 'green', icon: 'play', mono: false, tint: '#ffffff', cat: 'All', q: '' };
function toast(msg) { var t = $('#toast'); t.textContent = msg; t.classList.add('on'); setTimeout(function () { t.classList.remove('on'); }, 2200); }
// Apply a kit piece to an element as CSS border-image with the module's SliceCenter and SliceScale.
function slice(el, piece) {
var m = KIT.piece_meta[piece];
var url = 'img/ui/' + piece + '.png';
if (!m.slice) { el.style.borderImage = 'none'; el.style.background = 'url(' + url + ') center/contain no-repeat'; return; }
var x0 = m.slice[0], y0 = m.slice[1], x1 = m.slice[2], y1 = m.slice[3];
var t = y0, r = m.w - x1, b = m.h - y1, l = x0;
el.style.borderStyle = 'solid';
el.style.borderWidth = (t * SCALE) + 'px ' + (r * SCALE) + 'px ' + (b * SCALE) + 'px ' + (l * SCALE) + 'px';
el.style.borderImage = 'url(' + url + ') ' + t + ' ' + r + ' ' + b + ' ' + l + ' fill stretch';
el.dataset.piece = piece;
}
function iconSrc(name, mono) { return 'img/' + (mono ? 'mono' : 'color') + '/' + name + '.png'; }
function setButtonIcon() {
var img = $('#gbtn-icon');
if (state.mono) {
img.style.visibility = 'hidden';
var tinted = $('#gbtn-tint');
if (!tinted) { tinted = document.createElement('span'); tinted.id = 'gbtn-tint'; tinted.className = 'tint'; img.after(tinted); }
tinted.style.webkitMaskImage = tinted.style.maskImage = 'url(' + iconSrc(state.icon, true) + ')';
tinted.style.backgroundColor = state.tint;
img.style.display = 'none'; tinted.style.display = '';
} else {
var t2 = $('#gbtn-tint'); if (t2) t2.style.display = 'none';
img.style.display = ''; img.style.visibility = ''; img.src = iconSrc(state.icon, false);
}
}
function buildBuilder() {
var btn = $('#gbtn');
var hovering = false;
function show(s) { slice(btn, 'button-' + state.color + '-' + s); btn.dataset.state = s; }
btn.addEventListener('pointerenter', function () { hovering = true; show('hover'); });
btn.addEventListener('pointerleave', function () { hovering = false; show('normal'); });
btn.addEventListener('pointerdown', function () { show('pressed'); });
btn.addEventListener('pointerup', function () { show(hovering ? 'hover' : 'normal'); });
btn.addEventListener('click', function () { toast('Clicked! In Roblox: button.Activated:Connect(...)'); });
show('normal');
slice($('#panel'), 'panel-light');
slice($('#track'), 'bar-track');
slice($('#fill'), 'bar-fill-green');
slice($('.pill'), 'bar-track');
var rar = [['common', 'egg'], ['uncommon', 'pet'], ['rare', 'sword'], ['epic', 'crystal'], ['legendary', 'crown'], ['mythic', 'diamond']];
$('#slots').innerHTML = rar.map(function (r) { return '<div class="slot" title="' + r[0] + '" data-r="' + r[0] + '"><img src="' + iconSrc(r[1]) + '" alt="' + r[1] + '" width="48" height="48"><span>' + r[0] + '</span></div>'; }).join('');
document.querySelectorAll('#slots .slot').forEach(function (s) { slice(s, 'slot-' + s.dataset.r); });
$('#colors').innerHTML = COLORS.map(function (c) { return '<button type="button" class="sw' + (c === state.color ? ' on' : '') + '" data-c="' + c + '" style="background:' + SWATCH[c] + '" aria-label="' + c + '"></button>'; }).join('');
$('#colors').addEventListener('click', function (e) {
var b = e.target.closest('[data-c]'); if (!b) return;
state.color = b.dataset.c;
document.querySelectorAll('#colors .sw').forEach(function (x) { x.classList.toggle('on', x === b); });
show('normal');
slice($('#fill'), 'bar-fill-' + (['green', 'blue', 'red', 'gold', 'purple'].indexOf(state.color) >= 0 ? state.color : 'green'));
});
$('#t-text').addEventListener('input', function (e) { $('#gbtn-text').textContent = e.target.value || ' '; });
$('#t-width').addEventListener('input', function (e) { btn.style.width = e.target.value + 'px'; });
var all = [];
Object.keys(KIT.categories).forEach(function (c) { all = all.concat(KIT.categories[c]); });
$('#t-icon').innerHTML = all.map(function (n) { return '<option' + (n === state.icon ? ' selected' : '') + '>' + n + '</option>'; }).join('');
$('#t-icon').addEventListener('change', function (e) { state.icon = e.target.value; setButtonIcon(); });
$('#t-mono').addEventListener('change', function (e) { state.mono = e.target.checked; setButtonIcon(); });
$('#t-tint').addEventListener('input', function (e) { state.tint = e.target.value; if (!state.mono) { state.mono = true; $('#t-mono').checked = true; } setButtonIcon(); });
$('#t-bar').addEventListener('input', function (e) { var v = +e.target.value; $('#fill').style.width = v + '%'; $('#fill').style.display = v > 0 ? '' : 'none'; });
$('#fill').style.width = '65%';
btn.style.width = '240px';
}
function renderIcons() {
var mono = $('#mono-all').checked;
var names = [];
Object.keys(KIT.categories).forEach(function (c) { if (state.cat === 'All' || state.cat === c) names = names.concat(KIT.categories[c]); });
var q = state.q.trim().toLowerCase();
if (q) names = names.filter(function (n) { return n.indexOf(q) >= 0; });
$('#icons').innerHTML = names.map(function (n) {
return '<figure class="ic"><img src="' + iconSrc(n, mono) + '" alt="' + n + ' icon" width="72" height="72" loading="lazy"><figcaption>' + n +
(KIT.sampler.indexOf(n) >= 0 ? ' <span class="free-badge">FREE</span>' : '') + '</figcaption></figure>';
}).join('') || '<p class="hint">No icon matches. Try "coin", "egg" or "arrow".</p>';
}
function buildIcons() {
var cats = ['All'].concat(Object.keys(KIT.categories));
$('#cats').innerHTML = cats.map(function (c) {
var n = c === 'All' ? KIT.icons : KIT.categories[c].length;
return '<button type="button" class="chip' + (c === 'All' ? ' on' : '') + '" data-cat="' + c + '">' + c + '<span class="n">' + n + '</span></button>';
}).join('');
$('#cats').addEventListener('click', function (e) {
var b = e.target.closest('[data-cat]'); if (!b) return;
state.cat = b.dataset.cat;
document.querySelectorAll('#cats .chip').forEach(function (x) { x.classList.toggle('on', x === b); });
renderIcons();
});
$('#q').addEventListener('input', function (e) { state.q = e.target.value; renderIcons(); });
$('#mono-all').addEventListener('change', renderIcons);
$('#light-bg').addEventListener('change', function (e) { $('#icons').classList.toggle('light', e.target.checked); });
renderIcons();
}
function buildPieces() {
var groups = {};
Object.keys(KIT.piece_meta).forEach(function (k) { var g = KIT.piece_meta[k].group; (groups[g] = groups[g] || []).push(k); });
$('#pieces').innerHTML = Object.keys(groups).map(function (g) {
return '<h3>' + g + ' <span class="n">' + groups[g].length + '</span></h3><div class="pc-row">' + groups[g].map(function (k) {
var m = KIT.piece_meta[k];
return '<figure class="pc"><img src="img/ui/' + k + '.png" alt="' + k + '" width="' + (m.w / 4) + '" height="' + (m.h / 4) + '" loading="lazy"><figcaption>' + k + '</figcaption></figure>';
}).join('') + '</div>';
}).join('');
}
$('#btn-free').addEventListener('click', function () { toast('Free sampler downloading: 12 icons + the green button.'); });
fetch('kit.json').then(function (r) { return r.json(); }).then(function (kit) {
KIT = kit;
buildBuilder();
buildIcons();
buildPieces();
document.body.dataset.ready = '1';
});
})();