Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
{
"name": "Node.js & TypeScript",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",
"name": "Node.js & TypeScript",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",

// Features to add to the dev container. More info: https://containers.dev/features.
"features": {"ghcr.io/devcontainers-extra/features/ripgrep:1": {}},
// Features to add to the dev container. More info: https://containers.dev/features.
"features": { "ghcr.io/devcontainers-extra/features/ripgrep:1": {} },

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [5173],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [5173],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install"
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install"

// Configure tool-specific properties.
// "customizations": {},
// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
4 changes: 2 additions & 2 deletions .github/workflows/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ name: Deploy to GitHub Pages

on:
push:
branches: [ main ]
branches: [main]
workflow_dispatch:

concurrency:
group: 'pages'
group: "pages"
cancel-in-progress: true

permissions:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Test
on:
pull_request:
push:
branches: [ main ]
branches: [main]

jobs:
test:
Expand Down
24 changes: 12 additions & 12 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import { defineConfig, globalIgnores } from "eslint/config";

export default defineConfig([
globalIgnores(['dist', 'tmp']),
globalIgnores(["dist", "tmp"]),
{
files: ['**/*.{js,jsx}'],
files: ["**/*.{js,jsx}"],
extends: [
js.configs.recommended,
reactHooks.configs['recommended-latest'],
reactHooks.configs["recommended-latest"],
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: "latest",
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaVersion: "latest",
ecmaFeatures: { jsx: true },
sourceType: 'module',
sourceType: "module",
},
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
"no-unused-vars": ["error", { varsIgnorePattern: "^[A-Z_]" }],
},
},
])
]);
92 changes: 49 additions & 43 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,50 +1,56 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en" prefix="og: http://ogp.me/ns#">
<head>
<meta charset="utf-8">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-T8GEX2GPTP"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-T8GEX2GPTP', {"app_name": "pen"});
</script>
<head>
<meta charset="utf-8" />
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-T8GEX2GPTP"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "G-T8GEX2GPTP", { app_name: "pen" });
</script>

<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta name="keywords" content="calligraphy, penmanship, practice, lines, guidelines, sheet, template, print, printer, free, cursive">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<meta
name="keywords"
content="calligraphy, penmanship, practice, lines, guidelines, sheet, template, print, printer, free, cursive"
/>

<meta property="og:title" content="Penmanship Practice Sheets">
<meta property="og:description" content="Create custom, printable penmanship and calligraphy practice sheets. Design your own or use standard layouts for Copperplate, Spencerian, and more.">
<meta property="og:url" content="https://kylev.github.io/pen/">
<meta property="og:image" content="https://kylev.github.io/pen/og-image.png">
<meta property="og:type" content="website">
<meta property="og:locale" content="en_US">
<meta property="og:locale:alternate" content="en_GB">
<meta property="og:locale:alternate" content="es_LA">
<meta name="twitter:card" content="summary">
<meta name="twitter:creator" content="@kylev">
<meta property="og:title" content="Penmanship Practice Sheets" />
<meta
property="og:description"
content="Create custom, printable penmanship and calligraphy practice sheets. Design your own or use standard layouts for Copperplate, Spencerian, and more."
/>
<meta property="og:url" content="https://kylev.github.io/pen/" />
<meta property="og:image" content="https://kylev.github.io/pen/og-image.png" />
<meta property="og:type" content="website" />
<meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="en_GB" />
<meta property="og:locale:alternate" content="es_LA" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content="@kylev" />

<!-- TODO Make manifest pretty. -->
<link rel="manifest" href="manifest.json">
<link rel="shortcut icon" href="favicon.ico">
<!-- TODO Make manifest pretty. -->
<link rel="manifest" href="manifest.json" />
<link rel="shortcut icon" href="favicon.ico" />

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />

<title>Calligraphy Practice Sheets</title>
</head>
<body>
<noscript>
JavaScript is required.
</noscript>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
<title>Calligraphy Practice Sheets</title>
</head>
<body>
<noscript>JavaScript is required.</noscript>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "penpage",
"version": "2.0.3",
"private": true,
"type": "module",
"homepage": "https://kylev.github.io/pen/",
"dependencies": {
"@emotion/react": "^11.14.0",
Expand Down
3 changes: 3 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
printWidth: 100,
};
4 changes: 2 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";

import { I18nextProvider } from "react-i18next";
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { ThemeProvider, createTheme } from "@mui/material/styles";

import "./App.css";

Expand All @@ -17,7 +17,7 @@ gaWatchStore(store);

i18n.on("languageChanged", (lng) => {
gaLangChange(lng);
window.document.documentElement.setAttribute('lang', lng);
window.document.documentElement.setAttribute("lang", lng);
});

