Initial commit
All checks were successful
continuous-integration/drone Build is passing

This commit is contained in:
Michal Kunc 2022-04-06 10:46:44 +02:00
commit 2fd4e104b8
18 changed files with 1553 additions and 0 deletions

1
.dockerignore Symbolic link
View File

@ -0,0 +1 @@
.gitignore

20
.drone.yml Normal file
View File

@ -0,0 +1,20 @@
kind: pipeline
type: docker
name: default
steps:
- name: deploy
image: caprover/cli-caprover:2.2.3
commands:
- caprover deploy
environment:
CAPROVER_URL:
from_secret: caprover_url
CAPROVER_APP:
from_secret: caprover_app
CAPROVER_APP_TOKEN:
from_secret: app_token
CAPROVER_BRANCH: main
when:
branch:
- main

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/node_modules/
/public/build/
.DS_Store

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM node AS build
ARG CAPROVER_GIT_COMMIT_SHA=""
WORKDIR /app
COPY . /app
RUN npm install && \
npm run build
FROM nginx
COPY --from=build /app/public /usr/share/nginx/html

4
captain-definition Normal file
View File

@ -0,0 +1,4 @@
{
"schemaVersion": 2,
"dockerfilePath": "./Dockerfile"
}

1088
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "pf2e-cards",
"version": "1.0.0",
"private": true,
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public --no-clear",
"check": "svelte-check --tsconfig ./tsconfig.json"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"rollup": "^2.3.4",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"svelte": "^3.0.0",
"svelte-check": "^2.0.0",
"svelte-preprocess": "^4.0.0",
"@rollup/plugin-typescript": "^8.0.0",
"typescript": "^4.0.0",
"tslib": "^2.0.0",
"@tsconfig/svelte": "^2.0.0"
},
"dependencies": {
"dompurify": "^2.3.6",
"lz-string": "^1.4.4",
"sirv-cli": "^2.0.0"
}
}

BIN
public/bg.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

63
public/global.css Normal file
View File

@ -0,0 +1,63 @@
html, body {
position: relative;
width: 100%;
height: 100%;
}
body {
color: #333;
margin: 0;
padding: 8px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
a {
color: rgb(0,100,200);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: rgb(0,80,160);
}
label {
display: block;
}
input, button, select, textarea {
font-family: inherit;
font-size: inherit;
-webkit-padding: 0.4em 0;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
}
input:disabled {
color: #ccc;
}
button {
color: #333;
background-color: #f4f4f4;
outline: none;
}
button:disabled {
color: #999;
}
button:not(:disabled):active {
background-color: #ddd;
}
button:focus {
border-color: #666;
}

17
public/index.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>

83
rollup.config.js Normal file
View File

@ -0,0 +1,83 @@
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';
import typescript from '@rollup/plugin-typescript';
import css from 'rollup-plugin-css-only';
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
export default {
input: 'src/main.ts',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
preprocess: sveltePreprocess({ sourceMap: !production }),
compilerOptions: {
// enable run-time checks when not in production
dev: !production
}
}),
// we'll extract any component CSS out into
// a separate file - better for performance
css({ output: 'bundle.css' }),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
typescript({
sourceMap: !production,
inlineSources: !production
}),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};

106
src/App.svelte Normal file
View File

