better modals, keyboard input in modals, new keyboard shortcuts

This commit is contained in:
Hugh Bord 2021-08-05 15:30:53 +10:00
parent 014ad5602b
commit d2f38f822c
9 changed files with 456 additions and 313 deletions

View File

@ -49,9 +49,21 @@
# Keyboard Shortcuts
* Ctrl D - shows/hides debug panel
* Ctrl Z - Undo
* Ctrl Y - Redo
## ASCII Editing
* Ctrl + D - shows/hides debug panel
* Ctrl + Z - Undo
* Ctrl + Y - Redo
* Ctrl + M - New ASCII (lol @ cannot override ctrl n ?!)
* Ctrl + E - Edit ASCII
* Ctrl + P - Paste ASCII from Clipboard
* Ctrl + Shift + C - Save to clipboard
* Ctrl + Shift + F - Save to txt file
## Brush and Toolbar
* Ctrl + ] - Increase brush size
* Ctrl + [ - Decrease brush size
# FEATURES DONE

View File

@ -4,10 +4,7 @@
<EditAscii />
<PasteAscii />
<context-menu
:display="showContextMenu"
ref="menu"
>
<context-menu :display="showContextMenu" ref="menu">
<ul>
<li
@click="$store.commit('openModal', 'new-ascii')"
@ -16,34 +13,21 @@
>
New ASCII
</li>
<li @click="clearCache()" class="ml-1">Clear and Refresh</li>
<li @click="startImport('mirc')" class="ml-1">Import mIRC</li>
<li
@click="clearCache()"
class="ml-1"
>
Clear and Refresh
</li>
<li
@click="startImport('mirc')"
class="ml-1"
>
Import mIRC
</li>
<li
@click="exportMirc('file')"
@click="startExport('file')"
class="ml-1"
v-if="asciibirdMeta.length"
>
Export mIRC to File
</li>
<li
class="ml-1"
@click="$store.commit('openModal', 'paste-modal')"
>
<li class="ml-1" @click="$store.commit('openModal', 'paste-ascii')">
Import mIRC from Clipboard
</li>
<li
class="ml-1"
@click="exportMirc('clipboard')"
@click="startExport('clipboard')"
v-if="asciibirdMeta.length"
>
Export mIRC to Clipboard
@ -55,12 +39,7 @@
>
Save Asciibird State
</li>
<li
@click="startImport('asb')"
class="ml-1"
>
Load Asciibird State
</li>
<li @click="startImport('asb')" class="ml-1">Load Asciibird State</li>
<li
@click="$store.commit('openModal', 'edit-ascii')"
class="ml-1"
@ -82,7 +61,7 @@
style="display: none"
ref="asciiInput"
@change="onImport()"
>
/>
<template v-if="asciibirdMeta.length">
<t-button
@ -95,10 +74,7 @@
{{ value.title }}
</t-button>
<Toolbar
:canvas-x="canvasX"
:canvas-y="canvasY"
/>
<Toolbar :canvas-x="canvasX" :canvas-y="canvasY" />
<DebugPanel
:canvas-x="canvasX"
:canvas-y="canvasY"
@ -113,9 +89,7 @@
</template>
<template v-else>
<div style="left: 35%; top: 15%; position: absolute; z-index: -2">
<h1 style="font-size: 72px; text-align: center">
ASCIIBIRD
</h1>
<h1 style="font-size: 72px; text-align: center">ASCIIBIRD</h1>
<h1 style="font-size: 13px; text-align: center">
Right click to start
</h1>
@ -125,64 +99,31 @@
</template>
<script>
import LZString from 'lz-string';
import Toolbar from './components/Toolbar.vue';
import DebugPanel from './components/DebugPanel.vue';
import Editor from './views/Editor.vue';
import LZString from "lz-string";
import Toolbar from "./components/Toolbar.vue";
import DebugPanel from "./components/DebugPanel.vue";
import Editor from "./views/Editor.vue";
import CharPicker from './components/parts/CharPicker.vue';
import ColourPicker from './components/parts/ColourPicker.vue';
import ContextMenu from './components/parts/ContextMenu.vue';
import CharPicker from "./components/parts/CharPicker.vue";
import ColourPicker from "./components/parts/ColourPicker.vue";
import ContextMenu from "./components/parts/ContextMenu.vue";
import NewAscii from './components/modals/NewAscii.vue';
import EditAscii from './components/modals/EditAscii.vue';
import PasteAscii from './components/modals/PasteAscii.vue';
import NewAscii from "./components/modals/NewAscii.vue";
import EditAscii from "./components/modals/EditAscii.vue";
import PasteAscii from "./components/modals/PasteAscii.vue";
import { parseMircAscii, toolbarIcons } from './ascii';
import {
parseMircAscii,
toolbarIcons,
exportMirc,
downloadFile,
checkForGetRequest,
} from "./ascii";
export default {
async created() {
// Load from irc watch if present in the URL bar
const asciiUrlCdn = new URL(location.href).searchParams.get('ascii');
if (asciiUrlCdn) {
const res = await fetch(`https://ascii.jewbird.live/${asciiUrlCdn}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
const asciiData = await res.text();
this.mircAsciiImport(asciiData, asciiUrlCdn);
}
const asciiUrl = new URL(location.href).searchParams.get('ircwatch');
if (asciiUrl) {
const res = await fetch(`https://irc.watch/ascii/txt/${asciiUrl}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
const asciiData = await res.text();
this.mircAsciiImport(asciiData, asciiUrl);
}
const haxAscii = new URL(location.href).searchParams.get('haxAscii');
if (haxAscii) {
const res = await fetch(`https://art.h4x.life/${haxAscii}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
// Considers paths
const asciiName = haxAscii.split('/').pop();
const asciiData = await res.text();
this.mircAsciiImport(asciiData, asciiName);
}
checkForGetRequest();
},
components: {
Toolbar,
@ -195,7 +136,7 @@ export default {
EditAscii,
PasteAscii,
},
name: 'Dashboard',
name: "Dashboard",
data: () => ({
showNewAsciiModal: false,
currentTab: 1,
@ -212,8 +153,8 @@ export default {
},
icon() {
return [
this.currentTool.fa ?? 'fas',
this.currentTool.icon ?? 'mouse-pointer',
this.currentTool.fa ?? "fas",
this.currentTool.icon ?? "mouse-pointer",
];
},
options() {
@ -262,15 +203,15 @@ export default {
const fileReader = new FileReader();
const fileType = this.importType;
fileReader.addEventListener('load', () => {
fileReader.addEventListener("load", () => {
switch (fileType) {
case 'asb':
case "asb":
this.importAsciibirdState(fileReader.result, filename);
break;
default:
case 'mirc':
this.mircAsciiImport(fileReader.result, filename);
case "mirc":
parseMircAscii(fileReader.result, filename);
break;
}
});
@ -285,21 +226,18 @@ export default {
// console.log(this.importType);
this.$refs.asciiInput.click();
},
mircAsciiImport(contents, filename) {
parseMircAscii(contents, filename);
},
importAsciibirdState(fileContents) {
const contents = JSON.parse(
LZString.decompressFromEncodedURIComponent(fileContents),
LZString.decompressFromEncodedURIComponent(fileContents)
);
this.$store.commit('changeState', { ...contents });
this.$store.commit("changeState", { ...contents });
},
exportAsciibirdState() {
let output;
try {
output = LZString.compressToEncodedURIComponent(
JSON.stringify(this.$store.getters.state),
JSON.stringify(this.$store.getters.state)
);
// Default timestamp for filename
@ -311,110 +249,44 @@ export default {
const mi = today.getMinutes();
const s = today.getSeconds();
this.downloadToFile(
downloadFile(
output,
`asciibird-${y}-${m}-${d}-${h}-${mi}-${s}.asb`,
'application/gzip',
"application/gzip"
);
} catch (err) {
console.log(err);
}
},
exportMirc(type) {
const { currentAscii } = this.$store.getters;
const blocks = this.$store.getters.currentAsciiBlocks;
const output = [];
let curBlock = null;
let prevBlock = { bg: -1, fg: -1 };
for (let y = 0; y <= blocks.length - 1; y++) {
if (y >= currentAscii.height) {
continue;
}
for (let x = 0; x <= blocks[y].length - 1; x++) {
if (x >= currentAscii.width) {
continue;
}
curBlock = blocks[y][x];
// If we have a difference between our previous block
// we'll put a colour codes and continue as normal
if (curBlock.bg !== prevBlock.bg || curBlock.fg !== prevBlock.fg) {
curBlock = { ...blocks[y][x] };
const zeroPad = (num, places) => String(num).padStart(places, '0');
output.push(
`\u0003${zeroPad(
curBlock.fg ?? this.options.defaultFg,
2,
)},${zeroPad(curBlock.bg ?? this.options.defaultBg, 2)}`,
);
}
// null .chars will end up as space
output.push(curBlock.char ?? ' ');
prevBlock = blocks[y][x];
}
// We can never have a -1 colour code so we'll always
// write one at the start of each line
prevBlock = { bg: -1, fg: -1 };
// New line except for the very last line
if (y < blocks.length - 1) {
output.push('\n');
}
}
// Download to a txt file
// Check if txt already exists and append it
const filename = currentAscii.title.slice(currentAscii.title.length - 3) === 'txt'
? currentAscii.title
: `${currentAscii.title}.txt`;
startExport(type) {
let ascii = exportMirc();
switch (type) {
case 'clipboard':
this.$copyText(output.join('')).then(
case "clipboard":
this.$copyText(ascii.output.join("")).then(
(e) => {
alert('Copied');
console.log(e);
alert("Copied");
},
(e) => {
alert('Can not copy');
console.log(e);
},
alert("Can not copy");
}
);
break;
default:
case 'file':
this.downloadToFile(output.join(''), filename, 'text/plain');
case "file":
downloadFile(ascii.output.join(""), ascii.filename, "text/plain");
break;
}
},
downloadToFile(content, filename, contentType) {
const downloadFile = (content, filename, contentType) => {
const a = document.createElement('a');
const file = new Blob([content], { type: contentType });
a.href = URL.createObjectURL(file);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href);
};
return downloadFile(content, filename, contentType);
},
changeTab(key) {
// Update the tab index in vuex store
this.currentTab = key;
this.$store.commit('changeTab', key);
this.$store.commit("changeTab", key);
},
clearCache() {
localStorage.clear();
window.location.href = '/';
window.location.href = "/";
},
captureMouse(event) {
this.dashboardX = event.pageX;

View File

@ -120,6 +120,68 @@ export const mircColours99 = [
"#ffffff",
];
// Chars that end up in the toolbar
export const charCodes = [' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-',
'.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A',
'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e',
'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', '{', '|', '}', '~', 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è',
'ï', 'î', 'ì', 'Ä', 'Å', 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', 'ø', '£',
'Ø', '×', 'ƒ', 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '®', '¬', '½', '¼', '¡', '«',
'»', '░', '▒', '▓', '│', '┤', 'Á', 'Â', 'À', '©', '╣', '║', '╗', '╝', '¢', '¥', '┐', '└',
'┴', '┬', '├', '─', '┼', 'ã', 'Ã', '╚', '╔', '╩', '╦', '╠', '═', '╬', '¤', 'ð', 'Ð', 'Ê',
'Ë', 'È', 'ı', 'Í', 'Î', 'Ï', '┘', '┌', '█', '▄', '¦', 'Ì', '▀', 'Ó', 'ß', 'Ô', 'Ò', 'õ',
'Õ', 'µ', 'þ', 'Þ', 'Ú', 'Û', 'Ù', 'ý', 'Ý', '¯', '´', '≡', '±', '‗', '¾', '¶', '§', '÷',
'¸', '°', '¨', '·', '¹', '³', '²',
];
// Toolbar icons
export const toolbarIcons = [{
name: 'default',
icon: 'mouse-pointer',
fa: 'fas',
svgPath: 'assets/mouse-pointer-solid.svg',
},
{
name: 'select',
icon: 'square',
fa: 'far',
svgPath: 'assets/square-regular.svg',
},
{
name: 'text',
icon: 'font',
fa: 'fas',
svgPath: 'assets/font-solid.svg',
},
{
name: 'fill',
icon: 'fill-drip',
fa: 'fas',
svgPath: 'assets/fill-drip-solid.svg',
},
{
name: 'brush',
icon: 'paint-brush',
fa: 'fas',
svgPath: 'assets/paint-brush-solid.svg',
},
{
name: 'dropper',
icon: 'eye-dropper',
fa: 'fas',
svgPath: 'assets/eye-dropper-solid.svg',
},
{
name: 'eraser',
icon: 'eraser',
fa: 'fas',
svgPath: 'assets/eraser-solid.svg',
},
];
export const emptyBlock = {
bg: null,
fg: null,
@ -286,11 +348,11 @@ export const parseMircAscii = (content, title) => {
parsedColour = parseInt(`${colourChar1}${colourChar2}`);
if (
!Number.isNaN(colourChar1)
&& !Number.isNaN(colourChar2)
&& parseInt(colourChar2) > parseInt(colourChar1)
&& !Number.isNaN(parsedColour)
&& parseInt(parsedColour) < 10
!Number.isNaN(colourChar1) &&
!Number.isNaN(colourChar2) &&
parseInt(colourChar2) > parseInt(colourChar1) &&
!Number.isNaN(parsedColour) &&
parseInt(parsedColour) < 10
) {
parsedColour = parseInt(colourChar2);
widthOfColCodes += 1;
@ -298,8 +360,8 @@ export const parseMircAscii = (content, title) => {
}
if (
parseInt(colourChar2) === parseInt(colourChar1)
&& parseInt(parsedColour) < 10
parseInt(colourChar2) === parseInt(colourChar1) &&
parseInt(parsedColour) < 10
) {
parsedColour = parseInt(colourChar1);
asciiStringArray.shift();
@ -357,6 +419,7 @@ export const parseMircAscii = (content, title) => {
return true;
};
// Creates new blank ASCII
export const createNewAscii = (forms) => {
const newAscii = {
title: forms.createAscii.title,
@ -389,66 +452,134 @@ export const createNewAscii = (forms) => {
return true;
};
// Chars that end up in the toolbar
export const charCodes = [' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-',
'.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A',
'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e',
'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', '{', '|', '}', '~', 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è',
'ï', 'î', 'ì', 'Ä', 'Å', 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', 'ø', '£',
'Ø', '×', 'ƒ', 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '®', '¬', '½', '¼', '¡', '«',
'»', '░', '▒', '▓', '│', '┤', 'Á', 'Â', 'À', '©', '╣', '║', '╗', '╝', '¢', '¥', '┐', '└',
'┴', '┬', '├', '─', '┼', 'ã', 'Ã', '╚', '╔', '╩', '╦', '╠', '═', '╬', '¤', 'ð', 'Ð', 'Ê',
'Ë', 'È', 'ı', 'Í', 'Î', 'Ï', '┘', '┌', '█', '▄', '¦', 'Ì', '▀', 'Ó', 'ß', 'Ô', 'Ò', 'õ',
'Õ', 'µ', 'þ', 'Þ', 'Ú', 'Û', 'Ù', 'ý', 'Ý', '¯', '´', '≡', '±', '‗', '¾', '¶', '§', '÷',
'¸', '°', '¨', '·', '¹', '³', '²',
];
// Converts ASCIIBIRD blocks to mIRC colours
export const exportMirc = () => {
const {
currentAscii
} = store.getters;
const blocks = store.getters.currentAsciiBlocks;
const output = [];
let curBlock = null;
let prevBlock = {
bg: -1,
fg: -1
};
// Toolbar icons
export const toolbarIcons = [{
name: 'default',
icon: 'mouse-pointer',
fa: 'fas',
svgPath: 'assets/mouse-pointer-solid.svg',
},
{
name: 'select',
icon: 'square',
fa: 'far',
svgPath: 'assets/square-regular.svg',
},
{
name: 'text',
icon: 'font',
fa: 'fas',
svgPath: 'assets/font-solid.svg',
},
{
name: 'fill',
icon: 'fill-drip',
fa: 'fas',
svgPath: 'assets/fill-drip-solid.svg',
},
{
name: 'brush',
icon: 'paint-brush',
fa: 'fas',
svgPath: 'assets/paint-brush-solid.svg',
},
{
name: 'dropper',
icon: 'eye-dropper',
fa: 'fas',
svgPath: 'assets/eye-dropper-solid.svg',
},
{
name: 'eraser',
icon: 'eraser',
fa: 'fas',
svgPath: 'assets/eraser-solid.svg',
},
];
for (let y = 0; y <= blocks.length - 1; y++) {
if (y >= currentAscii.height) {
continue;
}
for (let x = 0; x <= blocks[y].length - 1; x++) {
if (x >= currentAscii.width) {
continue;
}
curBlock = blocks[y][x];
// If we have a difference between our previous block
// we'll put a colour codes and continue as normal
if (curBlock.bg !== prevBlock.bg || curBlock.fg !== prevBlock.fg) {
curBlock = {
...blocks[y][x]
};
const zeroPad = (num, places) => String(num).padStart(places, '0');
output.push(
`\u0003${zeroPad(
curBlock.fg ?? this.options.defaultFg,
2,
)},${zeroPad(curBlock.bg ?? this.options.defaultBg, 2)}`,
);
}
// null .chars will end up as space
output.push(curBlock.char ?? ' ');
prevBlock = blocks[y][x];
}
// We can never have a -1 colour code so we'll always
// write one at the start of each line
prevBlock = {
bg: -1,
fg: -1
};
// New line except for the very last line
if (y < blocks.length - 1) {
output.push('\n');
}
}
// Download to a txt file
// Check if txt already exists and append it
const filename = currentAscii.title.slice(currentAscii.title.length - 3) === 'txt' ?
currentAscii.title :
`${currentAscii.title}.txt`;
return {
filename,
output
}
}
// Download a string to a file with a filename
export const downloadFile = (content, filename, contentType) => {
const a = document.createElement('a');
const file = new Blob([content], {
type: contentType
});
a.href = URL.createObjectURL(file);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href);
};
export const checkForGetRequest = async () => {
const asciiUrlCdn = new URL(location.href).searchParams.get('ascii');
if (asciiUrlCdn) {
const res = await fetch(`https://ascii.jewbird.live/${asciiUrlCdn}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
const asciiData = await res.text();
parseMircAscii(asciiData, asciiUrlCdn);
return;
}
const asciiUrl = new URL(location.href).searchParams.get('ircwatch');
if (asciiUrl) {
const res = await fetch(`https://irc.watch/ascii/txt/${asciiUrl}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
const asciiData = await res.text();
parseMircAscii(asciiData, asciiUrl);
return;
}
const haxAscii = new URL(location.href).searchParams.get('haxAscii');
if (haxAscii) {
const res = await fetch(`https://art.h4x.life/${haxAscii}`, {
method: 'GET',
headers: {
Accept: 'text/plain',
},
});
// Considers paths
const asciiName = haxAscii.split('/').pop();
const asciiData = await res.text();
parseMircAscii(asciiData, asciiName);
return;
}
}
export default createNewAscii;

View File

@ -4,6 +4,7 @@
:header="currentAsciiEditingTitle"
:click-to-close="false"
:esc-to-close="true"
@closed="$store.commit('closeModal', 'edit-ascii')"
>
Width
<t-input
@ -34,7 +35,7 @@
<template v-slot:footer>
<div
class="flex justify-between"
@click="$modal.hide('edit-ascii-modal')"
@click="$store.commit('closeModal', 'edit-ascii')"
>
<t-button type="button">
Cancel
@ -77,22 +78,26 @@ export default {
},
},
watch: {
showEditAsciiModal(val, old) {
if (val !== old) {
this.showEditModal();
}
showEditAsciiModal(val) {
if (val === true) {
this.open();
}
// this.showEditModal();
if (val === false) {
this.close();
}
},
},
methods: {
showEditModal() {
// this.forms.editAscii.title = `Editing ASCII ${this.currentAscii.title}`;
updateAscii() {
this.$store.commit('updateAscii', this.forms.editAscii);
this.close()
},
open() {
this.forms.editAscii = this.currentAscii;
this.$modal.show('edit-ascii-modal');
},
updateAscii() {
this.$store.commit('updateAscii', this.forms.editAscii);
close() {
this.$modal.hide('edit-ascii-modal');
},
},

View File

@ -1,10 +1,10 @@
<template>
<t-modal
name="create-ascii-modal"
name="new-ascii-modal"
header="Create new ASCII"
:click-to-close="false"
:esc-to-close="true"
@before-closed="closeNewASCII"
@closed="$store.commit('closeModal', 'new-ascii')"
>
Width
<t-input
@ -33,7 +33,7 @@
<template v-slot:footer>
<div
class="flex justify-between"
@click="$modal.hide('create-ascii-modal')"
@click="$store.commit('closeModal', 'new-ascii')"
>
<t-button type="button">
Cancel
@ -70,25 +70,30 @@ export default {
},
},
watch: {
showNewAsciiModal() {
this.createClick();
showNewAsciiModal(val) {
if (val === true) {
this.open();
}
if (val === false) {
this.close();
}
},
},
methods: {
createClick() {
open() {
this.$modal.show('new-ascii-modal');
this.forms.createAscii.title = `New ASCII ${this.$store.getters.asciibirdMeta.length + 1}`;
this.$modal.show('create-ascii-modal');
},
initiateNewAscii() {
createNewASCII(this.forms);
this.$modal.hide('create-ascii-modal');
},
closeNewASCII() {
close() {
this.$modal.hide('new-ascii-modal');
this.forms.createAscii.width = 80;
this.forms.createAscii.height = 30;
this.forms.createAscii.title = 'New ASCII';
},
initiateNewAscii() {
createNewASCII(this.forms);
},
},
};
</script>

View File

@ -4,6 +4,7 @@
header="Import from Clipboard"
:click-to-close="false"
:esc-to-close="true"
@closed="$store.commit('closeModal', 'paste-ascii')"
>
Title
<t-input
@ -23,7 +24,9 @@
<div
class="flex justify-between"
>
<t-button type="button">
<t-button
type="button"
@click="$store.commit('closeModal', 'paste-ascii')">
Cancel
</t-button>
<t-button
@ -50,29 +53,39 @@ export default {
title: 'clipboard.txt',
}),
computed: {
showPasteModal() {
return this.$store.getters.modalState.pasteModal;
showPasteAscii() {
return this.$store.getters.modalState.pasteAscii;
},
checkPasteContent() {
return !this.pasteContent.length;
},
},
watch: {
showPasteModal(val, old) {
if (val !== old) {
this.pasteModal();
}
showPasteAscii(val, old) {
if (val === true) {
this.open();
}
if (val === false) {
this.close();
}
},
},
methods: {
pasteModal() {
open() {
this.$modal.show('paste-ascii-modal');
// this.$store.commit('openModal', 'paste-ascii')
},
importPasteAscii() {
parseMircAscii(this.pasteContent, this.title);
close() {
console.log("close")
this.pasteContent = '';
this.title = 'clipboard.txt';
this.$modal.hide('paste-ascii-modal');
},
importPasteAscii() {
parseMircAscii(this.pasteContent, this.title);
this.close()
},
},
};

View File

@ -5,7 +5,7 @@
<t-input
type="number"
name="width"
v-model="brushSizeWidth"
v-model="brushSizeWidthInput"
@change="updateBrushSize"
min="1"
max="10"
@ -16,7 +16,7 @@
<t-input
type="number"
name="height"
v-model="brushSizeHeight"
v-model="brushSizeHeightInput"
@change="updateBrushSize"
min="1"
max="10"
@ -30,7 +30,7 @@
name="options"
value="square"
checked
v-model="brushSizeType"
v-model="brushSizeTypeInput"
@change="updateBrushSize"
/>
<span class="text-sm">Square</span>
@ -40,7 +40,7 @@
<t-radio
name="options"
value="circle"
v-model="brushSizeType"
v-model="brushSizeTypeInput"
@change="updateBrushSize"
/>
<span class="text-sm">Circle</span>
@ -50,7 +50,7 @@
<t-radio
name="options"
value="cross"
v-model="brushSizeType"
v-model="brushSizeTypeInput"
@change="updateBrushSize"
/>
<span class="text-sm">Cross</span>
@ -61,8 +61,8 @@
ref="brushcanvas"
id="brushcanvas"
class="brushcanvas"
:width="brushSizeWidthPreview + 1 * currentAscii.blockWidth"
:height="brushSizeHeightPreview + 1 * currentAscii.blockHeight"
:width="brushSizeWidth * currentAscii.blockWidth"
:height="brushSizeHeight * currentAscii.blockHeight"
/>
</div>
</template>
@ -75,17 +75,17 @@ export default {
mounted() {
this.ctx = this.$refs.brushcanvas.getContext("2d");
this.delayRedrawCanvas();
this.brushSizeWidth = this.brushSizeWidthPreview;
this.brushSizeHeight = this.brushSizeHeightPreview;
this.brushSizeType = this.brushSizeTypePreview;
this.brushSizeWidthInput = this.brushSizeWidth;
this.brushSizeHeightInput = this.brushSizeHeight;
this.brushSizeTypeInput = this.brushSizeType;
},
data: () => ({
ctx: null,
redraw: true,
blocks: [],
brushSizeHeight: 1,
brushSizeWidth: 1,
brushSizeType: "square",
brushSizeHeightInput: 1,
brushSizeWidthInput: 1,
brushSizeTypeInput: "square",
}),
computed: {
currentAscii() {
@ -112,13 +112,13 @@ export default {
getChar() {
return this.$store.getters.getChar;
},
brushSizeHeightPreview() {
brushSizeHeight() {
return this.$store.getters.brushSizeHeight;
},
brushSizeWidthPreview() {
brushSizeWidth() {
return this.$store.getters.brushSizeWidth;
},
brushSizeTypePreview() {
brushSizeType() {
return this.$store.getters.brushSizeType;
},
mircColours() {
@ -131,9 +131,15 @@ export default {
watch: {
brushSizeWidth() {
this.delayRedrawCanvas();
this.brushSizeWidthInput = this.brushSizeWidth;
},
brushSizeHeight() {
this.delayRedrawCanvas();
this.brushSizeHeightInput = this.brushSizeHeight;
},
brushSizeType() {
this.delayRedrawCanvas();
this.brushSizeTypeInput = this.brushSizeType;
},
isTargettingBg() {
this.delayRedrawCanvas();
@ -157,19 +163,19 @@ export default {
methods: {
updateBrushSize() {
this.$store.commit("updateBrushSize", {
brushSizeHeight: this.brushSizeHeight,
brushSizeWidth: this.brushSizeWidth,
brushSizeType: this.brushSizeType,
brushSizeHeight: this.brushSizeHeightInput,
brushSizeWidth: this.brushSizeWidthInput,
brushSizeType: this.brushSizeTypeInput,
});
this.ctx.clearRect(0, 0, 1000, 1000);
this.ctx.clearRect(0, 0, 10000, 10000);
this.delayRedrawCanvas();
},
drawPreview() {
this.ctx.clearRect(0, 0, 10000, 10000);
const brushHeight = this.brushSizeHeightPreview;
const brushWidth = this.brushSizeWidthPreview;
const brushHeight = this.brushSizeHeight;
const brushWidth = this.brushSizeWidth;
this.blocks = [];
@ -199,7 +205,7 @@ export default {
for (y = 0; y < brushHeight; y++) {
this.blocks[y] = [];
for (x = 0; x < brushWidth; x++) {
switch (this.brushSizeTypePreview) {
switch (this.brushSizeType) {
case "cross":
// If we are 1x1 force fill 1 block, to avoid an empty 1x1
if (x === 0 && y === 0) {

View File

@ -15,7 +15,7 @@ export default new Vuex.Store({
modalState: {
newAscii: false,
editAscii: false,
pasteModal: false,
pasteAscii: false,
},
// The various options of ASCIIBIRD will eventually
// end up in its own modal I guess
@ -177,15 +177,30 @@ export default new Vuex.Store({
openModal(state, type) {
switch (type) {
case 'new-ascii':
state.modalState.newAscii = !state.modalState.newAscii;
state.modalState.newAscii = true;
break;
case 'edit-ascii':
state.modalState.editAscii = !state.modalState.editAscii;
state.modalState.editAscii = true;
break;
case 'paste-modal':
state.modalState.pasteModal = !state.modalState.pasteModal;
case 'paste-ascii':
state.modalState.pasteAscii = true;
break;
}
},
closeModal(state, type) {
switch (type) {
case 'new-ascii':
state.modalState.newAscii = false;
break;
case 'edit-ascii':
state.modalState.editAscii = false;
break;
case 'paste-ascii':
state.modalState.pasteAscii = false;
break;
}
},
@ -214,6 +229,13 @@ export default new Vuex.Store({
blockSizeMultiplier: (state) => state.blockSizeMultiplier,
brushBlocks: (state) => JSON.parse(LZString.decompressFromUTF16(state.brushBlocks)) || [],
selectBlocks: (state) => JSON.parse(LZString.decompressFromUTF16(state.selectBlocks)) || [],
isModalOpen: (state) => {
for (const modalState in state.modalState) {
if (state.modalState[modalState] === true)
return true
}
return false
},
},
actions: {},
modules: {},

View File

@ -63,7 +63,7 @@ body {
</style>
<script>
import { emptyBlock, toolbarIcons, mircColours99 } from '../ascii';
import { emptyBlock, toolbarIcons, mircColours99, exportMirc, downloadFile } from '../ascii';
export default {
name: 'Editor',
@ -80,6 +80,12 @@ export default {
const thisIs = this;
this.keyListener = function (e) {
// Stop blocking input when modals are open
if (this.isModalOpen) {
return
}
e.preventDefault();
if (this.isTextEditing) {
@ -88,6 +94,7 @@ export default {
}
const ctrlKey = e.ctrlKey || e.metaKey;
const shiftKey = e.shiftKey;
// Ctrl Z here
// skg - thanks for mac key suggestion, bro
@ -97,12 +104,11 @@ export default {
// Ctrl Y here
if (e.key === 'y' && ctrlKey) {
// Fk it works :\
this.redo();
}
// Ctrl C - copy blocks
if (e.key === 'c' && ctrlKey) {
if (e.key === 'c' && ctrlKey && !shiftKey) {
if (this.selectedBlocks.length) {
this.$store.commit('selectBlocks', this.selectedBlocks);
this.selectedBlocks = [];
@ -117,11 +123,70 @@ export default {
}
}
// Show / hide debug panel
if (e.key === 'd' && ctrlKey) {
this.$store.commit('toggleDebugPanel', !this.debugPanelState.visible);
}
// New ASCII
// Ctrl N doesn't seem to work in chrome? https://github.com/liftoff/GateOne/issues/290
if (e.key === 'm' && ctrlKey) {
this.$store.commit('openModal', 'new-ascii');
}
// Edit ASCII
if (e.key === 'e' && ctrlKey) {
this.$store.commit('openModal', 'edit-ascii');
}
// Paste ASCII
if (e.key === 'p' && ctrlKey) {
this.$store.commit('openModal', 'paste-ascii');
}
// Export to clipboard
if (e.key === 'C' && ctrlKey && shiftKey) {
let ascii = exportMirc();
this.$copyText(ascii.output.join('')).then(
(e) => {
alert('Copied');
},
(e) => {
alert('Can not copy');
},
);
}
// Export to txt
if (e.key === 'F' && ctrlKey && shiftKey) {
let ascii = exportMirc();
downloadFile(ascii.output.join(''), ascii.filename, 'text/plain');
}
if (e.key === ']' && ctrlKey &&
(this.brushSizeHeight < 10 && this.brushSizeHeight >= 1) &&
(this.brushSizeWidth < 10 && this.brushSizeWidth >= 1)) {
this.$store.commit("updateBrushSize", {
brushSizeHeight: this.brushSizeHeight + 1,
brushSizeWidth: this.brushSizeWidth + 1,
brushSizeType: this.brushSizeType,
});
}
if (e.key === '[' && ctrlKey &&
(this.brushSizeHeight < 10 && this.brushSizeHeight >= 1) &&
(this.brushSizeWidth < 10 && this.brushSizeWidth >= 1)) {
this.$store.commit("updateBrushSize", {
brushSizeHeight: this.brushSizeHeight - 1,
brushSizeWidth: this.brushSizeWidth - 1,
brushSizeType: this.brushSizeType,
});
}
};
document.addEventListener('keydown', this.keyListener.bind(this));
}
},
@ -151,6 +216,18 @@ export default {
selectedBlocks: [],
}),
computed: {
isModalOpen() {
return this.$store.getters.isModalOpen;
},
brushSizeHeight() {
return this.$store.getters.brushSizeHeight;
},
brushSizeWidth() {
return this.$store.getters.brushSizeWidth;
},
brushSizeType() {
return this.$store.getters.brushSizeType;
},
currentAscii() {
return this.$store.getters.currentAscii;
},