const theme = createTheme({
Expand Down
4 changes: 2 additions & 2 deletions src/App.test.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react';
import { expect, it } from 'vitest';
import { render, screen } from "@testing-library/react";
import { expect, it } from "vitest";

import App from "./App";

Expand Down
16 changes: 8 additions & 8 deletions src/BasicSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function BasicSettings({ hidden, role, store }) {
id="presets-field"
label={"presets"}
value={store.ratio}
onChange={v => store.setRatioPreset(v)}
onChange={(v) => store.setRatioPreset(v)}
choices={store.ratioChoices}
/>
</Grid>
Expand All @@ -31,11 +31,11 @@ function BasicSettings({ hidden, role, store }) {
type="number"
value={store.guideline.angle}
disabled={store.ratio !== "custom"}
onChange={e => store.setGuidelineAngle(e.target.value)}
onChange={(e) => store.setGuidelineAngle(e.target.value)}
slotProps={{
input: {
endAdornment: <InputAdornment position="end">°</InputAdornment>
}
endAdornment: <InputAdornment position="end">°</InputAdornment>,
},
}}
/>
</Grid>
Expand All @@ -47,15 +47,15 @@ function BasicSettings({ hidden, role, store }) {
min={2}
step={0.1}
value={store.xHeight}
onChange={v => (store.xHeight = v)}
onChange={(v) => (store.xHeight = v)}
/>
</Grid>
<Grid size={gridSize}>
<DropDownField
id="size-field"
label={"pagesize"}
value={store.pageSize}
onChange={v => (store.pageSize = v)}
onChange={(v) => (store.pageSize = v)}
choices={store.pageSizes}
/>
</Grid>
Expand All @@ -64,13 +64,13 @@ function BasicSettings({ hidden, role, store }) {
id="orientation-field"
label={"pageorientation"}
value={store.orientation}
onChange={v => (store.orientation = v)}
onChange={(v) => (store.orientation = v)}
choices={store.orientations}
/>
</Grid>
</Grid>
</Box>
);
};
}

export default BasicSettings;
10 changes: 5 additions & 5 deletions src/ColorDropDownField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ const colors = [
{ key: "transparent" },
{ key: "black" },
{ key: "gray" },
...range(1, 10).map(v => ({
...range(1, 10).map((v) => ({
key: Color.rgb(255, 255, 255)
.darken(v / 10.0)
.string(),
name: ["colorNames.graypct", { pct: v * 10 }]
name: ["colorNames.graypct", { pct: v * 10 }],
})),
{ key: "blue" },
{ key: Color.rgb(164, 221, 237).string(), name: "colorNames.nonphotoblue" },
Expand All @@ -22,10 +22,10 @@ const colors = [
{ key: "pink" },
{ key: "red" },
{ key: "white" },
{ key: "yellow" }
].map(c => (c.name ? c : { ...c, name: `colorNames.${c.key}` }));
{ key: "yellow" },
].map((c) => (c.name ? c : { ...c, name: `colorNames.${c.key}` }));

const ColorDropDownField = props => {
const ColorDropDownField = (props) => {
return <DropDownField choices={colors} {...props} />;
};

Expand Down
10 changes: 5 additions & 5 deletions src/CustomSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,39 @@ const CustomSettings = ({ hidden, role, store }) => {
id="guide-spacing-field"
label="guidespacing"
value={store.guideline.spacing}
onChange={v => (store.guideline.spacing = v)}
onChange={(v) => (store.guideline.spacing = v)}
/>
</Grid>

<Grid size={{ xs: 6, sm: 3 }}>
<ColorDropDownField
id="gap-color-field"
label="gapcolor"
onChange={v => (store.gapColor = v)}
onChange={(v) => (store.gapColor = v)}
value={store.gapColor}
/>
</Grid>
<Grid size={{ xs: 6, sm: 3 }}>
<ColorDropDownField
id="x-marker-field"
label="xmarker"
onChange={v => (store.xColor = v)}
onChange={(v) => (store.xColor = v)}
value={store.xColor}
/>
</Grid>
<Grid size={{ xs: 6, sm: 3 }}>
<ColorDropDownField
id="watermark-color-field"
label="watermark"
onChange={v => (store.watermarkColor = v)}
onChange={(v) => (store.watermarkColor = v)}
value={store.watermarkColor}
/>
</Grid>
<Grid size={{ xs: 6, sm: 3 }}>
<MillimeterField
id="print-gap-field"
label="globalmargin"
onChange={v => (store.printGap = v)}
onChange={(v) => (store.printGap = v)}
value={store.printGap}
/>
</Grid>
Expand Down
6 changes: 3 additions & 3 deletions src/DashDropDownField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ const dashes = [
{ key: "none", value: "none" },
{ key: "1cm", value: "1, 1" },
{ key: "2cm", value: "2, 2" },
{ key: "42vary", value: "4, 2" }
].map(c => (c.name ? c : { ...c, name: `dashNames.${c.key}` }));
{ key: "42vary", value: "4, 2" },
].map((c) => (c.name ? c : { ...c, name: `dashNames.${c.key}` }));

const DashDropDownField = props => {
const DashDropDownField = (props) => {
return <DropDownField choices={dashes} {...props} />;
};

Expand Down
Loading