@ -0,0 +1,106 @@
<script lang="ts">
import Card from './Card.svelte';
import CardInput from './CardInput.svelte';
import LZString from 'lz-string';
const MAX_CARDS = 9;
let cards = [];
let mhp = {
name: "Minor healing potion",
type: "Item 1",
tags: ["Consumable", "Healing", "Magical", "Necromancy", "Positive", "Potion"],
attributes: `<p><b>Usage</b> Held in 1 hand; <b>Bulk</b> L</p>
<p><b>Activate (A)</b> Interact</p>`,
description: `<p>A healing potion is a vial of a ruby-red liquid that imparts a tingling sensation as the drinkers wounds heal rapidly. When you drink a healing potion, you regain 2d8+5 Hit Points.</p>`
};
if (window.location.hash !== "") {
debugger;
cards = JSON.parse(LZString.decompressFromEncodedURIComponent(window.location.hash.substring(1)));
} else {
cards = [{id: 0, ...mhp}];
}
function get_idx_by_id(id) {
for(let idx = 0; idx < cards.length; idx++) {
if(id === cards[idx].id) {
return idx
}
}
}
//let card_selected = 0;
//$: selected_card = cards[card_selected];
let selector = cards[0].id;
//$: selector = cards[card_selected].id;
$: selected_card = cards.filter(x => x.id === selector)[0];
$: selected_idx = get_idx_by_id(selector);
$: window.location.hash = LZString.compressToEncodedURIComponent(JSON.stringify(cards));
function change_tags(e) {
selected_card.tags = e.detail.tags.split(',').map(x => x.replace(/\s+/g, '')).filter(x => x != '');
cards[selected_idx] = selected_card;
}
function add_card() {
if (cards.length < MAX_CARDS) {
cards = [...cards, {id: cards[cards.length-1].id+1, ...mhp}];
selector = cards[cards.length-1].id;
}
}
function remove_card() {
console.log(cards);
console.log(selected_idx);
if (cards.length > 1) { // remove only if there is more than 1 card
cards = cards.filter(x => x.id != selector);
if (selected_idx >= cards.length) {
selector = cards[cards.length-1].id;
} else {
selector = cards[selected_idx].id;
}
}
}
</script>
<main>
<section class="controls">
<h1>PF2e card generator</h1>
<CardInput bind:name={selected_card.name} bind:type={selected_card.type} tags={selected_card.tags} bind:attributes={selected_card.attributes} bind:description={selected_card.description} on:change_tags={change_tags} on:add_card={add_card} on:remove_card={remove_card}/>
</section>
<section class="cards">
{#each cards as card, idx (card.id)}
<div class="card">
<Card {...card}/>
<input class="card-selector" type="radio" bind:group={selector} value={card.id}/>
</div>
{/each}
</section>
</main>
<style>
@media print {
@page {
margin: 0;
}
.controls {
display: none;
}
.card-selector {
display: none;
}
}
.cards {
display: flex;
flex-wrap: wrap;
}
h1 {
text-transform: uppercase;
font-size: 4em;
font-weight: 100;
}
</style>

75
src/Card.svelte Normal file
View File

@ -0,0 +1,75 @@
<script lang="ts">
import DOMPurify from 'dompurify';
export let name:string, type, tags = [], attributes:string, description:string;
</script>
<style>
.card {
width: 2.5in;
height: 3.5in;
font-size: 8pt;
border: 2mm solid black;
/*background-image: url("/bg.webp");*/
background-clip: border-box;
page-break-after: auto;
page-break-inside: avoid;
}
.header {
display: flex;
justify-content: space-between;
border-bottom: 2px solid black;
}
.name, .type {
font-family: serif;
font-size: 10pt;
font-weight: bold;
}
.tags {
margin: 1pt 1pt 0pt 1pt;
padding: 0;
display: flex;
flex-wrap: wrap;
}
.tag {
font-family: sans-serif;
font-size: 6pt;
font-variant: all-small-caps;
color: white;
background-color: #5e0000;
padding: 0.5pt 1pt;
border: 1pt solid #d9c484;
}
.content {
margin: 1pt;
}
:global(section.content p) {
padding-bottom: 2pt;
margin: 0;
}
.description {
border-top: 1pt solid black;
text-align: justify;
}
</style>
<div class="card">
<section class="header">
<div class="name">{name}</div>
<div class="type">{type}</div>
</section>
<section class="tags">
{#each tags as tag}
<div class="tag">{tag}</div>
{/each}
</section>
<section class="content">
<p class="attributes">
{@html DOMPurify.sanitize(attributes)}
</p>
<p class="description">
{@html DOMPurify.sanitize(description)}
</p>
</section>
</div>

35
src/CardInput.svelte Normal file
View File

@ -0,0 +1,35 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
export let name, type, tags, attributes, description;
const dispatch = createEventDispatcher();
function change_tags(e) {
dispatch("change_tags", {tags: e.target.value});
}
</script>
<style>
div.cardInput {
width: 5in;
display: flex;
flex-direction: column;
}
</style>
<div class="cardInput">
<div class="row">Name: <input name="name" bind:value={name}/></div>
<div class="row">Type: <input name="type" bind:value={type}/></div>
<div class="row">Tags: <input name="tags" on:change={change_tags} value={tags}/></div>
<div class="row">Attributes:
<textarea bind:value={attributes}></textarea>
</div>
<div class="row">Description:
<textarea bind:value={description}></textarea>
</div>
<div class="row">
<button on:click={e => dispatch('add_card')}>+</button>
<button on:click={e => dispatch('remove_card')}>-</button>
</div>
</div>

1
src/global.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="svelte" />

7
src/main.ts Normal file
View File

@ -0,0 +1,7 @@
import App from './App.svelte';
const app = new App({
target: document.body,
});
export default app;

6
tsconfig.json Normal file
View File

@ -0,0 +1,6 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"include": ["src/**/*"],
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
}