Compare commits

..

2 Commits

@ -1,7 +1,5 @@
# rpg-cards-ng
**Try the super extra pre alpha version here:** https://rpg-cards-ng.netlify.com/
![Decks Overview](https://raw.githubusercontent.com/nkoehring/rpg-cards-ng/master/docs/scrot.jpg)
![Deck Example](https://raw.githubusercontent.com/nkoehring/rpg-cards-ng/master/docs/scrot2.jpg)

@ -10,12 +10,12 @@
"dependencies": {
"core-js": "^3.6.4",
"register-service-worker": "^1.6.2",
"tiptap": "^1.26.6",
"tiptap-extensions": "^1.28.6",
"vue": "^2.6.11",
"vue-router": "^3.1.5"
},
"devDependencies": {
"@editorjs/editorjs": "^2.17.0",
"@editorjs/list": "^1.4.0",
"@typescript-eslint/eslint-plugin": "^2.18.0",
"@typescript-eslint/parser": "^2.18.0",
"@vue/cli-plugin-babel": "^4.2.0",
@ -32,11 +32,13 @@
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^6.1.2",
"lint-staged": "^9.5.0",
"raw-loader": "^4.0.0",
"typescript": "~3.7.5",
"vue-property-decorator": "^8.4.0",
"vue-template-compiler": "^2.6.11"
},
"resolutions": {
"prosemirror-model": "1.8.2"
},
"eslintConfig": {
"root": true,
"env": {
@ -49,6 +51,9 @@
],
"parserOptions": {
"ecmaVersion": 2020
},
"rules": {
"no-useless-constructor": "off"
}
},
"browserslist": [

@ -1 +0,0 @@
/* /index.html 200

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -5,13 +5,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="icon" href="<%= BASE_URL %>favicon-32.png" sizes="32x32">
<link rel="icon" href="<%= BASE_URL %>favicon-128.png" sizes="128x128">
<link rel="icon" href="<%= BASE_URL %>favicon-192.png" sizes="192x192">
<link rel="shortcut icon" href="<%= BASE_URL %>favicon-196.png" sizes="196x196">
<link rel="apple-touch-icon" href="<%= BASE_URL %>favicon-152.png" sizes="152x152">
<link rel="apple-touch-icon" href="<%= BASE_URL %>favicon-180.png" sizes="180x180">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>

@ -46,6 +46,7 @@ html,body {
header {
display: block;
height: 10rem;
font-size: 2rem;
line-height: 2rem;
padding: 4rem 0;
@ -54,13 +55,6 @@ header {
header > p {
opacity: .6;
}
section[name=notifications] {
display: block;
max-width: 70rem;
margin: 0 auto 1em;
padding: 1rem 3rem;
border: .5em solid red;
}
#popup {
position: fixed;
@ -132,7 +126,3 @@ button.action-close {
border-radius: 1em;
cursor: pointer;
}
.codex-editor--narrow .codex-editor__redactor {
margin-right: 0;
}

@ -1 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><circle r="7" cx="7" cy="7" stroke="black" stroke-width="2" fill="none" /></svg>

Before

Width:  |  Height:  |  Size: 164 B

@ -1 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><rect width="14" height="14" stroke="black" stroke-width="2" fill="none" /></svg>

Before

Width:  |  Height:  |  Size: 165 B

@ -1 +0,0 @@
<svg width="19" height="4" viewBox="0 0 19 4" xmlns="http://www.w3.org/2000/svg"><path d="M1.25 0H7a1.25 1.25 0 1 1 0 2.5H1.25a1.25 1.25 0 1 1 0-2.5zM11 0h5.75a1.25 1.25 0 0 1 0 2.5H11A1.25 1.25 0 0 1 11 0z"/></svg>

Before

Width:  |  Height:  |  Size: 216 B

@ -1 +0,0 @@
<svg width="19" height="4" viewBox="0 0 19 4" xmlns="http://www.w3.org/2000/svg"><path d="m 16.61577,0 -5.820501,0.28200371 c -1.6647133,0.0806555 -1.6633064,1.74209559 0,1.84786639 L 16.61577,2.5 c 1.663306,0.1057709 1.664713,-2.58065548 0,-2.5 z M 6.9186458,0.51113173 1.2612438,0.8454868 c -1.66376299,0.0983292 -1.68672099,0.7081403 -0.0221,0.7906147 l 5.697126,0.2822659 c 1.664625,0.082474 1.6461385,-1.50556481 -0.017624,-1.40723567 z" /></svg>

Before

Width:  |  Height:  |  Size: 452 B

@ -1 +0,0 @@
<svg width="19" height="4" viewBox="0 0 19 4" xmlns="http://www.w3.org/2000/svg"><path d="m 1.25,0 5.8205009,0.28200371 c 1.6647132,0.0806555 1.6633063,1.74209559 0,1.84786639 L 1.25,2.5 c -1.66330631,0.1057709 -1.66471321,-2.58065548 0,-2.5 z m 9.697124,0.51113173 5.657402,0.33435507 c 1.663763,0.0983292 1.686721,0.7081403 0.0221,0.7906147 L 10.9295,1.9183674 C 9.264875,2.0008418 9.2833615,0.41280259 10.947124,0.51113173 Z" /></svg>

Before

Width:  |  Height:  |  Size: 438 B

@ -1 +0,0 @@
<svg width="10" height="14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 14"><path d="M7.6 8.15H2.25v4.525a1.125 1.125 0 0 1-2.25 0V1.125a1.125 1.125 0 1 1 2.25 0V5.9H7.6V1.125a1.125 1.125 0 0 1 2.25 0v11.55a1.125 1.125 0 0 1-2.25 0V8.15z"/></svg>

Before

Width:  |  Height:  |  Size: 254 B

@ -1 +0,0 @@
<svg width="16" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.14 1.494V4.98h4.62V1.494c0-.498.098-.871.293-1.12A.927.927 0 0 1 7.82 0c.322 0 .583.123.782.37.2.246.3.62.3 1.124v9.588c0 .503-.101.88-.303 1.128a.957.957 0 0 1-.779.374.921.921 0 0 1-.77-.378c-.193-.251-.29-.626-.29-1.124V6.989H2.14v4.093c0 .503-.1.88-.302 1.128a.957.957 0 0 1-.778.374.921.921 0 0 1-.772-.378C.096 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.285.374A.922.922 0 0 1 1.06 0c.321 0 .582.123.782.37.199.246.299.62.299 1.124zm11.653 9.985V5.27c-1.279.887-2.14 1.33-2.583 1.33a.802.802 0 0 1-.563-.228.703.703 0 0 1-.245-.529c0-.232.08-.402.241-.511.161-.11.446-.25.854-.424.61-.259 1.096-.532 1.462-.818a5.84 5.84 0 0 0 .97-.962c.282-.355.466-.573.552-.655.085-.082.246-.123.483-.123.267 0 .481.093.642.28.161.186.242.443.242.77v7.813c0 .914-.345 1.371-1.035 1.371-.307 0-.554-.093-.74-.28-.187-.186-.28-.461-.28-.825z"/></svg>

Before

Width:  |  Height:  |  Size: 918 B

@ -1 +0,0 @@
<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm10.99 9.288h3.527c.351 0 .62.072.804.216.185.144.277.34.277.588 0 .22-.073.408-.22.56-.146.154-.368.23-.665.23h-4.972c-.338 0-.601-.093-.79-.28a.896.896 0 0 1-.284-.659c0-.162.06-.377.182-.645s.255-.478.399-.631a38.617 38.617 0 0 1 1.621-1.598c.482-.444.827-.735 1.034-.875.369-.261.676-.523.922-.787.245-.263.432-.534.56-.81.129-.278.193-.549.193-.815 0-.288-.069-.546-.206-.773a1.428 1.428 0 0 0-.56-.53 1.618 1.618 0 0 0-.774-.19c-.59 0-1.054.26-1.392.777-.045.068-.12.252-.226.554-.106.302-.225.534-.358.696-.133.162-.328.243-.585.243a.76.76 0 0 1-.56-.223c-.149-.148-.223-.351-.223-.608 0-.31.07-.635.21-.972.139-.338.347-.645.624-.92a3.093 3.093 0 0 1 1.054-.665c.426-.169.924-.253 1.496-.253.69 0 1.277.108 1.764.324.315.144.592.343.83.595.24.252.425.544.558.875.133.33.2.674.2 1.03 0 .558-.14 1.066-.416 1.523-.277.457-.56.815-.848 1.074-.288.26-.771.666-1.45 1.22-.677.554-1.142.984-1.394 1.29a3.836 3.836 0 0 0-.331.44z"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -1 +0,0 @@
<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm11.61 4.919c.418 0 .778-.123 1.08-.368.301-.245.452-.597.452-1.055 0-.35-.12-.65-.36-.902-.241-.252-.566-.378-.974-.378-.277 0-.505.038-.684.116a1.1 1.1 0 0 0-.426.306 2.31 2.31 0 0 0-.296.49c-.093.2-.178.388-.255.565a.479.479 0 0 1-.245.225.965.965 0 0 1-.409.081.706.706 0 0 1-.5-.22c-.152-.148-.228-.345-.228-.59 0-.236.071-.484.214-.745a2.72 2.72 0 0 1 .627-.746 3.149 3.149 0 0 1 1.024-.568 4.122 4.122 0 0 1 1.368-.214c.44 0 .842.06 1.205.18.364.12.679.294.947.52.267.228.47.49.606.79.136.3.204.622.204.967 0 .454-.099.843-.296 1.168-.198.324-.48.64-.848.95.354.19.653.408.895.653.243.245.426.516.548.813.123.298.184.619.184.964 0 .413-.083.812-.248 1.198-.166.386-.41.73-.732 1.031a3.49 3.49 0 0 1-1.147.708c-.443.17-.932.256-1.467.256a3.512 3.512 0 0 1-1.464-.293 3.332 3.332 0 0 1-1.699-1.64c-.142-.314-.214-.573-.214-.777 0-.263.085-.475.255-.636a.89.89 0 0 1 .637-.242c.127 0 .25.037.367.112a.53.53 0 0 1 .232.27c.236.63.489 1.099.759 1.405.27.306.65.46 1.14.46a1.714 1.714 0 0 0 1.46-.824c.17-.273.256-.588.256-.947 0-.53-.145-.947-.436-1.249-.29-.302-.694-.453-1.212-.453-.09 0-.231.01-.422.028-.19.018-.313.027-.367.027-.25 0-.443-.062-.579-.187-.136-.125-.204-.299-.204-.521 0-.218.081-.394.245-.528.163-.134.406-.2.728-.2h.28z"/></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@ -1 +0,0 @@
<svg width="20" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm13.003 10.09v-1.252h-3.38c-.427 0-.746-.097-.96-.29-.213-.193-.32-.456-.32-.788 0-.085.016-.171.048-.259.031-.088.078-.18.141-.276.063-.097.128-.19.195-.28.068-.09.15-.2.25-.33l3.568-4.774a5.44 5.44 0 0 1 .576-.683.763.763 0 0 1 .542-.212c.682 0 1.023.39 1.023 1.171v5.212h.29c.346 0 .623.047.832.142.208.094.313.3.313.62 0 .26-.086.45-.256.568-.17.12-.427.179-.768.179h-.41v1.252c0 .346-.077.603-.23.771-.152.168-.356.253-.612.253a.78.78 0 0 1-.61-.26c-.154-.173-.232-.427-.232-.764zm-2.895-2.76h2.895V4.91L12.26 8.823z"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

@ -1 +0,0 @@
<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm14.16 2.645h-3.234l-.388 2.205c.644-.344 1.239-.517 1.783-.517.436 0 .843.082 1.222.245.38.164.712.39.998.677.286.289.51.63.674 1.025.163.395.245.82.245 1.273 0 .658-.148 1.257-.443 1.797-.295.54-.72.97-1.276 1.287-.556.318-1.197.477-1.923.477-.813 0-1.472-.15-1.978-.45-.506-.3-.865-.643-1.076-1.031-.21-.388-.316-.727-.316-1.018 0-.177.073-.345.22-.504a.725.725 0 0 1 .556-.238c.381 0 .665.22.85.66.182.404.427.719.736.943.309.225.654.337 1.035.337.35 0 .656-.09.919-.272.263-.182.466-.431.61-.749.142-.318.214-.678.214-1.082 0-.436-.078-.808-.232-1.117a1.607 1.607 0 0 0-.62-.69 1.674 1.674 0 0 0-.864-.229c-.39 0-.67.048-.837.143-.168.095-.41.262-.725.5-.316.239-.576.358-.78.358a.843.843 0 0 1-.592-.242c-.173-.16-.259-.344-.259-.548 0-.022.025-.177.075-.463l.572-3.26c.063-.39.181-.675.354-.852.172-.177.454-.265.844-.265h3.595c.708 0 1.062.27 1.062.81a.711.711 0 0 1-.26.572c-.172.145-.426.218-.762.218z"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -1 +0,0 @@
<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zM12.53 7.058a3.093 3.093 0 0 1 1.004-.814 2.734 2.734 0 0 1 1.214-.264c.43 0 .827.08 1.19.24.365.161.684.39.957.686.274.296.485.645.635 1.048a3.6 3.6 0 0 1 .223 1.262c0 .637-.145 1.216-.437 1.736-.292.52-.699.926-1.221 1.218-.522.292-1.114.438-1.774.438-.76 0-1.416-.186-1.967-.557-.552-.37-.974-.919-1.265-1.645-.292-.726-.438-1.613-.438-2.662 0-.855.088-1.62.265-2.293.176-.674.43-1.233.76-1.676.33-.443.73-.778 1.2-1.004.47-.226 1.006-.339 1.608-.339.579 0 1.089.113 1.53.34.44.225.773.506.997.84.224.335.335.656.335.964 0 .185-.07.354-.21.505a.698.698 0 0 1-.536.227.874.874 0 0 1-.529-.18 1.039 1.039 0 0 1-.36-.498 1.42 1.42 0 0 0-.495-.655 1.3 1.3 0 0 0-.786-.247c-.24 0-.479.069-.716.207a1.863 1.863 0 0 0-.6.56c-.33.479-.525 1.333-.584 2.563zm1.832 4.213c.456 0 .834-.186 1.133-.56.298-.373.447-.862.447-1.468 0-.412-.07-.766-.21-1.062a1.584 1.584 0 0 0-.577-.678 1.47 1.47 0 0 0-.807-.234c-.28 0-.548.074-.804.224-.255.149-.461.365-.617.647a2.024 2.024 0 0 0-.234.994c0 .61.158 1.12.475 1.527.316.407.714.61 1.194.61z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

@ -1 +0,0 @@
<svg width="17" height="13" viewBox="0 0 17 13" xmlns="http://www.w3.org/2000/svg"> <path d="M5.625 4.85h9.25a1.125 1.125 0 0 1 0 2.25h-9.25a1.125 1.125 0 0 1 0-2.25zm0-4.85h9.25a1.125 1.125 0 0 1 0 2.25h-9.25a1.125 1.125 0 0 1 0-2.25zm0 9.85h9.25a1.125 1.125 0 0 1 0 2.25h-9.25a1.125 1.125 0 0 1 0-2.25zm-4.5-5a1.125 1.125 0 1 1 0 2.25 1.125 1.125 0 0 1 0-2.25zm0-4.85a1.125 1.125 0 1 1 0 2.25 1.125 1.125 0 0 1 0-2.25zm0 9.85a1.125 1.125 0 1 1 0 2.25 1.125 1.125 0 0 1 0-2.25z"/></svg>

Before

Width:  |  Height:  |  Size: 488 B

@ -1 +0,0 @@
<svg width="17" height="13" viewBox="0 0 17 13" xmlns="http://www.w3.org/2000/svg"><path d="M5.819 4.607h9.362a1.069 1.069 0 0 1 0 2.138H5.82a1.069 1.069 0 1 1 0-2.138zm0-4.607h9.362a1.069 1.069 0 0 1 0 2.138H5.82a1.069 1.069 0 1 1 0-2.138zm0 9.357h9.362a1.069 1.069 0 0 1 0 2.138H5.82a1.069 1.069 0 0 1 0-2.137zM1.468 4.155V1.33c-.554.404-.926.606-1.118.606a.338.338 0 0 1-.244-.104A.327.327 0 0 1 0 1.59c0-.107.035-.184.105-.234.07-.05.192-.114.369-.192.264-.118.475-.243.633-.373.158-.13.298-.276.42-.438a3.94 3.94 0 0 1 .238-.298C1.802.019 1.872 0 1.975 0c.115 0 .208.042.277.127.07.085.105.202.105.351v3.556c0 .416-.15.624-.448.624a.421.421 0 0 1-.32-.127c-.08-.085-.121-.21-.121-.376zm-.283 6.664h1.572c.156 0 .275.03.358.091a.294.294 0 0 1 .123.25.323.323 0 0 1-.098.238c-.065.065-.164.097-.296.097H.629a.494.494 0 0 1-.353-.119.372.372 0 0 1-.126-.28c0-.068.027-.16.081-.273a.977.977 0 0 1 .178-.268c.267-.264.507-.49.722-.678.215-.188.368-.312.46-.371.165-.11.302-.222.412-.334.109-.112.192-.226.25-.344a.786.786 0 0 0 .085-.345.6.6 0 0 0-.341-.553.75.75 0 0 0-.345-.08c-.263 0-.47.11-.62.329-.02.029-.054.107-.101.235a.966.966 0 0 1-.16.295c-.059.069-.145.103-.26.103a.348.348 0 0 1-.25-.094.34.34 0 0 1-.099-.258c0-.132.031-.27.093-.413.063-.143.155-.273.279-.39.123-.116.28-.21.47-.282.189-.072.411-.107.666-.107.307 0 .569.045.786.137a1.182 1.182 0 0 1 .618.623 1.18 1.18 0 0 1-.096 1.083 2.03 2.03 0 0 1-.378.457c-.128.11-.344.282-.646.517-.302.235-.509.417-.621.547a1.637 1.637 0 0 0-.148.187z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

@ -1 +0,0 @@
<svg width="17" height="13" viewBox="0 0 17 13" xmlns="http://www.w3.org/2000/svg"> <path d="M5.625 4.85h9.25a1.125 1.125 0 0 1 0 2.25h-9.25a1.125 1.125 0 0 1 0-2.25zm0-4.85h9.25a1.125 1.125 0 0 1 0 2.25h-9.25a1.125 1.125 0 0 1 0-2.25zm0 9.85h9.25a1.125 1.125 0 0 1 0 2.25h-9.25a1.125 1.125 0 0 1 0-2.25zm-4.5-5a1.125 1.125 0 1 1 0 2.25 1.125 1.125 0 0 1 0-2.25zm0-4.85a1.125 1.125 0 1 1 0 2.25 1.125 1.125 0 0 1 0-2.25zm0 9.85a1.125 1.125 0 1 1 0 2.25 1.125 1.125 0 0 1 0-2.25z"/></svg>

Before

Width:  |  Height:  |  Size: 488 B

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0.2 -0.3 9 11.4" width="12" height="14">
<path d="M0 2.77V.92A1 1 0 01.2.28C.35.1.56 0 .83 0h7.66c.28.01.48.1.63.28.14.17.21.38.21.64v1.85c0 .26-.08.48-.23.66-.15.17-.37.26-.66.26-.28 0-.5-.09-.64-.26a1 1 0 01-.21-.66V1.69H5.6v7.58h.5c.25 0 .45.08.6.23.17.16.25.35.25.6s-.08.45-.24.6a.87.87 0 01-.62.22H3.21a.87.87 0 01-.61-.22.78.78 0 01-.24-.6c0-.25.08-.44.24-.6a.85.85 0 01.61-.23h.5V1.7H1.73v1.08c0 .26-.08.48-.23.66-.15.17-.37.26-.66.26-.28 0-.5-.09-.64-.26A1 1 0 010 2.77z"/>
</svg>

Before

Width:  |  Height:  |  Size: 540 B

@ -0,0 +1,45 @@
<template>
<ol>
<li :key="n" v-for="n in amount" :style="style"> </li>
</ol>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardBoxes extends Vue {
@Prop() public readonly params!: string[]
private get amount () {
return parseInt(this.params[0], 10)
}
private get style () {
const size = parseFloat(this.params[1])
return {
width: `calc(${size}em - 0.25em)`,
height: `calc(${size}em - 0.25em)`
}
}
}
</script>
<style scoped>
ol {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
list-style: none;
margin: 0 0 1em 0;
padding: 0;
}
ol > li {
display: inline-block;
width: 1rem;
height: 1rem;
font-size: 1rem;
margin: 0 .3em .3em 0;
border: 0.25em solid var(--highlight-color);
}
</style>

@ -0,0 +1,51 @@
<template>
<ul>
<li v-for="(param, i) in params"
:key="`param${i}`"
v-editable:[i]="editable"
@keydown="handleKey(i, $event)">
{{ param }}
</li>
</ul>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardBulletList extends Vue {
@Prop() public readonly params!: string[]
@Prop() public readonly editable!: boolean
private addEntry (index: number) {
const newParams = [...this.params]
newParams.splice(index + 1, 0, '')
this.$emit('replace', newParams)
}
private removeEntry (index: number) {
const newParams = [...this.params]
newParams.splice(index, 1)
this.$emit('replace', newParams)
}
private handleKey (index: number, event: KeyboardEvent) {
const { key, shiftKey } = event
if (key === 'Enter' && shiftKey) {
event.preventDefault()
this.addEntry(index)
} else if (key === 'Backspace') {
const text = (event.target as HTMLElement).innerText
if (text.trim() === '') this.removeEntry(index)
}
}
}
</script>
<style scoped>
ul {
list-style-position: inside;
margin: 0;
padding-left: .5em;
}
</style>

@ -0,0 +1,22 @@
<template>
<p>
<span v-editable:0="editable" class="title">{{ params[0] }}</span>
<span v-editable:1="editable">{{ params[1] }}</span>
</p>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardDescription extends Vue {
@Prop() public readonly params!: string[]
@Prop() public readonly editable!: boolean
}
</script>
<style scoped>
p { margin: 0; line-height: 1.2; }
p > .title { font-weight: bold; font-style: italic; }
p > .title::after { content: ' '; }
</style>

@ -0,0 +1,73 @@
<template>
<ol>
<li :key="titles[i]" v-for="(v, i) in params">
<span class="title">{{ titles[i] }}</span>
<span class="description">{{ v }} ({{ mod(v) }})</span>
</li>
</ol>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardDndstats extends Vue {
@Prop() public readonly params!: string[]
private titles = ['STR', 'DEX', 'CON', 'INT', 'WIS', 'CHA']
private mod (v: number): string {
switch (v) {
case 1: return '-5'
case 2:
case 3: return '-4'
case 4:
case 5: return '-3'
case 6:
case 7: return '-2'
case 8:
case 9: return '-1'
case 10:
case 11: return '0'
case 12:
case 13: return '+1'
case 14:
case 15: return '+2'
case 16:
case 17: return '+3'
case 18:
case 19: return '+4'
case 20:
case 21: return '+5'
case 22:
case 23: return '+6'
case 24:
case 25: return '+7'
case 26:
case 27: return '+8'
case 28:
case 29: return '+9'
default: return '+10'
}
}
}
</script>
<style scoped>
ol {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
list-style: none;
margin: 0;
padding: 0;
}
ol > li {
display: flex;
flex-flow: column nowrap;
justify-content: center;
font-size: 1rem;
text-align: center;
}
.title { font-weight: bold; }
</style>

@ -0,0 +1,101 @@
<template>
<editor-menu-bar :editor="editor" v-slot="{ commands, isActive, focused }">
<div class="menu-bar" :class="{ active: focused }">
<button class="editor-button-bold" :class="{ active: active.bold }" @click="menuAction('bold', commands.bold, isActive)" />
<button class="editor-button-italic" :class="{ active: active.italic }" @click="menuAction('italic', commands.italic, isActive)" />
<button class="editor-button-paragraph" :class="{ active: active.paragraph }" @click="menuAction('paragraph', commands.paragraph, isActive)" />
<button class="editor-button-heading2" :class="{ active: active.heading2 }" @click="menuAction('heading2', commands.heading({ level: 2}), isActive)" />
<button class="editor-button-heading3" :class="{ active: active.heading3 }" @click="menuAction('heading3', commands.heading({ level: 3}), isActive)" />
<button class="editor-button-bullet-list" :class="{ active: active.bullet_list }" @click="menuAction('bullet_list', commands.bullet_list, isActive)" />
<button class="editor-button-horizontal-rule" :class="{ active: active.horizontal_rule }" @click="menuAction('horizontal_rule', commands.horizontal_rule, isActive)" />
<button class="editor-button-stat-block" :class="{ active: active.stat_block }" @click="menuAction('stat_block', commands.stat_block, isActive)" />
</div>
</editor-menu-bar>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import { Editor, EditorMenuBar } from 'tiptap'
@Component({
components: { EditorMenuBar }
})
export default class DeckCardEditorMenu extends Vue {
@Prop() public readonly editor!: Editor
private active: {[key: string]: boolean} = {
bold: false,
italic: false,
paragraph: false,
heading2: false,
heading3: false,
bulletList: false,
horizontalLule: false
}
private menuAction (name: string, command: () => void, isActive: {[key: string]: () => boolean}) {
command()
Object.keys(this.active).forEach(action => {
this.active[action] = isActive[action]()
})
}
}
</script>
<style scoped>
.card-front > main > .menu-bar {
position: absolute;
width: 70%;
margin: -3rem 0 0 -1rem;
padding: .2rem 1rem;
visibility: hidden;
opacity: 0;
transition: opacity 0.2s, visibility 0.2s;
background-color: var(--highlight-color);
z-index: 2;
}
.card-front > main > .menu-bar.active {
opacity: 1.0;
visibility: visible;
}
.menu-bar > button {
position: relative;
width: 1.6rem;
height: 1.6rem;
margin: 0 .1rem;
background-color: #EEE;
background-position: center;
background-repeat: no-repeat;
background-size: 75%;
border: none;
border-radius: 0;
}
.menu-bar > button:after {
position: absolute;
top: 0;
left: 0;
height: 1.6rem;
width: 1.6rem;
font-size: 1.2rem;
color: black;
}
.menu-bar > button.active {
background-color: #FF0;
}
.editor-button-bold { background-image: url(../assets/zondicons/format-bold.svg); }
.editor-button-italic { background-image: url(../assets/zondicons/format-italic.svg); }
.editor-button-bullet-list { background-image: url(../assets/zondicons/list-bullet.svg); }
.editor-button-heading2:after { content: 'H2'; }
.editor-button-heading3:after { content: 'H3'; }
.editor-button-paragraph:after { content: 'P'; }
.editor-button-horizontal-rule:after { content: '—'; }
.editor-button-stat-block:after { content: 'ST'; }
</style>

@ -1,163 +0,0 @@
<template>
<main ref="cardEl" class="card-content"></main>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import Editor from '@editorjs/editorjs'
import List from '@editorjs/list'
import { Heading, Delimiter, Charges, DnDStats } from '@/editor'
@Component
export default class DeckCardEditor extends Vue {
@Prop() public readonly cardId!: string
@Prop() public readonly active!: boolean
@Prop() public readonly content!: Card['content']
private editor!: Editor
private get id () {
return `${this.cardId}-editor`
}
private mounted () {
this.editor = new Editor({
holder: this.$refs.cardEl as HTMLElement,
autofocus: false,
tools: {
// header: Heading,
list: { class: List, inlineToolbar: true },
heading: { class: Heading, inlineToolbar: true },
delimiter: { class: Delimiter, inlineToolbar: false },
charges: { class: Charges, inlineToolbar: false },
dndstats: { class: DnDStats, inlineToolbar: false }
},
data: this.content,
placeholder: 'Click here to write your card.',
onChange: () => {
console.log('editor change, saving')
this.editor.save().then(value => {
this.$emit('change', { field: 'content', value })
}).catch(error => {
console.error('error saving data', error)
})
}
})
}
}
</script>
<style>
.card-content .cdx-block {
padding: 0;
}
.card-content .ce-paragraph, .card-content p {
margin: 0;
line-height: 1.3;
}
.card-content ul {
list-style-position: inside;
margin: 0;
padding-left: .5em;
}
.card-content li > p {
display: inline;
}
.card-content h2 {
font-size: 1.4rem;
color: var(--highlight-color);
margin: 0;
font-weight: normal;
}
.card-content h3 {
font-size: 1.4rem;
color: var(--highlight-color);
margin: 0 0 .2em 0;
font-weight: normal;
font-variant: small-caps;
line-height: .9em;
border-bottom: 1px solid var(--highlight-color);
}
.card-content .card-delimiter {
height: 0;
margin: .2em 0;
padding: 0;
border: 2px solid var(--highlight-color);
}
.card-content .card-delimiter.pointing-right {
height: 0;
margin: .2em 0;
border-style: solid;
border-width: 2px 0 2px 220px;
border-color: transparent transparent transparent var(--highlight-color);
}
.card-content .card-delimiter.pointing-left {
height: 0;
margin: .2em 0;
border-style: solid;
border-width: 2px 220px 2px 0;
border-color: transparent var(--highlight-color) transparent transparent;
}
.card-content .cdx-list__item {
padding: 0;
line-height: 1.3;
}
.card-content .card-charges-wrapper {
display: flex;
justify-content: flex-start;
align-items: center;
min-height: 1em;
}
.card-content .card-charges-wrapper.card-charges-stretch { justify-content: space-around; }
.card-content .card-charges-wrapper > .card-charge {
width: 1.0em;
height: 1.0em;
border: 2px solid var(--highlight-color);
margin: .5em .2em;
}
.card-content .card-charges-wrapper > .card-charge-circle { border-radius: 100%; }
.card-content .card-charges-wrapper > .card-charge-size-1 { width: 1.0em; height: 1.0em; }
.card-content .card-charges-wrapper > .card-charge-size-2 { width: 1.2em; height: 1.2em; }
.card-content .card-charges-wrapper > .card-charge-size-3 { width: 1.4em; height: 1.4em; }
.card-content .card-charges-wrapper > .card-charge-size-4 { width: 1.6em; height: 1.6em; }
.card-content .card-charges-wrapper > .card-charge-size-5 { width: 1.8em; height: 1.8em; }
.card-content .card-dnd-stats {
display: flex;
flex-flow: row nowrap;
justify-content: space-around;
align-items: center;
color: var(--highlight-color);
}
.card-content .dnd-stat-block {
flex: 1 1 auto;
display: flex;
flex-flow: row wrap;
font-size: .8em;
}
.card-content .dnd-stat-block > .dnd-stat-title {
width: 100%;
font-weight: bold;
text-align: center;
}
.card-content .dnd-stat-block > input {
width: 50%;
background: white;
color: var(--highlight-color);
border: none;
padding: 0;
margin: 0;
font-size: 1em;
text-align: center;
}
.card-content .dnd-stat-block {
}
[contenteditable="true"] { outline: none; }
</style>

@ -0,0 +1,18 @@
<template>
<div :style="{flex: params[0]}"> </div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardFill extends Vue {
@Prop() public readonly params!: string[]
}
</script>
<style scoped>
div {
flex: 1;
}
</style>

@ -0,0 +1,17 @@
<template>
<p v-editable:0="editable">{{ params[0] }}</p>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardNote extends Vue {
@Prop() public readonly params!: string[]
@Prop() public readonly editable!: boolean
}
</script>
<style scoped>
p { margin: 0 0 .5em 0; line-height: 1.2; font-style: italic; }
</style>

@ -0,0 +1,22 @@
<template>
<p>
<span class="title" v-editable:0="editable">{{ params[0] }}</span>
<span class="description" v-editable:1="editable">{{ params[1] }}</span>
</p>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardProperty extends Vue {
@Prop() public readonly params!: string[]
@Prop() public readonly editable!: boolean
}
</script>
<style scoped>
p { margin: 0 0 0 1em; line-height: 1.2; text-indent: -1em; }
p > .title { font-weight: bold; }
p > .title::after { content: ' '; }
</style>

@ -0,0 +1,34 @@
<template>
<hr :class="params[0]" />
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardRule extends Vue {
@Prop() public readonly params!: string[]
}
</script>
<style scoped>
hr {
height: 0;
margin: .2em 0;
border: 2px solid var(--highlight-color);
}
hr.pointing-right {
height: 0;
margin: .2em 0;
border-style: solid;
border-width: 2px 0 2px 220px;
border-color: transparent transparent transparent var(--highlight-color);
}
hr.pointing-left {
height: 0;
margin: .2em 0;
border-style: solid;
border-width: 2px 220px 2px 0;
border-color: transparent var(--highlight-color) transparent transparent;
}
</style>

@ -0,0 +1,25 @@
<template>
<h4 v-editable:0="editable">{{ params[0] }}</h4>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardSection extends Vue {
@Prop() public readonly params!: string[]
@Prop() public readonly editable!: boolean
}
</script>
<style scoped>
h4 {
font-size: 1.4rem;
color: var(--highlight-color);
margin: 0 0 .2em 0;
font-weight: normal;
font-variant: small-caps;
line-height: .9em;
border-bottom: 1px solid var(--highlight-color);
}
</style>

@ -0,0 +1,22 @@
<template>
<h3 v-editable:0="editable">{{ params[0] }}</h3>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardSubtitle extends Vue {
@Prop() public readonly params!: string[]
@Prop() public readonly editable!: boolean
}
</script>
<style scoped>
h3 {
font-size: 1.4rem;
color: var(--highlight-color);
margin: 0;
font-weight: normal;
}
</style>

@ -0,0 +1,17 @@
<template>
<p v-editable:0="editable">{{ params[0] }}</p>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component
export default class DeckCardText extends Vue {
@Prop() public readonly params!: string[]
@Prop() public readonly editable!: boolean
}
</script>
<style scoped>
p { margin: 0 0 .5em 0; line-height: 1.2; }
</style>

@ -10,18 +10,15 @@
<section name="card-front" class="card-front">
<header>
<h1 :contenteditable="isSelection"
@blur="editField('name', $event)"
@keypress.enter.prevent="editField('name', $event)">
{{ card.name }}
</h1>
<img :src="icon" />
</header>
<deck-card-editor
:card-id="card.id"
:active="isSelection"
:content="card.content"
@change="$emit('edit', $event)"
/>
<main>
<deck-card-editor-menu :editor="editor" />
<editor-content :editor="editor" />
</main>
</section>
<section name="card-back" class="card-back">
<div class="icon-wrapper">
@ -36,18 +33,65 @@
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import { cardWHtoStyle, iconPath } from '@/lib'
import DeckCardEditor from '@/components/deck-card-editor.vue'
import { Editor, EditorContent } from 'tiptap'
import {
Heading,
Bold,
Italic,
HorizontalRule,
BulletList,
ListItem,
History,
Table,
TableCell,
TableRow,
TableHeader
} from 'tiptap-extensions'
import StatBlock from '@/editor/stat-block.js'
import DeckCardEditorMenu from '@/components/deck-card-editor-menu.vue'
interface EditorContext {
state: object;
getHTML: () => string;
getJSON: () => ContentNode;
transaction: object;
}
const extensions = [
new Heading({ level: [2, 3] }),
new Bold(),
new Italic(),
new HorizontalRule(),
new BulletList(),
new ListItem(),
new History(),
new StatBlock()
]
@Component({
components: { DeckCardEditor }
components: { EditorContent, DeckCardEditorMenu }
})
export default class DeckCard extends Vue {
@Prop() public readonly card!: Card
@Prop() public readonly deck!: Deck
@Prop() public readonly isSelection!: boolean
/// TODO: onEdit
// this.$emit('edit', { field: 'content', value: doc.content })
private editor = new Editor({ autoFocus: false, extensions })
private mounted () {
this.editor.on('update', ({ getJSON }: EditorContext) => {
const doc = getJSON()
this.$emit('edit', { field: 'content', value: doc.content })
})
this.editor.setContent({
type: 'doc',
content: this.card.content
})
}
private beforeDestroy () {
this.editor.destroy()
}
private editHeadline = false;
private editFieldIndex: number | null = null;
@ -200,7 +244,6 @@ export default class DeckCard extends Vue {
border-radius: 1rem;
font-size: 1.2rem;
color: black;
overflow: hidden;
}
.card-back {
@ -233,3 +276,57 @@ export default class DeckCard extends Vue {
}
}
</style>
<style>
.ProseMirror p {
margin: 0;
line-height: 1.2;
}
.ProseMirror ul {
list-style-position: inside;
margin: 0;
padding-left: .5em;
}
.ProseMirror li > p {
display: inline;
}
.ProseMirror h2 {
font-size: 1.4rem;
color: var(--highlight-color);
margin: 0;
font-weight: normal;
}
.ProseMirror h3 {
font-size: 1.4rem;
color: var(--highlight-color);
margin: 0 0 .2em 0;
font-weight: normal;
font-variant: small-caps;
line-height: .9em;
border-bottom: 1px solid var(--highlight-color);
}
.ProseMirror hr {
height: 0;
margin: .2em 0;
border: 2px solid var(--highlight-color);
}
.ProseMirror hr.pointing-right {
height: 0;
margin: .2em 0;
border-style: solid;
border-width: 2px 0 2px 220px;
border-color: transparent transparent transparent var(--highlight-color);
}
.ProseMirror hr.pointing-left {
height: 0;
margin: .2em 0;
border-style: solid;
border-width: 2px 220px 2px 0;
border-color: transparent var(--highlight-color) transparent transparent;
}
[contenteditable="true"] { outline: none; }
</style>

@ -1,137 +0,0 @@
import { ContentlessBlock, BlockToolArgs } from './contentless-block'
import icon from '../assets/editor/charges.svg.txt'
import iconCircle from '../assets/editor/charges-circle.svg.txt'
const title = 'Charges'
interface ChargesData {
variant: string;
amount: number;
size: number;
stretch: boolean;
}
class Charges extends ContentlessBlock {
static MIN_SIZE = 1
static MAX_SIZE = 5
private _variant: string
private _amount: number
private _size: number
private _stretch: boolean
constructor (args: BlockToolArgs) {
super(args)
console.log('new charges', args)
this._settingButtons = [
{ name: 'box', icon, action: (name: string) => this.setVariant(name) },
{ name: 'more', icon: icon, action: () => this.increaseAmount() },
{ name: 'bigger', icon: icon, action: () => this.increaseSize() },
{ name: 'circle', icon: iconCircle, action: (name: string) => this.setVariant(name) },
{ name: 'less', icon: icon, action: () => this.decreaseAmount() },
{ name: 'smaller', icon: icon, action: () => this.decreaseSize() },
{ name: 'toggle-stretch', icon: icon, action: () => this.toggleStretch() }
]
const { variant, amount, size, stretch } = (args.data || {}) as ChargesData
this._variant = variant || 'box'
this._amount = amount || 5
this._size = size || 1
this._stretch = !(stretch === false)
this._element = this._render()
}
private setVariant (variant: string) {
if (this._variant === variant) return
const charges = Array.from(this._element.children)
charges.forEach(charge => {
charge.classList.remove(`card-charge-${this._variant}`)
charge.classList.add(`card-charge-${variant}`)
})
this._variant = variant
}
private toggleStretch () {
if (this._stretch) this._element.classList.remove('card-charges-stretch')
else this._element.classList.add('card-charges-stretch')
this._stretch = !this._stretch
}
private createCharge (): HTMLElement {
const charge = document.createElement('DIV')
charge.classList.add('card-charge', `card-charge-${this._variant}`, `card-charge-size-${this._size}`)
return charge
}
private increaseAmount () {
this._element.appendChild(this.createCharge())
this._amount++
}
private decreaseAmount () {
const child = this._element.lastElementChild
if (child) {
this._element.removeChild(child)
this._amount--
}
}
private increaseSize () {
if (this._size >= Charges.MAX_SIZE) return
const charges = Array.from(this._element.children)
charges.forEach(charge => {
charge.classList.remove(`card-charge-size-${this._size}`)
charge.classList.add(`card-charge-size-${this._size + 1}`)
})
this._size++
}
private decreaseSize () {
if (this._size <= Charges.MIN_SIZE) return
const charges = Array.from(this._element.children)
charges.forEach(charge => {
charge.classList.remove(`card-charge-size-${this._size}`)
charge.classList.add(`card-charge-size-${this._size - 1}`)
})
this._size--
}
protected _render (): HTMLElement {
const el = document.createElement('DIV')
el.classList.add('card-charges-wrapper', this._CSS.block)
if (this._stretch) el.classList.add('card-charges-stretch')
for (let i = 0; i < this._amount; i++) {
el.appendChild(this.createCharge())
}
console.log('rendered', this._amount, 'charges', el)
return el
}
public save (): ChargesData {
return {
variant: this._variant,
amount: this._amount,
size: this._size,
stretch: this._stretch
}
}
static get toolbox () {
return { icon, title }
}
}
export default Charges

@ -1,222 +0,0 @@
import {
BlockTool,
BlockToolData,
ToolboxConfig,
API,
HTMLPasteEvent,
ToolSettings,
SanitizerConfig
} from '@editorjs/editorjs'
export { HTMLPasteEvent } from '@editorjs/editorjs'
interface PasteConfig {
tags: string[];
}
export interface ContentBlockConfig extends ToolSettings {
placeholder?: string;
}
export interface ContentBlockSettingButton {
name: string;
icon: string;
action: (name: string, event?: MouseEvent) => void; // action triggered by button
isActive?: (name: string) => boolean; // determine if current button is active
}
export type ContentBlockSettings = ContentBlockSettingButton[]
export interface ContentBlockArgs {
api: API;
config?: ContentBlockConfig;
data?: BlockToolData;
}
export interface CSSClasses {
[key: string]: string;
}
export interface ContentBlockData extends BlockToolData {
text?: string;
}
type importFunction = (str: string) => ContentBlockData
type exportFunction = (data: ContentBlockData) => string
export interface ConversionConfig {
import: string | importFunction;
export: string | exportFunction;
}
export class ContentBlock implements BlockTool {
// Default placeholder for Paragraph Tool
static get DEFAULT_PLACEHOLDER (): string {
return ''
}
static _supportedTags: string[] = []
static _toolboxConfig: ToolboxConfig = {
icon: '<svg></svg>',
title: 'UnnamedContentPlugin'
}
protected _defaultPlaceholder (): string {
return ContentBlock.DEFAULT_PLACEHOLDER
}
protected api: API
protected _element: HTMLElement
protected _data: ContentBlockData
protected _config: ContentBlockConfig
protected _placeholder: string
protected _CSS: CSSClasses = {}
protected onKeyUp: (event: KeyboardEvent) => void
protected _settingButtons: ContentBlockSettings = []
constructor ({ data, config, api }: ContentBlockArgs) {
this.api = api
this._config = config as ContentBlockConfig
this._CSS.block = this.api.styles.block
this.onKeyUp = (event: KeyboardEvent) => this._onKeyUp(event)
// Placeholder it is first Block
this._placeholder = config?.placeholder ? config.placeholder : this._defaultPlaceholder()
this._data = data as ContentBlockData
this._element = this._render()
}
// Check if text content is empty and set empty string to inner html.
// We need this because some browsers (e.g. Safari) insert <br> into empty contenteditanle elements
_onKeyUp (event: KeyboardEvent) {
if (event.code !== 'Backspace' && event.code !== 'Delete') return
if (this._element.textContent === '') {
this._element.innerHTML = ''
}
}
// render tool view
// whenever a redraw is needed the result is saved in this._element
protected _render (): HTMLElement {
const el = document.createElement('DIV')
el.classList.add(this._CSS.block)
el.dataset.placeholder = this._placeholder
el.addEventListener('keyup', this.onKeyUp)
el.innerHTML = this.data.text || ''
el.contentEditable = 'true'
return el
}
// Return Tool's view
public render (): HTMLElement {
return this._element
}
// Method that specified how to merge two Text blocks.
// Called by Editor.js by backspace at the beginning of the Block
public merge (data: ContentBlockData) {
this.data = {
text: (this.data.text || '') + data.text
}
}
// Validate Paragraph block data (by default checks for emptiness)
public validate (savedData: ContentBlockData): boolean {
if (!savedData.text) return false
return savedData.text.trim() !== ''
}
// Extract Tool's data from the view
public save (toolsContent: HTMLElement): ContentBlockData {
return {
text: toolsContent.innerHTML
}
}
public get CSS (): CSSClasses {
return this._CSS
}
/**
* Enable Conversion Toolbar. Paragraph can be converted to/from other tools
*/
static get conversionConfig (): ConversionConfig {
return {
export: 'text', // to convert Paragraph to other block, use 'text' property of saved data
import: 'text' // to covert other block's exported string to Paragraph, fill 'text' property of tool data
}
}
// Sanitizer rules
static get sanitize (): SanitizerConfig {
return {
text: { br: true }
}
}
get data (): ContentBlockData {
const text = this._element?.innerHTML
if (text !== undefined) this._data.text = text
if (this._data.text === undefined) this._data.text = ''
return this._data
}
set data (data: ContentBlockData) {
this._data = data || {}
this._element.innerHTML = this._data.text || ''
}
public renderSettings (): HTMLElement {
const wrapper = document.createElement('DIV')
this._settingButtons.forEach(tune => {
// make sure the settings button does something
if (!tune.icon || typeof tune.action !== 'function') return
const { name, icon, action, isActive } = tune
const btn = document.createElement('SPAN')
btn.classList.add(this.api.styles.settingsButton)
if (typeof isActive === 'function' && isActive(name)) {
btn.classList.add(this.api.styles.settingsButtonActive)
}
btn.innerHTML = icon
btn.addEventListener('click', event => action(name, event))
wrapper.appendChild(btn)
})
return wrapper
}
// Used by Editor.js paste handling API.
// Provides configuration to handle the tools tags.
static get pasteConfig (): PasteConfig {
return {
tags: this._supportedTags
}
}
// overwrite this if you need special handling of paste data
protected pasteHandler (element: HTMLElement): ContentBlockData {
return { text: element.innerText }
}
// On paste callback fired from Editor.
public onPaste (event: HTMLPasteEvent) {
const element = event.detail.data
this.data = this.pasteHandler(element)
}
// Icon and title for displaying at the Toolbox
static get toolbox (): ToolboxConfig {
return this._toolboxConfig
}
}
export default ContentBlock

@ -1,73 +0,0 @@
import { BlockTool, BlockToolData, ToolSettings, ToolboxConfig, API } from '@editorjs/editorjs'
import { ContentBlockSettings, CSSClasses } from './content-block'
export interface BlockToolArgs {
api: API;
config?: ToolSettings;
data?: BlockToolData;
}
export class ContentlessBlock implements BlockTool {
static get contentless () {
return true
}
protected api: API
protected _element: HTMLElement
protected _data: object
protected _config: ToolSettings
protected _CSS: CSSClasses = {}
protected _settingButtons: ContentBlockSettings = []
constructor ({ data, config, api }: BlockToolArgs) {
this.api = api
this._config = config as ToolSettings
this._data = data || {}
this._CSS.block = this.api.styles.block
this._element = this._render()
}
protected _render (): HTMLElement {
const el = document.createElement('DIV')
el.classList.add(this._CSS.block)
return el
}
public render (): HTMLElement {
return this._element
}
public save (_toolsContent: HTMLElement): object {
return {}
}
public renderSettings (): HTMLElement {
const wrapper = document.createElement('DIV')
this._settingButtons.forEach(tune => {
// make sure the settings button does something
if (!tune.icon || typeof tune.action !== 'function') return
const { name, icon, action, isActive } = tune
const btn = document.createElement('SPAN')
btn.classList.add(this.api.styles.settingsButton)
if (typeof isActive === 'function' && isActive(name)) {
btn.classList.add(this.api.styles.settingsButtonActive)
}
btn.innerHTML = icon
btn.addEventListener('click', event => action(name, event))
wrapper.appendChild(btn)
})
return wrapper
}
static get toolbox (): ToolboxConfig {
return { icon: '<svg></svg>', title: 'UnnamedPlugin' }
}
}
export default ContentlessBlock

@ -1,53 +0,0 @@
import { ContentlessBlock, BlockToolArgs } from './contentless-block'
import icon from '../assets/editor/delimiter.svg.txt'
import iconR from '../assets/editor/delimiter_r.svg.txt'
import iconL from '../assets/editor/delimiter_l.svg.txt'
const title = 'Delimiter'
interface DelimiterData {
variant: string;
}
class Delimiter extends ContentlessBlock {
private _variant = 'none'
constructor (args: BlockToolArgs) {
super(args)
this._settingButtons = [
{ name: 'straight', icon, action: (name: string) => this.setDelimiterType(name) },
{ name: 'pointing-left', icon: iconL, action: (name: string) => this.setDelimiterType(name) },
{ name: 'pointing-right', icon: iconR, action: (name: string) => this.setDelimiterType(name) }
]
const { variant } = (args.data || {}) as DelimiterData
if (variant) this.setDelimiterType(variant)
}
private setDelimiterType (name: string) {
this._element.classList.remove('pointing-left')
this._element.classList.remove('pointing-right')
this._variant = 'none'
if (name === 'pointing-left' || name === 'pointing-right') {
this._variant = name
this._element.classList.add(name)
}
}
protected _render (): HTMLElement {
const el = document.createElement('HR')
el.classList.add('card-delimiter', this._CSS.block)
return el
}
public save (): DelimiterData {
return {
variant: this._variant
}
}
static get toolbox () {
return { icon, title }
}
}
export default Delimiter

@ -1,106 +0,0 @@
import { ContentlessBlock, BlockToolArgs } from './contentless-block'
import icon from '../assets/editor/charges-circle.svg.txt'
const title = 'DnDStats'
interface DnDStatsData {
text: string;
}
class DnDStats extends ContentlessBlock {
static _toolboxConfig = { icon, title }
private _stats = [10, 10, 10, 10, 10, 10]
constructor (args: BlockToolArgs) {
super(args)
this.data = args.data as DnDStatsData
this._element = this._render()
}
public get data () {
return {
text: this._stats.join(',')
}
}
public set data (data: DnDStatsData) {
if (data.text === undefined) data.text = ''
const newStats = data.text.split(',')
.map(x => parseInt(x, 10))
.filter(x => !Number.isNaN(x))
while (newStats.length < 6) newStats.push(10) // fill missing stats
this._stats = newStats
}
// creates a random four character long id
private randomId (): string {
const min = 46656 // '1000'
const max = 1679615 /* 'zzzz' */ - 46656 /* '1000' */
return (min + Math.floor(max * Math.random())).toString(36)
}
private renderStatMod (value: number): string {
const mod = Math.floor((value - 10) / 2.0)
const sign = mod < 0 ? '' : '+'
return ` (${sign}${mod})`
}
private createStatBlock (title: string, value: number, changeHandler: (newValue: number) => void): HTMLElement {
const id = `dnd-stat-${title}-${this.randomId()}`
const labelWrapper = document.createElement('label')
const titleEl = document.createElement('span')
const statInputEl = document.createElement('input')
const statModEl = document.createElement('span')
// should allow focussing block with tab
labelWrapper.setAttribute('z-index', '1')
labelWrapper.classList.add('dnd-stat-block')
labelWrapper.setAttribute('for', id)
titleEl.classList.add('dnd-stat-title')
titleEl.innerText = title
statInputEl.id = id
statInputEl.value = `${value}`
statInputEl.addEventListener('input', () => {
const value = parseInt(statInputEl.value, 10)
statModEl.innerText = this.renderStatMod(value)
changeHandler(value)
})
statModEl.innerText = this.renderStatMod(value)
labelWrapper.appendChild(titleEl)
labelWrapper.appendChild(statInputEl)
labelWrapper.appendChild(statModEl)
return labelWrapper
}
protected _render (): HTMLElement {
const el = document.createElement('div')
el.classList.add('card-dnd-stats')
const stats = this._stats || [10, 10, 10, 10, 10, 10]
const titles = ['STR', 'DEX', 'CON', 'INT', 'WIS', 'CHA']
stats.forEach((stat, i) => {
const title = titles[i]
const block = this.createStatBlock(title, stat, newValue => {
this._stats[i] = newValue
})
el.appendChild(block)
})
return el
}
public save (): DnDStatsData {
return this.data
}
}
export default DnDStats

@ -1,159 +0,0 @@
import {
ContentBlock,
ContentBlockArgs,
ContentBlockConfig,
ContentBlockData
} from './content-block'
import icon from '../assets/editor/header.svg.txt'
import icon1 from '../assets/editor/header1.svg.txt'
import icon2 from '../assets/editor/header2.svg.txt'
import icon3 from '../assets/editor/header3.svg.txt'
import icon4 from '../assets/editor/header4.svg.txt'
import icon5 from '../assets/editor/header5.svg.txt'
import icon6 from '../assets/editor/header6.svg.txt'
const title = 'Heading'
enum HeadingLevel {
One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6
}
const icons = [null, icon1, icon2, icon3, icon4, icon5, icon6]
interface HeadingConfig extends ContentBlockConfig {
placeholder?: string;
levels?: HeadingLevel[];
defaultLevel?: HeadingLevel;
}
interface HeadingData extends ContentBlockData {
text: string;
level?: HeadingLevel;
}
class Heading extends ContentBlock {
static _supportedTags = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']
static _toolboxConfig = { icon, title }
protected _config: HeadingConfig
private defaultLevel: HeadingLevel
private currentLevel: HeadingLevel
constructor (args: ContentBlockArgs) {
super(args)
this._config = args.config as HeadingConfig
if (this._config.levels === undefined) {
this._config.levels = [HeadingLevel.Two, HeadingLevel.Three]
}
if (this._config.defaultLevel === undefined) {
this._config.defaultLevel = HeadingLevel.Two
}
if (this._config.levels.indexOf(this._config.defaultLevel) === -1) {
console.warn('(ง\'̀-\'́)ง Heading Tool: the default level specified was not found in available levels')
}
this.defaultLevel = this._config.defaultLevel
this.currentLevel = this.defaultLevel
// setting data will rerender the element with the right settings
this.data = {
level: this.currentLevel,
text: (args.data as HeadingData).text || ''
}
this._settingButtons = this._config.levels.map(level => {
return {
name: `H${level}`,
icon: icons[level] || icon,
action: (name: string) => this.setLevel(name),
isActive: (name: string): boolean => this.isCurrentLevel(name)
}
})
}
public get data (): HeadingData {
return this._data as HeadingData
}
public set data (data: HeadingData) {
const currentData = this._data as HeadingData
if (data.level === undefined) data.level = currentData.level || this.defaultLevel
if (data.text === undefined) data.text = currentData.text || ''
this._data = data
this.currentLevel = data.level
const newHeader = this._render()
if (this._element.parentNode) {
this._element.parentNode.replaceChild(newHeader, this._element)
}
this._element = newHeader
}
private isCurrentLevel (name: string): boolean {
const currentLevel = `H${this.currentLevel}`
return name === currentLevel
}
private setLevel (name: string) {
const level = parseInt(name[1], 10)
this.data = { level, text: this._element.innerHTML }
}
protected _render (): HTMLElement {
const el = document.createElement(`H${this.currentLevel}`)
el.innerHTML = this.data.text || ''
el.classList.add(this._CSS.block)
el.contentEditable = 'true'
el.dataset.placeholder = this._config.placeholder || ''
return el
}
// Handle pasted H1-H6 tags to substitute with header tool
protected pasteHandler (element: HTMLHeadingElement): HeadingData {
const text = element.innerHTML
let level = this.defaultLevel
const tagMatch = element.tagName.match(/H(\d)/)
if (tagMatch) level = parseInt(tagMatch[1], 10)
// Fallback to nearest level when specified not available
if (this._config.levels) {
level = this._config.levels.reduce((prevLevel, currLevel) => {
return Math.abs(currLevel - level) < Math.abs(prevLevel - level) ? currLevel : prevLevel
})
}
return { level, text }
}
// Method that specified how to merge two Text blocks.
// Called by Editor.js by backspace at the beginning of the Block
public merge (data: HeadingData) {
this.data = {
text: this.data.text + (data.text || ''),
level: this.data.level
}
}
// extract tools data from view
public save (toolsContent: HTMLElement): HeadingData {
return {
text: toolsContent.innerHTML,
level: this.currentLevel
}
}
static get sanitize () {
return { level: {} }
}
}
export default Heading

@ -1,4 +0,0 @@
export { default as Delimiter } from './delimiter'
export { default as Heading } from './heading'
export { default as Charges } from './charges'
export { default as DnDStats } from './dnd-stats'

@ -0,0 +1,53 @@
import { Node } from 'tiptap'
import { tableNodes, tableEditing, goToNextCell, deleteTable } from 'prosemirror-tables'
import { createTable } from 'prosemirror-utils'
import { TextSelection } from 'prosemirror-state'
export default class StatBlock extends Node {
get name () {
return 'stat_block'
}
get defaultOptions () {
return {
resizable: false
}
}
get schema () {
return {
group: 'block',
content: 'stat_column+',
toDOM: () => ['ol', { 'data-type': this.name }, 0],
parseDOM: [{
priority: 51,
tag: `[data-type="${this.name}"]`
}]
}
}
commands ({ schema }) {
return () => (state, dispatch) => {
const offset = state.tr.selection.anchor + 1
const nodes = createTable(schema, 2, 6, true)
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView()
const resolvedPos = tr.doc.resolve(offset)
tr.setSelection(TextSelection.near(resolvedPos))
dispatch(tr)
}
}
keys () {
return {
Tab: goToNextCell(1),
'Shift-Tab': goToNextCell(-1)
}
}
get plugins () {
return [tableEditing()]
}
}

@ -0,0 +1,49 @@
import { Node } from 'tiptap'
import { tableEditing, goToNextCell, deleteTable } from 'prosemirror-tables'
import { createTable } from 'prosemirror-utils'
import { TextSelection } from 'prosemirror-state'
import { TableNodes } from 'tiptap-extensions'
export default class StatBlock extends Node {
public get name () {
return 'stat_block'
}
public get defaultOptions () {
return {
resizable: false
}
}
public get schema () {
return TableNodes.table
}
public commands ({ schema }) {
return {
createStatBlock: () => (state, dispatch) => {
const offset = state.tr.selection.anchor + 1
const nodes = createTable(schema, 2, 6, true)
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView()
const resolvedPos = tr.doc.resolve(offset)
tr.setSelection(TextSelection.near(resolvedPos))
dispatch(tr)
},
deleteTable: () => deleteTable
}
}
public keys () {
return {
Tab: goToNextCell(1),
'Shift-Tab': goToNextCell(-1)
}
}
public get plugins () {
return [tableEditing()]
}
}

@ -41,14 +41,10 @@ export function defaultDeck (): Deck {
export function defaultCard (): Card {
return {
id: `c${randomId()}`,
name: 'no title yet',
name: '',
count: 1,
tags: [],
icon: 'robe',
content: {
time: Date.now(),
blocks: [],
version: '2.17.0'
}
content: []
}
}

12
src/modules.d.ts vendored

@ -1,12 +0,0 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
declare module '*.txt' {
const content: string
export default content
}
declare module '@editorjs/paragraph'
declare module '@editorjs/list'

@ -0,0 +1,104 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */
declare module 'tiptap-extensions' {
import { Node, Mark, Extension } from 'tiptap'
export class Heading extends Node {
[key: string]: any
}
export class Blockquote extends Node {
[key: string]: any
}
export class BulletList extends Node {
[key: string]: any
}
export class Code extends Node {
[key: string]: any
}
export class CodeBlock extends Node {
[key: string]: any
}
export class CodeBlockHighlight extends Node {
[key: string]: any
}
export class HardBreak extends Node {
[key: string]: any
}
export class HorizontalRule extends Node {
[key: string]: any
}
export class Image extends Node {
[key: string]: any
}
export class ListItem extends Node {
[key: string]: any
}
export class Mention extends Node {
[key: string]: any
}
export class OrderedList extends Node {
[key: string]: any
}
export class Table extends Node {
[key: string]: any
}
export class TableCell extends Node {
[key: string]: any
}
export class TableHeader extends Node {
[key: string]: any
}
export class TableNodes extends Node {
[key: string]: any
}
export class TableRow extends Node {
[key: string]: any
}
export class TodoItem extends Node {
[key: string]: any
}
export class TodoList extends Node {
[key: string]: any
}
export class Link extends Mark {
[key: string]: any
}
export class Bold extends Mark {
[key: string]: any
}
export class Italic extends Mark {
[key: string]: any
}
export class Strike extends Mark {
[key: string]: any
}
export class Underline extends Mark {
[key: string]: any
}
export class Collaboration extends Extension {
[key: string]: any
}
export class Focus extends Extension {
[key: string]: any
}
export class History extends Extension {
[key: string]: any
}
export class Placeholder extends Extension {
[key: string]: any
}
export class Search extends Extension {
[key: string]: any
}
export class TrailingNode extends Extension {
[key: string]: any
}
export class Suggestions {
[key: string]: any
}
export class Highlight {
[key: string]: any
}
}

@ -0,0 +1,56 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */
declare module 'tiptap' {
import { Vue } from 'vue/types/vue'
export class Emitter {
on(event: any, fn: (...args: any[]) => void): Emitter
emit(event: any, ...args: any[]): Emitter
off(event: any, fn: (...args: any[]) => void): Emitter
}
export class Editor extends Emitter {
constructor (...arg: any[])
[key: string]: any
}
export class Extension {
constructor(...arg: any[])
init(): null
bindEditor(editor: Editor): void
get name(): string|null
get type(): string
get update(): object
get defaultOptions(): object
get plugins(): any[]
inputRules(): any[]
pasteRules(): any[]
keys(): object
}
export class Node extends Extension {
get type(): 'node'
get view(): null
get schema(): null
command(): () => object
}
export class Mark extends Extension {
get type(): 'mark'
get view(): null
get schema(): null
command(): () => object
}
export class EditorMenuBubble extends Vue {
[key: string]: any
}
export class EditorContent extends Vue {
[key: string]: any
}
export class EditorMenuBar extends Vue {
[key: string]: any
}
export class EditorFloatingMenu extends Vue {
[key: string]: any
}
}

@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}

@ -1,16 +1,16 @@
interface KV<V> {
[key: string]: V;
interface TextNode {
type: 'text';
text: string;
}
interface ContentBlock {
interface SimpleNode {
type: string;
data: object;
}
interface CardContent {
time: number;
blocks: ContentBlock[];
version: string;
interface ContentNode {
type: string;
content: (ContentNode | TextNode | SimpleNode)[];
attrs?: object;
}
interface Card {
@ -19,7 +19,7 @@ interface Card {
count: number;
tags: string[];
icon: string;
content: CardContent;
content: (ContentNode | SimpleNode)[];
backIcon?: string;
color?: string;
}

@ -86,29 +86,23 @@ export default class DeckView extends Vue {
if (this.deck === null) return
const newCard = defaultCard()
newCard.content = {
time: Date.now(),
blocks: [{
newCard.name = 'Click here to add an awesome title'
newCard.content = [{
type: 'heading',
data: {
text: 'Next Level RPG Card',
level: 2
}
}, {
type: 'delimiter',
data: { variant: 'pointing-left' }
}, {
type: 'paragraph',
data: { text: 'This card is a rich text editor so you can basically do whatever you want.' }
attrs: { level: 2 },
content: [{
type: 'text',
text: 'feel free to edit this card'
}]
}, {
type: 'paragraph',
data: { text: ' ' }
type: 'horizontal_rule'
}, {
type: 'paragraph',
data: { text: 'You see that delimiter over there? It seems to be wrong, or maybe you like it that way. In any way you can change it by clicking on it and then on the little tool button on the right.' }
}],
version: '2.17.0'
}
content: [{
type: 'text',
text: 'This is a rich-text editor, so you can basically do whatever you want.'
}]
}]
this.deck.cards.push(newCard)
this.$storage.persist()

@ -1,14 +1,6 @@
<template>
<main name="home" :class="{ popup }">
<header>RPG Cards for y'all</header>
<section name="notifications">
<p class="warning">
<strong>This is a pre-alpha version.</strong>
Many features are still unstable or completely missing.
<br />
Check out <a href="https://github.com/nkoehring/rpg-cards-ng/">the code repository</a> for more information.
</p>
</section>
<section name="deck-covers" class="cards" :class="{ centered: !savedDecks.length }">
<router-link :to="{ name: 'Deck', params: { id: deck.id } }" :key="deck.id" v-for="deck in savedDecks">
<deck-cover :deck="deck" />

@ -1,10 +0,0 @@
module.exports = {
chainWebpack: config => {
config.module
.rule('raw')
.test(/\.txt$/)
.use('raw-loader')
.loader('raw-loader')
.end()
}
}

@ -770,19 +770,6 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@editorjs/editorjs@^2.17.0":
version "2.17.0"
resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.17.0.tgz#38f20d7f99bc21868904b6b937905b6daad5a2a2"
integrity sha512-5rMjZLdiFOiUGESe5MZagvuVaLggORXBEolbbDLLVWHslR+r4+TACOXBcN8A6m9hMmnpHIJsC3442MZEWdNfQA==
dependencies:
codex-notifier "^1.1.2"
codex-tooltip "^1.0.0"
"@editorjs/list@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@editorjs/list/-/list-1.4.0.tgz#e92459a8ac2305bc4385245e329c8b5c8437456a"
integrity sha512-iYDXGbVXvsAJbSxbjFMP4p7kS1zhQyNDqVNzkfMRhItulzKYlOMlFjTIGHqu5SxPy6NrcckhVFaWdfGDn5/gEA==
"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@ -2372,16 +2359,6 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codex-notifier@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/codex-notifier/-/codex-notifier-1.1.2.tgz#a733079185f4c927fa296f1d71eb8753fe080895"
integrity sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==
codex-tooltip@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/codex-tooltip/-/codex-tooltip-1.0.0.tgz#720353b27fadc40f2d054d171479b016ffcb63ea"
integrity sha512-Wa/p/om166GVjg+q436BERBZZz3yvTnCDDzMV2kjKIzsUkj6vCWphTSTo+M0QJRfwODKzhXYaw8+S4EXPW6r0g==
collection-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@ -3820,6 +3797,13 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
fault@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13"
integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==
dependencies:
format "^0.2.0"
faye-websocket@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
@ -4040,6 +4024,11 @@ form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"
format@^0.2.0:
version "0.2.2"
resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@ -4401,6 +4390,11 @@ highlight.js@^9.6.0:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c"
integrity sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==
highlight.js@~9.16.0:
version "9.16.2"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.16.2.tgz#68368d039ffe1c6211bcc07e483daf95de3e403e"
integrity sha512-feMUrVLZvjy0oC7FVJQcSQRqbBq9kwqnYE4+Kj9ZjbHh3g+BisiPgF49NyQbVLNdrL/qqZr3Ca9yOKwgn2i/tw==
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@ -5547,6 +5541,14 @@ lower-case@^1.1.1:
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
lowlight@1.13.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.13.0.tgz#9b4fd00559985e40e11c916ccab14c7c0cf4320d"
integrity sha512-bFXLa+UO1eM3zieFAcNqf6rTQ1D5ERFv64/euQbbH/LT3U9XXwH6tOrgUAGWDsQ1QgN3ZhgOcv8p3/S+qKGdTQ==
dependencies:
fault "^1.0.2"
highlight.js "~9.16.0"
lru-cache@^4.0.1, lru-cache@^4.1.2:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@ -6257,6 +6259,11 @@ ora@^3.4.0:
strip-ansi "^5.2.0"
wcwidth "^1.0.1"
orderedmap@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.1.tgz#c618e77611b3b21d0fe3edc92586265e0059c789"
integrity sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ==
original@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
@ -7022,6 +7029,137 @@ promise-inflight@^1.0.1:
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
prosemirror-collab@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/prosemirror-collab/-/prosemirror-collab-1.2.2.tgz#8d2c0e82779cfef5d051154bd0836428bd6d9c4a"
integrity sha512-tBnHKMLgy5Qmx9MYVcLfs3pAyjtcqYYDd9kp3y+LSiQzkhMQDfZSV3NXWe4Gsly32adSef173BvObwfoSQL5MA==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-commands@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.1.2.tgz#6868cabc9f9112fba94c805139473527774b0dea"
integrity sha512-JBa06kjgX67d9JVUVJbCkxwvSGtQnWAN/85nq9csOMS5Z9WZLEvVDtVvZranNlu8l/XNVBWrZxOOK+pB03eTfA==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-dropcursor@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.3.2.tgz#28738c4ed7102e814d7a8a26d70018523fc7cd6d"
integrity sha512-4c94OUGyobGnwcQI70OXyMhE/9T4aTgjU+CHxkd5c7D+jH/J0mKM/lk+jneFVKt7+E4/M0D9HzRPifu8U28Thw==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0"
prosemirror-view "^1.1.0"
prosemirror-gapcursor@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.1.2.tgz#a1400a86a51d4cccc065e68d5625a9fb5bc623e0"
integrity sha512-Z+eqk6RysZVxidGWN5aWoSTbn5bTHf1XZ+nQJVwUSdwdBVkfQMFdTHgfrXA8W5MhHHdNg/EEEYG3z3Zi/vE2QQ==
dependencies:
prosemirror-keymap "^1.0.0"
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-view "^1.0.0"
prosemirror-history@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.1.2.tgz#3e8f11efbd316e98322028be67549df1f94fc6da"
integrity sha512-erhxYS5gm/6MiXP8jUoJBgc8IbaqjHDVPl9KGg5JrMZOSSOwHv85+4Fb0Q7sYtv2fYwAjOSw/kSA9vkxJ6wOwA==
dependencies:
prosemirror-state "^1.2.2"
prosemirror-transform "^1.0.0"
rope-sequence "^1.3.0"
prosemirror-inputrules@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.1.2.tgz#487e46c763e1212a4577397aba7706139084f012"
integrity sha512-Ja5Z3BWestlHYGvtSGqyvxMeB8QEuBjlHM8YnKtLGUXMDp965qdDV4goV8lJb17kIWHk7e7JNj6Catuoa3302g==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-keymap@1.1.3, prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.1.3.tgz#be22d6108df2521608e9216a87b1a810f0ed361e"
integrity sha512-PRA4NzkUMzV/NFf5pyQ6tmlIHiW/qjQ1kGWUlV2rF/dvlOxtpGpTEjIMhWgLuMf+HiDEFnUEP7uhYXu+t+491g==
dependencies:
prosemirror-state "^1.0.0"
w3c-keyname "^2.2.0"
prosemirror-model@1.8.2, prosemirror-model@^1.0.0, prosemirror-model@^1.1.0, prosemirror-model@^1.8.1:
version "1.8.2"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.8.2.tgz#c74eaacb0bbfea49b59a6d89fef5516181666a56"
integrity sha512-piffokzW7opZVCjf/9YaoXvTC0g7zMRWKJib1hpphPfC+4x6ZXe5CiExgycoWZJe59VxxP7uHX8aFiwg2i9mUQ==
dependencies:
orderedmap "^1.1.0"
prosemirror-schema-list@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.2.tgz#310809209094b03425da7f5c337105074913da6c"
integrity sha512-dgM9PwtM4twa5WsgSYMB+J8bwjnR43DAD3L9MsR9rKm/nZR5Y85xcjB7gusVMSsbQ2NomMZF03RE6No6mTnclQ==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-state@1.3.2, prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.3.2.tgz#1b910b0dc01c1f00926bb9ba1589f7b7ac0d658b"
integrity sha512-t/JqE3aR0SV9QrzFVkAXsQwsgrQBNs/BDbcFH20RssW0xauqNNdjTXxy/J/kM7F+0zYi6+BRmz7cMMQQFU3mwQ==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-tables@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.0.0.tgz#ec3d0b11e638c6a92dd14ae816d0a2efd1719b70"
integrity sha512-zFw5Us4G5Vdq0yIj8GiqZOGA6ud5UKpMKElux9O0HrfmhkuGa1jf1PCpz2R5pmIQJv+tIM24H1mox/ODBAX37Q==
dependencies:
prosemirror-keymap "^1.1.2"
prosemirror-model "^1.8.1"
prosemirror-state "^1.3.1"
prosemirror-transform "^1.2.1"
prosemirror-view "^1.13.3"
prosemirror-transform@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.2.2.tgz#4439ae7e88ea1395d9beed6a4cd852d72b16ed2f"
integrity sha512-expO11jAsxaHk2RdZtzPsumc1bAAZi4UiXwTLQbftsdnIUWZE5Snyag595p1lx/B8QHUZ6tYWWOaOkzXKoJmYw==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.2.4.tgz#8d5843834f5ccedfb614faa9220672bb4834b00a"
integrity sha512-0A668uf0EN89L9O9brE05kHcqp7FHmT5YN7Tom58Kj926QqOBs7iNRHDLWxrSaQB5MNZtzDOD9T3EyJ88YDcBg==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-utils@0.9.6:
version "0.9.6"
resolved "https://registry.yarnpkg.com/prosemirror-utils/-/prosemirror-utils-0.9.6.tgz#3d97bd85897e3b535555867dc95a51399116a973"
integrity sha512-UC+j9hQQ1POYfMc5p7UFxBTptRiGPR7Kkmbl3jVvU8VgQbkI89tR/GK+3QYC8n+VvBZrtAoCrJItNhWSxX3slA==
prosemirror-view@1.13.4:
version "1.13.4"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.13.4.tgz#01d873db7731e0aacc410a9038447d1b7536fd07"
integrity sha512-mtgWEK16uYQFk3kijRlkSpAmDuy7rxYuv0pgyEBDmLT1PCPY8380CoaYnP8znUT6BXIGlJ8oTveK3M50U+B0vw==
dependencies:
prosemirror-model "^1.1.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0"
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3:
version "1.14.4"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.14.4.tgz#dccfdc6fc5d0206d0b0b7a6abb688d3f741406d6"
integrity sha512-qPlKWigymKio7Ed52E+mwZEn2TJE28u8Masj0ygGHYZRhY/abB/s0XEGLwx2FTsvt92gIqfRRezhUzTk3eE2rQ==
dependencies:
prosemirror-model "^1.1.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0"
proxy-addr@~2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
@ -7165,14 +7303,6 @@ raw-body@2.4.0:
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-loader@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.0.tgz#d639c40fb9d72b5c7f8abc1fb2ddb25b29d3d540"
integrity sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==
dependencies:
loader-utils "^1.2.3"
schema-utils "^2.5.0"
read-pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
@ -7508,6 +7638,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
rope-sequence@^1.3.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.2.tgz#a19e02d72991ca71feb6b5f8a91154e48e3c098b"
integrity sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==
run-async@^2.2.0, run-async@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8"
@ -8338,6 +8473,63 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
tiptap-commands@^1.12.5:
version "1.12.5"
resolved "https://registry.yarnpkg.com/tiptap-commands/-/tiptap-commands-1.12.5.tgz#d2bd1adcc79fe47938bd64b91b6dec4688a88086"
integrity sha512-wzQCH3CL1VWy6E47Hy+9kt882w7SND+FD9e9xAAsYhG/QI0cmuvAf/8doZZhUmYwkraYeF7/2bU04IXr36t44Q==
dependencies:
prosemirror-commands "1.1.2"
prosemirror-inputrules "1.1.2"
prosemirror-model "1.8.2"
prosemirror-schema-list "1.1.2"
prosemirror-state "1.3.2"
prosemirror-tables "1.0.0"
prosemirror-utils "0.9.6"
tiptap-utils "^1.8.3"
tiptap-extensions@^1.28.6:
version "1.28.6"
resolved "https://registry.yarnpkg.com/tiptap-extensions/-/tiptap-extensions-1.28.6.tgz#ce3ca3ccd9c9658e749c5aa5e97255a70468eaff"
integrity sha512-hkYKJHxkqmeIRyuOaVvsrC/IcoXZmBw/Gx4JJHPiCAKvLQyjgYZpNjbVH9nIgdlxLvVjFDVBoPWGHy00qGp8qQ==
dependencies:
lowlight "1.13.0"
prosemirror-collab "1.2.2"
prosemirror-history "1.1.2"
prosemirror-model "1.8.2"
prosemirror-state "1.3.2"
prosemirror-tables "1.0.0"
prosemirror-transform "1.2.2"
prosemirror-utils "0.9.6"
prosemirror-view "1.13.4"
tiptap "^1.26.6"
tiptap-commands "^1.12.5"
tiptap-utils@^1.8.3:
version "1.8.3"
resolved "https://registry.yarnpkg.com/tiptap-utils/-/tiptap-utils-1.8.3.tgz#fdfc8f7888f6e9ed0dae081f5f66b9f5429608a9"
integrity sha512-SgqDTCA5ux17KKTpEV2YC54ugBWU2jzpiFlCmVckPjYl5BhmOwuJ1Q5H/8v/XGcnHDqP31Ui4lk31Vts4NmtTA==
dependencies:
prosemirror-model "1.8.2"
prosemirror-state "1.3.2"
prosemirror-tables "1.0.0"
prosemirror-utils "0.9.6"
tiptap@^1.26.6:
version "1.26.6"
resolved "https://registry.yarnpkg.com/tiptap/-/tiptap-1.26.6.tgz#b287fa7cb1c20690868aee8dc526de5390b054f0"
integrity sha512-U5qyYZi5IH7LhYwYrStRBp5MxF5MiGFLt9ogOAF/0N/LIg0XwVwe/AaSx0UH/s4dY7R8OvEa9u4qimO08Wp1LA==
dependencies:
prosemirror-commands "1.1.2"
prosemirror-dropcursor "1.3.2"
prosemirror-gapcursor "1.1.2"
prosemirror-inputrules "1.1.2"
prosemirror-keymap "1.1.3"
prosemirror-model "1.8.2"
prosemirror-state "1.3.2"
prosemirror-view "1.13.4"
tiptap-commands "^1.12.5"
tiptap-utils "^1.8.3"
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@ -8821,6 +9013,11 @@ vue@^2.6.11:
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
w3c-keyname@^2.2.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.2.tgz#7ea63170454bb19f1a3c6b628fc3dc8889276e91"
integrity sha512-8Vs/aVwcy0IJACaPm4tyzh1fzehZE70bGSjEl3dDms5UXtWnaBElrSHC8lDDeak0Gk5jxKOFstL64/65o7Ge2A==
watchpack@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"

Loading…
Cancel
Save