Skip to content

Instantly share code, notes, and snippets.

@Meshiest
Last active January 4, 2021 06:32
Show Gist options
  • Select an option

  • Save Meshiest/e1791bbb97ac1da34663a83e714dba69 to your computer and use it in GitHub Desktop.

Select an option

Save Meshiest/e1791bbb97ac1da34663a83e714dba69 to your computer and use it in GitHub Desktop.

Revisions

  1. Meshiest revised this gist Jan 4, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -159,6 +159,7 @@

    .group::before {
    content: attr(name);
    white-space: nowrap;
    min-width: 40px;
    height: 24px;
    padding: 0 4px;
  2. Meshiest revised this gist Jan 4, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -727,7 +727,7 @@
    const palette = $('#palette');
    const group = document.createElement('div');
    group.className = 'group';
    group.setAttribute('name', 'Col ' + ($$('.group').length + 1));
    group.setAttribute('name', 'Group ' + ($$('.group').length + 1));
    const stub = document.createElement('div');
    stub.className = 'stub button';

    @@ -776,7 +776,7 @@
    groups: $$('.group').map((c, columnIndex) => {
    // convert colors to linear rgb from whatever format is thrown in
    return {
    name: c.getAttribute('name') || ('Col ' + (columnIndex + 1)),
    name: c.getAttribute('name') || ('Group ' + (columnIndex + 1)),
    colors: $$('.color', c).map(e => {
    // a technique so cursed you will shit the bed (pulling rgb from hex)
    let [sR, sG, sB] = e.style.backgroundColor.match(/[\d\.]+/g).map(Number);
  3. Meshiest revised this gist Jan 4, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -919,5 +919,5 @@ <h2>Example images (Click to demo)</h2>
    </div>
    </div>
    <h1 style="margin-left: 14px;">Palette</h1>
    <input type="color" id="colorPicker" value="#fffff">
    <input type="color" id="colorPicker" value="#ffffff">
    <div id="palette"></div>
  4. Meshiest revised this gist Jan 4, 2021. 1 changed file with 111 additions and 10 deletions.
    121 changes: 111 additions & 10 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -105,6 +105,7 @@
    display: flex;
    padding: 2px;
    align-items: flex-start;
    position: relative;
    }

    .group {
    @@ -117,6 +118,7 @@
    flex-direction: column;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
    background-color: #335;
    position: relative;
    }

    .group .stub {
    @@ -141,23 +143,73 @@
    margin-bottom: 4px;
    }

    .group.selected::after, .group.selected::before {
    opacity: 1;
    top: -20px;
    }
    .group::after, .group::before {
    position: absolute;
    pointer-events: none;
    opacity: 0;
    top: -10px;
    background-color: #335;
    left: 0;
    transition: opacity .1s ease, top .1s ease;
    }

    .group::before {
    content: attr(name);
    min-width: 40px;
    height: 24px;
    padding: 0 4px;
    transform: translate(0%, -100%);
    display: flex;
    align-items: center;
    z-index: 1;
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
    }

    .group::after {
    content: '';
    width: 24px;
    height: 24px;
    transform: translate(5px, -12px) rotate(45deg);
    }

    #palette > .add {
    margin: 4px;
    }

    #palette > .add::before {
    content: "Description: " attr(description);
    position: absolute;
    pointer-events: none;
    top: -2px;
    left: 0;
    transform: translateY(-100%);
    font-weight: normal;
    font-size: 14px;
    }

    .color {
    order: 0;
    width: 32px;
    height: 32px;
    margin-bottom: 4px;
    cursor: pointer;
    transform: scale(1);
    transition: transform .1s ease;
    }

    .color.selected {
    width: 28px;
    height: 28px;
    border: 2px solid #aad;
    border-radius: 50%;
    transform: scale(1.4);
    z-index: 1;
    /*border-radius: 50%;*/
    }

    .button:hover { background-color: #779; }
    @@ -168,6 +220,14 @@
    order: 10;
    }

    #colorPicker {
    position: absolute;
    left: 28px;
    margin-top: -15px;
    opacity: 0;
    pointer-events: none;
    }

    </style>
    <script>

    @@ -339,7 +399,7 @@
    const [r, g, b] = linearRGB([sR, sG, sB]);
    return { r, g, b, a: 255 };
    }),
    name: div.match(/DIV: (.*)/)[1],
    name: div.match(/DIV: ?(.*)/)[1],
    })),
    }
    initPalette(data);
    @@ -407,11 +467,43 @@
    undo();
    save();

    // rename group on r
    } else if (e.code === 'KeyR' && !e.ctrlKey && !e.shiftKey) {
    if (!selected || !selected.classList.contains('group')) return;
    const name = prompt('Enter a column name', selected.getAttribute('name'));
    if (name) {
    selected.setAttribute('name', name);
    snapshot();
    }

    // change palette description on shift R
    } else if (e.code === 'KeyR' && !e.ctrlKey && e.shiftKey) {
    const btn = $('#palette > .add');
    const description = prompt('Enter a palette description', btn.getAttribute('description'));
    if (description) {
    btn.setAttribute('description', description);
    snapshot();
    }

    } else if (e.code === 'KeyC' && !e.ctrlKey && !e.shiftKey) {
    if (!selected || !selected.classList.contains('color')) return;
    $('#colorPicker').value = '#' + selected.getAttribute('hex');
    $('#colorPicker').onchange = e => {
    selected.setAttribute('hex', e.target.value.replace(/^#/, ''))
    selected.style.background = e.target.value;
    snapshot();
    $('#colorPicker').onchange = () => {};
    };
    $('#colorPicker').click();

    // save on ctrl s
    } else if (e.code === 'KeyS' && e.ctrlKey && e.shiftKey) {
    e.preventDefault();
    const name = prompt('Enter a save name', localStorage.lastName || 'Generated');
    if (typeof name === 'object') return;
    localStorage.lastName = name;
    $('#download').href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(save(), 0, 2));
    $('#download').download = 'Generated' + '.bp';
    $('#download').download = name + '.bp';
    $('#download').click();

    } else if ((e.code === 'KeyS' || e.code === 'KeyW') && e.ctrlKey) {
    @@ -474,7 +566,7 @@
    if (numGroups < 2) return;
    modFn(getAt((col + 1) % numGroups, row));

    // delete everything
    // move color to the left
    } else if (e.code === 'KeyA' && e.ctrlKey) {
    e.preventDefault();
    // if nothing is selected, ignore
    @@ -499,6 +591,7 @@

    snapshot();

    // move color to the right
    } else if (e.code === 'KeyD' && e.ctrlKey) {
    e.preventDefault();
    if (!selected) return;
    @@ -508,7 +601,6 @@
    const numGroups = $$('.group').length;
    if (numGroups < 2) return;
    const el = getAt((col + 1) % numGroups, row);
    console.log(el, (col + 1) % numGroups, row);

    // ignore group movements
    if (selected.classList.contains('group')) return;
    @@ -635,6 +727,7 @@
    const palette = $('#palette');
    const group = document.createElement('div');
    group.className = 'group';
    group.setAttribute('name', 'Col ' + ($$('.group').length + 1));
    const stub = document.createElement('div');
    stub.className = 'stub button';

    @@ -678,12 +771,12 @@
    presetVersion: '1',
    type: 'ColorPalette',
    data: {
    description: 'Generated',
    description: $('#palette > .add').getAttribute('description') || 'Built with palette creator',
    // populate columns
    groups: $$('.group').map((c, columnIndex) => {
    // convert colors to linear rgb from whatever format is thrown in
    return {
    name: 'Col ' + (columnIndex + 1),
    name: c.getAttribute('name') || ('Col ' + (columnIndex + 1)),
    colors: $$('.color', c).map(e => {
    // a technique so cursed you will shit the bed (pulling rgb from hex)
    let [sR, sG, sB] = e.style.backgroundColor.match(/[\d\.]+/g).map(Number);
    @@ -708,7 +801,7 @@
    function initPalette(data) {
    if (!data) {
    data = {
    description: 'New Palette',
    description: 'Built with palette creator',
    groups: [],
    }
    }
    @@ -756,10 +849,14 @@
    }
    };

    palette.appendChild(addBtn());
    const btn = addBtn();
    btn.setAttribute('description', data.description || 'Generated palette')
    palette.appendChild(btn);

    let i = 0;
    for (const group of data.groups) {
    createGroup(group.colors, true);
    const el = createGroup(group.colors, true);
    el.setAttribute('name', group.name || ('Col' + ++i))
    }
    }

    @@ -802,6 +899,9 @@ <h2>Keybinds</h2>
    <tr><td>Ctrl+Shift+S</td><td>Download preset</td></tr>
    <tr><td>Enter, Shift+E</td><td>Next group, new if last group/no other groups</td></tr>
    <tr><td>Space</td><td>Unselect selected</td></tr>
    <tr><td>R</td><td>Rename selected group</td></tr>
    <tr><td>Shift+R</td><td>Change palette description</td></tr>
    <tr><td>C</td><td>Show color picker for selected color</td></tr>
    <tr><td>W/S</td><td>Up/down in group</td></tr>
    <tr><td>A/D</td><td>Left/Right among groups</td></tr>
    <tr><td>Shift+W/S</td><td>Swap color up/down in group</td></tr>
    @@ -819,4 +919,5 @@ <h2>Example images (Click to demo)</h2>
    </div>
    </div>
    <h1 style="margin-left: 14px;">Palette</h1>
    <input type="color" id="colorPicker" value="#fffff">
    <div id="palette"></div>
  5. Meshiest revised this gist Jan 3, 2021. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -462,6 +462,7 @@
    // select the previous group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    if (numGroups < 2) return;
    modFn(getAt((col + numGroups - 1) % numGroups, row));

    } else if (e.code === 'KeyD' && !e.ctrlKey) {
    @@ -505,6 +506,7 @@
    // select the next group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    if (numGroups < 2) return;
    const el = getAt((col + 1) % numGroups, row);
    console.log(el, (col + 1) % numGroups, row);

  6. Meshiest revised this gist Jan 3, 2021. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -470,6 +470,7 @@
    // select the next group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    if (numGroups < 2) return;
    modFn(getAt((col + 1) % numGroups, row));

    // delete everything
    @@ -481,6 +482,7 @@
    // select the previous group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    if (numGroups < 2) return;
    const el = getAt((col + numGroups - 1) % numGroups, row);

    // ignore group movements
  7. Meshiest revised this gist Jan 3, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -785,6 +785,7 @@ <h2>Usage</h2>
    <li>Selected groups/colors can be re-arranged with the below keybinds.</group.>
    <li>Paste an image from clipboard to upload to the color selector.</li>
    <li>Paste a preset from clipboard to replace the current preset.</li>
    <li>Paste a blockland colorset from clipboard to replace the current preset.</li>
    <li>Brickadia will not load any palettes larger than 16x16.</li>
    <li>Click <a href="#noinfo">here</a> to hide info.</li>
    </ul>
  8. Meshiest revised this gist Jan 3, 2021. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -342,7 +342,6 @@
    name: div.match(/DIV: (.*)/)[1],
    })),
    }
    console.log(data);
    initPalette(data);
    snapshot();
    return;
  9. Meshiest revised this gist Jan 3, 2021. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -344,6 +344,7 @@
    }
    console.log(data);
    initPalette(data);
    snapshot();
    return;
    }

    @@ -353,8 +354,10 @@
    const data = JSON.parse(pasteData);
    if (data.formatVersion === '1' &&
    data.presetVersion === '1' &&
    data.type === 'ColorPalette')
    return initPalette(data.data);
    data.type === 'ColorPalette') {
    initPalette(data.data);
    snapshot();
    }
    } catch (e) {
    console.error('error pasting json', e);
    }
  10. Meshiest revised this gist Jan 3, 2021. 1 changed file with 27 additions and 1 deletion.
    28 changes: 27 additions & 1 deletion paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -321,14 +321,40 @@
    } else {
    const pasteData = e.clipboardData.getData('Text');

    const blocklandRegex = /(((\d{1,3} \d{1,3} \d{1,3} \d{1,3}|\d\.\d{3} \d\.\d{3} \d\.\d{3} \d\.\d{3})\r?\n)+DIV:.*)/g;
    const blMatches = pasteData.match(blocklandRegex);
    if (blMatches) {
    const data = {
    description: 'Imported from blockland colorset',
    groups: blMatches.map(div => ({
    colors: div
    .match(/(\d{1,3} \d{1,3} \d{1,3} \d{1,3}|\d\.\d{3} \d\.\d{3} \d\.\d{3} \d\.\d{3})/g)
    .map(c => {
    let [sR, sG, sB] = c.split(' ').map(Number);
    if (c.match(/\d\.\d{3} \d\.\d{3} \d\.\d{3} \d\.\d{3}/)) {
    sR *= 255;
    sG *= 255;
    sB *= 255;
    }
    const [r, g, b] = linearRGB([sR, sG, sB]);
    return { r, g, b, a: 255 };
    }),
    name: div.match(/DIV: (.*)/)[1],
    })),
    }
    console.log(data);
    initPalette(data);
    return;
    }

    // try to parse the pasted data
    if (pasteData && pasteData.length > 0) {
    try {
    const data = JSON.parse(pasteData);
    if (data.formatVersion === '1' &&
    data.presetVersion === '1' &&
    data.type === 'ColorPalette')
    initPalette(data.data);
    return initPalette(data.data);
    } catch (e) {
    console.error('error pasting json', e);
    }
  11. Meshiest revised this gist Jan 3, 2021. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -35,6 +35,9 @@
    pointer-events: none;
    }

    #noinfo:target {
    display: none;
    }

    .eyedrop-container {
    width: 600px;
    @@ -743,7 +746,7 @@
    <div id="dropper"></div>
    </div>
    </div>
    <div class="info">
    <div class="info" id="noinfo">
    <a id="download" style="display: none"></a>
    <h1>Info</h1>
    <h2>Usage</h2>
    @@ -755,6 +758,7 @@ <h2>Usage</h2>
    <li>Paste an image from clipboard to upload to the color selector.</li>
    <li>Paste a preset from clipboard to replace the current preset.</li>
    <li>Brickadia will not load any palettes larger than 16x16.</li>
    <li>Click <a href="#noinfo">here</a> to hide info.</li>
    </ul>
    <h2>Keybinds</h2>
    <table class="keybinds">
  12. Meshiest revised this gist Jan 3, 2021. 1 changed file with 15 additions and 0 deletions.
    15 changes: 15 additions & 0 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -19,6 +19,12 @@

    #dropper {
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 30px;
    text-align: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    @@ -170,6 +176,7 @@

    const history = [];
    let undoStack = [];
    let used = [];

    // add a palette snapshot to history
    // if you read this code and want to kill me, I understand
    @@ -223,6 +230,12 @@
    dropper.style.transform = 'translate(-50%, -100%)';
    }
    dropper.style.background = `rgb(${pixels[off]}, ${pixels[off+1]}, ${pixels[off+2]})`;
    const hex = [pixels[off], pixels[off+1], pixels[off+2]].map(i =>
    i.toString(16).padStart(2, '0')).join('');
    if (used.includes(hex))
    dropper.innerHTML = '&check;';
    else
    dropper.innerHTML = '';
    };

    // add the color on click
    @@ -623,6 +636,7 @@

    // save the palette to data
    function save() {
    used = [];
    const data = {
    // generate the preset file
    formatVersion: '1',
    @@ -642,6 +656,7 @@
    // convert to hex
    const hex = [sR, sG, sB].map(i =>
    i.toString(16).padStart(2, '0')).join('');
    used.push(hex);

    // convert sRGB to linear rgb
    const [r, g, b] = linearRGB([sR, sG, sB]);
  13. Meshiest revised this gist Jan 3, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -370,7 +370,7 @@
    $('#download').download = 'Generated' + '.bp';
    $('#download').click();

    } else if (e.code === 'KeyS' && e.ctrlKey) {
    } else if ((e.code === 'KeyS' || e.code === 'KeyW') && e.ctrlKey) {
    e.preventDefault(); // prevent annoying save popup
    } else if (e.code === 'KeyW') {

  14. Meshiest revised this gist Jan 3, 2021. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -370,6 +370,8 @@
    $('#download').download = 'Generated' + '.bp';
    $('#download').click();

    } else if (e.code === 'KeyS' && e.ctrlKey) {
    e.preventDefault(); // prevent annoying save popup
    } else if (e.code === 'KeyW') {

    // if nothing is selected, select the first group
  15. Meshiest revised this gist Jan 3, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -364,7 +364,7 @@
    save();

    // save on ctrl s
    } else if (e.code === 'KeyS' && e.ctrlKey) {
    } else if (e.code === 'KeyS' && e.ctrlKey && e.shiftKey) {
    e.preventDefault();
    $('#download').href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(save(), 0, 2));
    $('#download').download = 'Generated' + '.bp';
    @@ -745,7 +745,7 @@ <h2>Keybinds</h2>
    <tbody>
    <tr><td>Ctrl+Z</td><td>Undo</td></tr>
    <tr><td>Ctrl+Shift+Z</td><td>Redo</td></tr>
    <tr><td>Ctrl+S</td><td>Download preset</td></tr>
    <tr><td>Ctrl+Shift+S</td><td>Download preset</td></tr>
    <tr><td>Enter, Shift+E</td><td>Next group, new if last group/no other groups</td></tr>
    <tr><td>Space</td><td>Unselect selected</td></tr>
    <tr><td>W/S</td><td>Up/down in group</td></tr>
  16. Meshiest revised this gist Jan 3, 2021. 1 changed file with 55 additions and 9 deletions.
    64 changes: 55 additions & 9 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -324,7 +324,7 @@
    const getAt = (col, row=-1) => {
    const group = $$('.group')[col];
    const colors = $$('.color', group);
    return row > -1 ?
    return row > -1 && colors.length > row ?
    colors[Math.min(row, colors.length - 1)]
    : row == -2
    ? colors[colors.length - 1]
    @@ -409,7 +409,7 @@
    }
    }

    } else if (e.code === 'KeyA') {
    } else if (e.code === 'KeyA' && !e.ctrlKey) {
    // if nothing is selected, select the first group
    if (!selected) return modFn(getAt(0));

    @@ -418,12 +418,61 @@
    const numGroups = $$('.group').length;
    modFn(getAt((col + numGroups - 1) % numGroups, row));

    } else if (e.code === 'KeyD') {
    } else if (e.code === 'KeyD' && !e.ctrlKey) {
    if (!selected) return modFn(getAt(0));

    // select the next group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    modFn(getAt((col + 1) % numGroups, row));

    // delete everything
    } else if (e.code === 'KeyA' && e.ctrlKey) {
    e.preventDefault();
    // if nothing is selected, ignore
    if (!selected) return;

    // select the previous group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    const el = getAt((col + numGroups - 1) % numGroups, row);

    // ignore group movements
    if (selected.classList.contains('group')) return;

    // if the dest is a group, put the color in there
    if (el.classList.contains('group')) {
    el.appendChild(selected);
    } else {
    // move selected to the element
    el.before(selected);
    }

    snapshot();

    } else if (e.code === 'KeyD' && e.ctrlKey) {
    e.preventDefault();
    if (!selected) return;

    // select the next group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    const el = getAt((col + 1) % numGroups, row);
    console.log(el, (col + 1) % numGroups, row);

    // ignore group movements
    if (selected.classList.contains('group')) return;

    // if the dest is a group, put the color in there
    if (el.classList.contains('group')) {
    el.appendChild(selected);
    } else {
    // move selected to the element
    el.before(selected);
    }

    snapshot();

    // delete everything
    } else if (e.code === 'Delete' && e.shiftKey && e.ctrlKey) {
    e.preventDefault();
    @@ -612,11 +661,8 @@
    }
    }

    console.log('a');
    const palette = $('#palette');
    console.log('b');
    palette.innerHTML = '';
    console.log('c');

    // handle all click events through the parent due to the nature of the undo/redo
    $('#palette').onclick = e => {
    @@ -659,7 +705,6 @@
    };

    palette.appendChild(addBtn());
    console.log(palette);

    for (const group of data.groups) {
    createGroup(group.colors, true);
    @@ -705,8 +750,9 @@ <h2>Keybinds</h2>
    <tr><td>Space</td><td>Unselect selected</td></tr>
    <tr><td>W/S</td><td>Up/down in group</td></tr>
    <tr><td>A/D</td><td>Left/Right among groups</td></tr>
    <tr><td>Shift+W/S</td><td>Move color up/down in group</td></tr>
    <tr><td>Shift+A/D</td><td>Move color to group left/right or move group</td></tr>
    <tr><td>Shift+W/S</td><td>Swap color up/down in group</td></tr>
    <tr><td>Shift+A/D</td><td>Swap color to group left/right or move group</td></tr>
    <tr><td>Ctrl+A/D</td><td>Move color to group left/right</td></tr>
    <tr><td>Delete, Shift+X</td><td>Remove color, will remove group if group is empty</td></tr>
    <tr><td>Ctrl+Shift+Delete</td><td>Reset palette</td></tr>
    </tbody>
  17. Meshiest revised this gist Jan 3, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -674,7 +674,7 @@
    <ol>
    <li>Paste an image from your clipboard.</li>
    <li>Hover over the colors to preview.</li>
    <li>Click or press a number on your keyboard to add to palette.</li>
    <li>Click on color to add to palette.</li>
    </ol>
    </div>
    <canvas id="selector"></canvas>
  18. Meshiest revised this gist Jan 3, 2021. 1 changed file with 9 additions and 2 deletions.
    11 changes: 9 additions & 2 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -424,8 +424,14 @@
    const numGroups = $$('.group').length;
    modFn(getAt((col + 1) % numGroups, row));

    // deselect everything
    } else if (e.code === 'Delete' || e.code === 'KeyX' && e.shiftKey) {
    // delete everything
    } else if (e.code === 'Delete' && e.shiftKey && e.ctrlKey) {
    e.preventDefault();
    initPalette();
    snapshot();

    // delete selected thing
    } else if (e.code === 'Delete' && !e.shiftKey && !e.ctrlKey || e.code === 'KeyX' && e.shiftKey) {
    if (!selected) return;

    // delete a group, select previous group
    @@ -702,6 +708,7 @@ <h2>Keybinds</h2>
    <tr><td>Shift+W/S</td><td>Move color up/down in group</td></tr>
    <tr><td>Shift+A/D</td><td>Move color to group left/right or move group</td></tr>
    <tr><td>Delete, Shift+X</td><td>Remove color, will remove group if group is empty</td></tr>
    <tr><td>Ctrl+Shift+Delete</td><td>Reset palette</td></tr>
    </tbody>
    </table>
    <h2>Example images (Click to demo)</h2>
  19. Meshiest revised this gist Jan 3, 2021. 1 changed file with 11 additions and 8 deletions.
    19 changes: 11 additions & 8 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -260,6 +260,8 @@
    try {
    if (localStorage.temp) {
    initPalette(JSON.parse(localStorage.temp));
    } else {
    initPalette();
    }
    } catch (e) {
    console.warn('error parsing json', e);
    @@ -423,7 +425,7 @@
    modFn(getAt((col + 1) % numGroups, row));

    // deselect everything
    } else if (e.code === 'Delete') {
    } else if (e.code === 'Delete' || e.code === 'KeyX' && e.shiftKey) {
    if (!selected) return;

    // delete a group, select previous group
    @@ -457,7 +459,7 @@
    $$('.selected').forEach(e => e.classList.remove('selected'));

    // next group/new group
    } else if (e.key === 'Enter') {
    } else if (e.key === 'Enter' || e.code === 'KeyE' && e.shiftKey) {
    // nothing is selected - create a new group
    if (!selected) {
    createGroup([]);
    @@ -604,8 +606,11 @@
    }
    }

    console.log('a');
    const palette = $('#palette');
    console.log('b');
    palette.innerHTML = '';
    console.log('c');

    // handle all click events through the parent due to the nature of the undo/redo
    $('#palette').onclick = e => {
    @@ -648,11 +653,11 @@
    };

    palette.appendChild(addBtn());
    console.log(palette);

    for (const group of data.groups) {
    createGroup(group.colors, true);
    }
    snapshot();
    }

    </script>
    @@ -690,13 +695,13 @@ <h2>Keybinds</h2>
    <tr><td>Ctrl+Z</td><td>Undo</td></tr>
    <tr><td>Ctrl+Shift+Z</td><td>Redo</td></tr>
    <tr><td>Ctrl+S</td><td>Download preset</td></tr>
    <tr><td>Enter</td><td>Next group, new if last group/no other groups</td></tr>
    <tr><td>Enter, Shift+E</td><td>Next group, new if last group/no other groups</td></tr>
    <tr><td>Space</td><td>Unselect selected</td></tr>
    <tr><td>W/S</td><td>Up/down in group</td></tr>
    <tr><td>A/D</td><td>Left/Right among groups</td></tr>
    <tr><td>Shift+W/S</td><td>Move color up/down in group</td></tr>
    <tr><td>Shift+A/D</td><td>Move color to group left/right or move group</td></tr>
    <tr><td>Delete</td><td>Remove color, will remove group if group is empty</td></tr>
    <tr><td>Delete, Shift+X</td><td>Remove color, will remove group if group is empty</td></tr>
    </tbody>
    </table>
    <h2>Example images (Click to demo)</h2>
    @@ -706,7 +711,5 @@ <h2>Example images (Click to demo)</h2>
    </div>
    </div>
    </div>
    <style id="dynamic"></style>
    <h1 style="margin-left: 14px;">Palette</h1>
    <div id="palette">
    </div>
    <div id="palette"></div>
  20. Meshiest revised this gist Jan 3, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -681,6 +681,7 @@ <h2>Usage</h2>
    <li>Selected groups/colors can be re-arranged with the below keybinds.</group.>
    <li>Paste an image from clipboard to upload to the color selector.</li>
    <li>Paste a preset from clipboard to replace the current preset.</li>
    <li>Brickadia will not load any palettes larger than 16x16.</li>
    </ul>
    <h2>Keybinds</h2>
    <table class="keybinds">
  21. Meshiest created this gist Jan 3, 2021.
    711 changes: 711 additions & 0 deletions paletteCreator.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,711 @@
    <style>
    body, html {
    margin: 0;
    background-color: black;
    color: white;
    font-family: monospace;
    }

    body {
    margin: 14px;
    }

    #selector {
    }

    h1, h2, h3 {
    margin: 8px 0;
    }

    #dropper {
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
    width: 40px;
    height: 40px;
    border-radius: 50%;
    position: absolute;
    top: -100px;
    left: 0;
    transform: translate(-50%, -100%);
    pointer-events: none;
    }


    .eyedrop-container {
    width: 600px;
    height: 600px;
    resize: both;
    overflow: scroll;
    margin: 14px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
    background: repeating-conic-gradient(#f5f5f5 0% 25%, white 0% 50%)
    50% / 20px 20px;
    }

    .eyedrop {
    position: relative;
    }

    .instructions {
    color: #444;
    font-weight: bold;
    position: absolute;
    top: 14px;
    left: 14px;
    background-color: white;
    box-shadow: 0 0 30px 10px white;
    }

    .picker {
    display: flex;
    }

    .info {
    display: flex;
    flex-direction: column;
    }

    .images {
    display: flex;
    flex-flow: row wrap;
    }

    .images img {
    cursor: pointer;
    width: 100px;
    height: 100px;
    object-fit: cover;
    }

    .button {
    background-color: #557;
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-size: 20px;
    font-weight: bold;
    height: 32px;
    user-select: none;
    cursor: pointer;
    }

    #palette {
    margin: 14px;
    background-color: #224;
    display: flex;
    padding: 2px;
    align-items: flex-start;
    }

    .group {
    min-width: 32px;
    padding: 2px;
    border: 2px solid transparent;
    margin: 4px;
    order: 0;
    display: flex;
    flex-direction: column;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
    background-color: #335;
    }

    .group .stub {
    background-color: #112;
    width: 40px;
    height: 24px;
    order: -10;
    display: flex;
    margin: -4px;
    margin-bottom: 4px;
    }


    .group.selected {
    border: 2px solid #aad;
    }

    .group.selected .stub {
    margin: -2px;
    width: 36px;
    height: 22px;
    margin-bottom: 4px;
    }

    #palette > .add {
    margin: 4px;
    }

    .color {
    order: 0;
    width: 32px;
    height: 32px;
    margin-bottom: 4px;
    cursor: pointer;
    }

    .color.selected {
    width: 28px;
    height: 28px;
    border: 2px solid #aad;
    border-radius: 50%;
    }

    .button:hover { background-color: #779; }
    .button:active { background-color: #99b; }

    .add {
    width: 32px;
    order: 10;
    }

    </style>
    <script>

    // poor man's jquery
    const $ = document.querySelector.bind(document);
    const $$ = (q, el) => Array.from((el || document).querySelectorAll(q));

    let pixels, dropWidth, dropHeight;

    const history = [];
    let undoStack = [];

    // add a palette snapshot to history
    // if you read this code and want to kill me, I understand
    function snapshot() {
    history.push($('#palette').innerHTML);
    save();
    // remove entries over 1000
    history.splice(1000);
    undoStack = [];
    }

    // undo an undo
    function redo() {
    if (!undoStack.length) return;
    const state = undoStack.pop();
    history.push(state);
    $('#palette').innerHTML = state;
    }

    // undo some history
    function undo() {
    if (history.length < 2) return;
    const state = history.pop();
    undoStack.push(state);
    $('#palette').innerHTML = history[history.length - 1];
    }

    window.onload = () => {
    // hide the dropper on mouse leave
    $('#selector').onmouseleave = e => {
    const dropper = $('#dropper');
    dropper.style.display = 'none';
    };

    // set the eyedrop color on hover
    $('#selector').onmousemove = e => {
    if (!pixels) return;
    const dropper = $('#dropper');
    const { layerX: x, layerY: y } = e;
    const off = (dropWidth * y + x) * 4;
    if ((pixels[off+3]) === 0) {
    dropper.style.display = 'none';
    return;
    }
    dropper.style.display = 'block';
    dropper.style.left = x + 'px';
    dropper.style.top = y + 'px';
    if (y < 40) {
    dropper.style.transform = 'translate(-50%, 0)';
    } else {
    dropper.style.transform = 'translate(-50%, -100%)';
    }
    dropper.style.background = `rgb(${pixels[off]}, ${pixels[off+1]}, ${pixels[off+2]})`;
    };

    // add the color on click
    $('#selector').onclick = e => {
    if (!pixels) return;
    const { layerX: x, layerY: y } = e;
    const off = (dropWidth * y + x) * 4;
    const selected = $('.selected');
    const hex = [pixels[off], pixels[off+1], pixels[off+2]].map(i =>
    i.toString(16).padStart(2, '0')).join('');

    if ((pixels[off+3]) === 0) return;

    if (!selected) {
    // create a new group if there is nothing currently selected
    const group = createGroup([]);
    group.appendChild(createColor(hex, true));
    snapshot();

    } else if (selected.classList.contains('group')) {
    // add a new color to the group if a group is selected
    selected.appendChild(createColor(hex, true));
    snapshot();

    } else if (selected.classList.contains('color') && selected.getAttribute('hex') !== hex) {
    // update a color if a color is selected
    selected.style.background = '#' + hex;
    selected.setAttribute('hex', hex);
    snapshot();
    }
    };

    $$('.images img').forEach(el => el.onclick = () => renderEyedropImage(el));

    try {
    if (localStorage.temp) {
    initPalette(JSON.parse(localStorage.temp));
    }
    } catch (e) {
    console.warn('error parsing json', e);
    initPalette();
    }
    snapshot();
    };

    // swap to elements in dom
    function swapDom(a, b) {
    const aParent = a.parentNode;
    const bParent = b.parentNode;

    const aHolder = document.createElement('div');
    const bHolder = document.createElement('div');

    aParent.replaceChild(aHolder, a);
    bParent.replaceChild(bHolder, b);

    aParent.replaceChild(b, aHolder);
    bParent.replaceChild(a, bHolder);
    }

    // when you paste, render the image on the canvas
    document.onpaste = e => {
    const items = Array.from(e.clipboardData.items)
    .filter(i => i.type.indexOf('image') === 0);

    if (items.length > 0) {
    const reader = new FileReader();
    reader.onload = function(event) {
    const img = new Image();
    img.src = event.target.result;
    img.onload = res => {
    if (img.width > 0 && img.height > 0) {
    renderEyedropImage(img);
    }
    }
    };
    reader.readAsDataURL(items[0].getAsFile());
    } else {
    const pasteData = e.clipboardData.getData('Text');

    // try to parse the pasted data
    if (pasteData && pasteData.length > 0) {
    try {
    const data = JSON.parse(pasteData);
    if (data.formatVersion === '1' &&
    data.presetVersion === '1' &&
    data.type === 'ColorPalette')
    initPalette(data.data);
    } catch (e) {
    console.error('error pasting json', e);
    }
    }
    }
    };

    // get the color or group given a column and a row
    const getAt = (col, row=-1) => {
    const group = $$('.group')[col];
    const colors = $$('.color', group);
    return row > -1 ?
    colors[Math.min(row, colors.length - 1)]
    : row == -2
    ? colors[colors.length - 1]
    : group;
    };

    // get the position of a color/group
    const getIndex = el => {
    const groups = $$('.group');
    // if the selected thing is a group, return -1 for the row
    if (el.classList.contains('group')) return [groups.findIndex(e => e == el), -1];
    // otherwise return the group and index
    return [groups.findIndex(e => e == el.parentNode), $$('.color', el.parentNode).findIndex(e => e == el)];
    };

    document.onkeydown = e => {
    const selected = $('.selected');

    // if shift key is pressed, use swap instead of select
    const modFn = e.shiftKey ? el => {
    // if the swap would be illegal, do a select instead
    if (el.classList.contains('group') && selected.classList.contains('color') ||
    selected.classList.contains('group') && el.classList.contains('color'))
    return select(el);

    // swap the dom and save to history
    swapDom(selected, el);
    snapshot();
    } : select;

    // undo and redo on CTRL + Z
    if (e.code === 'KeyZ' && e.ctrlKey) {
    if (e.shiftKey)
    redo()
    else
    undo();
    save();

    // save on ctrl s
    } else if (e.code === 'KeyS' && e.ctrlKey) {
    e.preventDefault();
    $('#download').href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(save(), 0, 2));
    $('#download').download = 'Generated' + '.bp';
    $('#download').click();

    } else if (e.code === 'KeyW') {

    // if nothing is selected, select the first group
    if (!selected) return modFn(getAt(0));

    const [col, row] = getIndex(selected);
    const lastColor = getAt(col, -2);
    // if the group is selected, select the last color
    if ((row === -1 || row === 0 && e.shiftKey) && lastColor) {
    modFn(lastColor);
    // select the group if it's the first color
    } else if (row === 0) {
    modFn(getAt(col));
    // otherwise select the previous color
    } else {
    modFn(getAt(col, row-1));
    }
    } else if (e.code === 'KeyS') {
    const selected = $('.selected');
    // if nothing is selected, select the first group
    if (!selected) return modFn(getAt(0));

    const [col, row] = getIndex(selected);
    const firstColor = getAt(col, 0);
    // if the group is selected, select the last color
    if (row === -1 && firstColor) {
    modFn(firstColor);

    // otherwise select the previous color
    } else {
    // if there's no more colors in this group, select the group
    if (row + 1 >= $$('.color', getAt(col)).length) {
    modFn(getAt(col, e.shiftKey ? 0 : -1));
    } else {
    // otherwise select the next color
    modFn(getAt(col, row+1));
    }
    }

    } else if (e.code === 'KeyA') {
    // if nothing is selected, select the first group
    if (!selected) return modFn(getAt(0));

    // select the previous group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    modFn(getAt((col + numGroups - 1) % numGroups, row));

    } else if (e.code === 'KeyD') {
    // select the next group + wrap around
    const [col, row] = getIndex(selected);
    const numGroups = $$('.group').length;
    modFn(getAt((col + 1) % numGroups, row));

    // deselect everything
    } else if (e.code === 'Delete') {
    if (!selected) return;

    // delete a group, select previous group
    if (selected.classList.contains('group')) {
    if (selected.previousSibling && selected.previousSibling.classList.contains('group'))
    // select the previous group
    select(selected.previousSibling);
    else {
    // select the first group
    const groups = $$('.group').filter(g => g !== selected);
    if (groups.length > 0)
    select(groups[0]);
    }

    // delete a color, select previous color
    } else if (selected.classList.contains('color')) {
    // if the previous sibling is a color, select it
    if (selected.previousSibling.classList.contains('color'))
    select(selected.previousSibling);
    else if (selected.nextSibling)
    select(selected.nextSibling);
    else
    // select the group instead
    select(selected.parentNode);
    }
    selected.remove();
    snapshot();

    // delete whatever is selected
    } else if (e.code === 'Space') {
    $$('.selected').forEach(e => e.classList.remove('selected'));

    // next group/new group
    } else if (e.key === 'Enter') {
    // nothing is selected - create a new group
    if (!selected) {
    createGroup([]);
    snapshot();

    // a group is selected
    } else if (selected.classList.contains('group')) {
    // if there is a next group, select it
    if (selected.nextSibling) {
    select(selected.nextSibling);
    } else {
    // otherwise create a new group
    createGroup([]);
    snapshot();
    }

    }
    } else {
    // debug key stuff
    // console.log(e.key, e.code, e);
    }
    };

    // update the color selecting canvas with an image
    function renderEyedropImage(image) {
    // create the element
    const canvas = $('#selector');
    const ctx = canvas.getContext('2d');

    // set the canvas size
    canvas.style.width = (dropWidth = canvas.width = ctx.canvas.width = image.naturalWidth) + 'px';
    canvas.style.height = (dropHeight = canvas.height = ctx.canvas.height = image.naturalHeight) + 'px';

    // draw the image
    ctx.drawImage(image, 0, 0);
    const imageData = ctx.getImageData(0, 0, image.naturalWidth, image.naturalHeight);
    pixels = imageData.data;

    $('.instructions').style.display = 'none';
    }

    function addBtn() {
    const elem = document.createElement('div');
    elem.className = 'add button';
    elem.innerText = '+';
    return elem;
    }

    function select(elem) {
    if (!elem) return;
    $$('.selected').forEach(e => e.classList.remove('selected'));
    elem.classList.add('selected');
    }

    // create a color
    function createColor(hex, ignoreSelect) {
    const colorElem = document.createElement('div');
    colorElem.className = 'color';
    colorElem.setAttribute('hex', hex);
    colorElem.style.backgroundColor = '#' + hex;
    if (!ignoreSelect)
    select(colorElem);
    return colorElem;
    }

    // create a group
    function createGroup(colors, ignoreSelect) {
    const palette = $('#palette');
    const group = document.createElement('div');
    group.className = 'group';
    const stub = document.createElement('div');
    stub.className = 'stub button';

    group.appendChild(stub);
    group.appendChild(addBtn());
    palette.appendChild(group);
    if (!ignoreSelect)
    select(group);

    for (const {r: lR, g: lG, b: lB} of colors) {
    const [r, g, b] = sRGB([lR, lG, lB]);
    group.appendChild(createColor([r, g, b].map(i =>
    i.toString(16).padStart(2, '0')).join(''), true));
    }

    return group;
    }

    // convert srgb to linear rgb
    const linearRGB = rgba =>
    rgba.map((c, i) => i === 3
    ? c
    : Math.round(((c/255) > 0.04045 ? Math.pow((c/255) * (1.0 / 1.055) + 0.0521327, 2.4 ) : (c/255) * (1.0 / 12.92))*255)
    );

    // convert linear rgb to srgb
    const sRGB = linear =>
    linear.map((c, i) => i === 3
    ? c
    : Math.round(((c/255) > 0.0031308
    ? 1.055 * Math.pow((c/255), 1/2.4) - 0.055
    : c / 255 * 12.92)*255)
    );

    // save the palette to data
    function save() {
    const data = {
    // generate the preset file
    formatVersion: '1',
    presetVersion: '1',
    type: 'ColorPalette',
    data: {
    description: 'Generated',
    // populate columns
    groups: $$('.group').map((c, columnIndex) => {
    // convert colors to linear rgb from whatever format is thrown in
    return {
    name: 'Col ' + (columnIndex + 1),
    colors: $$('.color', c).map(e => {
    // a technique so cursed you will shit the bed (pulling rgb from hex)
    let [sR, sG, sB] = e.style.backgroundColor.match(/[\d\.]+/g).map(Number);

    // convert to hex
    const hex = [sR, sG, sB].map(i =>
    i.toString(16).padStart(2, '0')).join('');

    // convert sRGB to linear rgb
    const [r, g, b] = linearRGB([sR, sG, sB]);
    return {r, g, b, a: 255};
    })
    };
    }),
    },
    };
    localStorage.temp = JSON.stringify(data.data);
    return data;
    }

    function initPalette(data) {
    if (!data) {
    data = {
    description: 'New Palette',
    groups: [],
    }
    }

    const palette = $('#palette');
    palette.innerHTML = '';

    // handle all click events through the parent due to the nature of the undo/redo
    $('#palette').onclick = e => {
    const elem = e.target;

    // toggle color select
    if (elem.classList.contains('color')) {
    if (elem.classList.contains('selected'))
    elem.classList.remove('selected');
    else
    select(elem);
    }
    // detect clicks on buttons
    if (elem.classList.contains('button')) {
    // button is in the group
    if (elem.parentNode.classList.contains('group')) {
    const group = elem.parentNode;
    // group add button
    if (elem.classList.contains('add')) {
    group.appendChild(createColor('ffffff'));
    snapshot();
    }

    // group stub button (toggle select)
    if (elem.classList.contains('stub')) {
    if (group.classList.contains('selected'))
    group.classList.remove('selected');
    else
    select(group);
    }
    } else {

    // palette add button
    if (elem.classList.contains('add')) {
    createGroup([]);
    snapshot();
    }
    }
    }
    };

    palette.appendChild(addBtn());

    for (const group of data.groups) {
    createGroup(group.colors, true);
    }
    snapshot();
    }

    </script>
    <div class="picker">
    <div class="eyedrop-container">
    <div class="eyedrop">
    <div class="instructions">
    <ol>
    <li>Paste an image from your clipboard.</li>
    <li>Hover over the colors to preview.</li>
    <li>Click or press a number on your keyboard to add to palette.</li>
    </ol>
    </div>
    <canvas id="selector"></canvas>
    <div id="dropper"></div>
    </div>
    </div>
    <div class="info">
    <a id="download" style="display: none"></a>
    <h1>Info</h1>
    <h2>Usage</h2>
    <ul>
    <li>Click + buttons to add groups/colors to the palette.</li>
    <li>Click the group stub or a color to select a color.</li>
    <li>Clicking on the color selector image will modify the selected color or add a new color to the group.</li>
    <li>Selected groups/colors can be re-arranged with the below keybinds.</group.>
    <li>Paste an image from clipboard to upload to the color selector.</li>
    <li>Paste a preset from clipboard to replace the current preset.</li>
    </ul>
    <h2>Keybinds</h2>
    <table class="keybinds">
    <thead><tr><th>Key</th><th>Description</th></tr></thead>
    <tbody>
    <tr><td>Ctrl+Z</td><td>Undo</td></tr>
    <tr><td>Ctrl+Shift+Z</td><td>Redo</td></tr>
    <tr><td>Ctrl+S</td><td>Download preset</td></tr>
    <tr><td>Enter</td><td>Next group, new if last group/no other groups</td></tr>
    <tr><td>Space</td><td>Unselect selected</td></tr>
    <tr><td>W/S</td><td>Up/down in group</td></tr>
    <tr><td>A/D</td><td>Left/Right among groups</td></tr>
    <tr><td>Shift+W/S</td><td>Move color up/down in group</td></tr>
    <tr><td>Shift+A/D</td><td>Move color to group left/right or move group</td></tr>
    <tr><td>Delete</td><td>Remove color, will remove group if group is empty</td></tr>
    </tbody>
    </table>
    <h2>Example images (Click to demo)</h2>
    <div class="images">
    <img src="https://i.imgur.com/qzF7dNY.png" crossorigin="anonymous">
    <img src="https://i.imgur.com/wdUUC5I.png" crossorigin="anonymous">
    </div>
    </div>
    </div>
    <style id="dynamic"></style>
    <h1 style="margin-left: 14px;">Palette</h1>
    <div id="palette">
    </div>