From dc797901b26bb4f2e41d8445a1763f475348a6a8 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 23 Oct 2024 17:35:40 +0200 Subject: [PATCH] Rewrite project --- .editorconfig | 8 +- .github/workflows/main.yml | 23 ++++ .gitignore | 9 +- .npmrc | 1 + .prettierignore | 2 + .travis.yml | 12 -- license | 2 +- package.json | 124 +++++++++++--------- readme.md | 8 +- src/index.css | 185 ++++++++++++++--------------- src/index.html | 15 +-- src/index.js | 230 ------------------------------------- src/index.jsx | 179 +++++++++++++++++++++++++++++ tsconfig.json | 17 +++ 14 files changed, 405 insertions(+), 410 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml delete mode 100644 src/index.js create mode 100644 src/index.jsx create mode 100644 tsconfig.json diff --git a/.editorconfig b/.editorconfig index c6c8b36..0f17867 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ root = true [*] -indent_style = space -indent_size = 2 -end_of_line = lf charset = utf-8 -trim_trailing_whitespace = true +end_of_line = lf +indent_size = 2 +indent_style = space insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..80eb13a --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,23 @@ +jobs: + main: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: node + - run: npm install + - run: npm test + - uses: JamesIves/github-pages-deploy-action@releases/v4 + with: + branch: gh-pages + commit-message: . + folder: dest + git-config-email: tituswormer@gmail.com + git-config-name: Titus Wormer + single-commit: true +name: main +on: + push: + branches: + - website diff --git a/.gitignore b/.gitignore index fd5ed48..84ce63e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ -.DS_Store -*.log -node_modules/ dest/ +node_modules/ +*.d.ts +*.log +*.map +*.tsbuildinfo +.DS_Store yarn.lock diff --git a/.npmrc b/.npmrc index 43c97e7..3757b30 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ +ignore-scripts=true package-lock=false diff --git a/.prettierignore b/.prettierignore index 46c3855..50bbfdc 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,3 @@ dest/ +*.html +*.md diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9e4b306..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: node_js -node_js: node -deploy: - provider: pages - local_dir: dest - target_branch: gh-pages - skip_cleanup: true - github_token: $GITHUB_TOKEN - email: tituswormer@gmail.com - name: Titus Wormer - on: - branch: website diff --git a/license b/license index 0c06d5b..bc8f165 100644 --- a/license +++ b/license @@ -1,6 +1,6 @@ (The MIT License) -Copyright (c) 2014 Titus Wormer +Copyright (c) Titus Wormer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/package.json b/package.json index fef0029..675acf5 100644 --- a/package.json +++ b/package.json @@ -1,66 +1,44 @@ { - "name": "www-retext-pos", - "private": true, - "license": "MIT", - "repository": "retextjs/retext-pos", - "bugs": "https://github.com/retextjs/retext-pos/issues", "author": "Titus Wormer (https://wooorm.com)", - "contributors": [ - "Titus Wormer (https://wooorm.com)" - ], - "devDependencies": { - "browserify": "^16.0.0", - "cssnano": "^4.0.0", - "debounce": "^1.0.0", - "global": "^4.4.0", - "postcss-cli": "^6.0.0", - "postcss-preset-env": "^6.6.0", - "prettier": "^1.0.0", - "rehype-cli": "^8.0.0", - "rehype-preset-minify": "^4.0.0", - "rehype-prevent-favicon-request": "^2.0.0", - "remark-cli": "^7.0.0", - "remark-preset-wooorm": "^6.0.0", - "retext-english": "^3.0.0", - "retext-pos": "^2.0.0", - "stylelint": "^11.0.0", - "stylelint-config-standard": "^19.0.0", - "tinyify": "^2.0.0", - "unified": "^8.0.0", - "virtual-dom": "^2.1.1", - "xo": "^0.25.0" - }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix && stylelint src/index.css --fix", - "build:js": "browserify src -p tinyify -o dest/index.js", - "build:css": "postcss src/index.css -o dest/index.css", - "build:html": "rehype -u preset-minify -u prevent-favicon-request src -o dest", - "build": "npm run build:js && npm run build:css && npm run build:html", - "test": "npm run format && npm run build" - }, - "prettier": { - "tabWidth": 2, - "useTabs": false, - "singleQuote": true, - "bracketSpacing": false, - "semi": false, - "trailingComma": "none" - }, - "xo": { - "prettier": true, - "esnext": false, - "rules": { - "unicorn/prefer-node-append": "off" - } - }, - "stylelint": { - "extends": "stylelint-config-standard" - }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ], + "bugs": "https://github.com/retextjs/retext-pos/issues", + "contributors": [ + "Titus Wormer (https://wooorm.com)" + ], + "devDependencies": { + "@types/nlcst": "^2.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "cssnano": "^7.0.0", + "devlop": "^1.0.0", + "esbuild": "^0.24.0", + "postcss-cli": "^11.0.0", + "postcss-preset-env": "^10.0.0", + "prettier": "^3.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "rehype-cli": "^12.0.0", + "rehype-preset-minify": "^7.0.0", + "rehype-prevent-favicon-request": "^4.0.0", + "remark-cli": "^12.0.0", + "remark-preset-wooorm": "^10.0.0", + "retext-english": "^5.0.0", + "retext-pos": "^5.0.0", + "retext-stringify": "^4.0.0", + "stylelint": "^16.0.0", + "stylelint-config-standard": "^36.0.0", + "type-coverage": "^2.0.0", + "typescript": "^5.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0", + "xo": "^0.59.0" + }, + "license": "MIT", + "name": "www-retext-pos", "postcss": { "plugins": { "postcss-preset-env": {}, @@ -69,9 +47,41 @@ } } }, + "prettier": { + "bracketSpacing": false, + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "useTabs": false + }, + "private": true, "remarkConfig": { "plugins": [ - "preset-wooorm" + "remark-preset-wooorm" ] + }, + "repository": "retextjs/retext-pos", + "typeCoverage": { + "atLeast": 100, + "strict": true + }, + "type": "module", + "scripts": { + "build": "tsc --build --clean && tsc --build && type-coverage", + "format": "remark --frail --output --quiet -- . && prettier --log-level warn --write -- . && xo && stylelint src/index.css --fix", + "generate:css": "postcss --output dest/index.css -- src/index.css", + "generate:html": "rehype --frail --output dest/ --quiet --use rehype-preset-minify --use rehype-prevent-favicon-request -- src/", + "generate:js:module": "esbuild src/index.jsx --bundle --conditions=browser,production --define:process.env.NODE_ENV=\\\"production\\\" --format=esm --loader:.js=jsx --log-level=warning --minify --outfile=dest/index.module.js --target=es2020", + "generate:js:nomodule": "esbuild src/index.jsx --bundle --conditions=browser,production --define:process.env.NODE_ENV=\\\"production\\\" --loader:.js=jsx --log-level=warning --minify --outfile=dest/index.nomodule.js --target=es6", + "generate:js": "npm run generate:js:module && npm run generate:js:nomodule", + "generate": "npm run generate:css && npm run generate:html && npm run generate:js", + "test": "npm run build && npm run format && npm run generate" + }, + "stylelint": { + "extends": "stylelint-config-standard" + }, + "xo": { + "prettier": true } } diff --git a/readme.md b/readme.md index 8afacb9..92fad1e 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ ## Related -* [readability](https://github.com/wooorm/readability) -* [write-music](https://github.com/wooorm/write-music) -* [common-words](https://github.com/wooorm/common-words) -* [short-words](https://github.com/wooorm/short-words) +* [readability](https://github.com/wooorm/readability) +* [write-music](https://github.com/wooorm/write-music) +* [common-words](https://github.com/wooorm/common-words) +* [short-words](https://github.com/wooorm/short-words) diff --git a/src/index.css b/src/index.css index a5970f0..ad7cf94 100644 --- a/src/index.css +++ b/src/index.css @@ -1,29 +1,54 @@ -:root { - color-scheme: light dark; - font-family: system-ui; - background-color: hsl(0, 0%, 95%); - word-break: break-word; +* { + box-sizing: border-box; + line-height: calc(1em + 1ex); +} + +a { + color: #0367d8; + text-decoration: none; + transition: 200ms; + transition-property: color; +} + +a:focus, +a:hover, +a:target { + color: inherit; } body { margin: 0; } +code { + font-size: 16px; +} + +h1, +p { + margin-bottom: calc(1em + 1ex); + margin-top: calc(1em + 1ex); +} + +h1 { + font-size: 3em; + font-weight: 100; + text-align: center; +} + +html { + background-color: hsl(0deg 0% 95%); + color-scheme: light dark; + font-family: system-ui; + word-break: break-word; +} + main { - background-color: hsl(0, 0%, 97.5%); - position: relative; - max-width: 40em; + background-color: hsl(0deg 0% 97.5%); margin: 0 auto; + max-width: 40em; padding: 0 calc(2em + 2ex); -} - -@media (min-width: 40em) and (min-height: 20em) { - main { - /* Go all Tschichold when supported */ - margin: 11.1vh 22.2vw 22.2vh 11.1vw; - border: 1px solid hsl(214, 13%, 90%); - border-radius: 3px; - } + position: relative; } main > div { @@ -31,25 +56,17 @@ main > div { } section { + border: 0 solid hsl(214deg 13% 90%); + border-bottom-width: 1px; + border-top-width: 1px; margin: calc(2em + 2ex) calc(-2em - 2ex); padding: calc(2em + 2ex); - border: 0 solid hsl(214, 13%, 90%); - border-top-width: 1px; - border-bottom-width: 1px; } section + section { margin-top: calc(-2em - 2ex - 1px); } -section > :first-child { - margin-top: 0; -} - -section > :last-child { - margin-bottom: 0; -} - section:first-child { border-top-left-radius: inherit; border-top-right-radius: inherit; @@ -64,111 +81,95 @@ section:last-child { margin-bottom: 0; } -.highlight { - background-color: hsl(0, 0%, 100%); -} - -* { - line-height: calc(1em + 1ex); - box-sizing: border-box; -} - -p, -h1 { - margin-top: calc(1em + 1ex); - margin-bottom: calc(1em + 1ex); -} - -h1 { - font-size: 3em; - font-weight: 100; - text-align: center; -} - -a { - text-decoration: none; - color: #0367d8; - transition: 200ms; - transition-property: color; -} - -a:hover, -a:focus, -a:target { - color: inherit; +section > :first-child { + margin-top: 0; } -code { - font-size: 16px; +section > :last-child { + margin-bottom: 0; } template { display: none; } -.editor { - position: relative; - max-width: 100%; - overflow: hidden; +textarea { + color: inherit; + position: absolute; + top: 0; } textarea, .draw { /* Can’t use a nice font: kerning renders differently in textareas. */ + background: transparent; + border: none; + box-sizing: border-box; font-family: monospace; font-size: 16px; + height: 100%; letter-spacing: normal; line-height: calc(1em + 1ex); - white-space: pre-wrap; - word-wrap: break-word; - background: transparent; - box-sizing: border-box; - border: none; - outline: none; margin: 0; - padding: 0; - width: 100%; - height: 100%; + outline: none; overflow: hidden; + padding: 0; resize: none; + white-space: pre-wrap; + width: 100%; + word-wrap: break-word; +} + +.credits { + text-align: center; } .draw { -webkit-print-color-adjust: exact; color: transparent; + print-color-adjust: exact; } -textarea { - position: absolute; - top: 0; - color: inherit; +.editor { + max-width: 100%; + overflow: hidden; + position: relative; } -.credits { - text-align: center; +.highlight { + background-color: hsl(0deg 0% 100%); +} + +@media (width >= 40em) and (height >= 20em) { + main { + /* Go all Tschichold when supported */ + border: 1px solid hsl(214deg 13% 90%); + border-radius: 3px; + margin: 11.1vh 22.2vw 22.2vh 11.1vw; + } } @media (prefers-color-scheme: dark) { - :root { - background-color: hsl(214, 13%, 7.5%); - color: hsl(214, 13%, 95%); + html { + background-color: hsl(214deg 13% 7.5%); + color: hsl(214deg 13% 95%); } main { - background-color: hsl(214, 13%, 5%); + background-color: hsl(214deg 13% 5%); } - @media (min-width: 40em) and (min-height: 20em) { - main { - border-color: hsl(214, 13%, 12.5%); - } + section { + border-color: hsl(214deg 13% 12.5%); } .highlight { - background-color: hsl(214, 13%, 2.5%); + background-color: hsl(214deg 13% 2.5%); } - section { - border-color: hsl(214, 13%, 12.5%); + @media (width >= 40em) and (height >= 20em) { + main { + border-color: hsl(214deg 13% 12.5%); + } } } diff --git a/src/index.html b/src/index.html index 08ee0aa..ad3ee6f 100644 --- a/src/index.html +++ b/src/index.html @@ -1,14 +1,15 @@ retext-pos - - - - - - + + + + + +
- + + diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 8e39833..0000000 --- a/src/index.js +++ /dev/null @@ -1,230 +0,0 @@ -var doc = require('global/document') -var win = require('global/window') -var createElement = require('virtual-dom/create-element') -var diff = require('virtual-dom/diff') -var patch = require('virtual-dom/patch') -var h = require('virtual-dom/h') -var debounce = require('debounce') -var unified = require('unified') -var english = require('retext-english') -var pos = require('retext-pos') - -var own = {}.hasOwnProperty - -var darkQuery = '(prefers-color-scheme: dark)' - -var map = { - CC: 'Coord Conjunction', - CD: 'Cardinal number', - DT: 'Determiner', - EX: 'Existential there', - FW: 'Foreign Word', - IN: 'Preposition', - JJ: 'Adjective', - JJR: 'Adjective, comparative', - JJS: 'Adjective, superlative', - LS: 'List item marker', - MD: 'Modal', - NN: 'Noun, singular or mass', - NNP: 'Proper noun, singular', - NNPS: 'Proper noun, plural', - NNS: 'Noun, plural', - POS: 'Possessive ending', - PDT: 'Predeterminer', - PP$: 'Possessive pronoun', - PRP: 'Personal pronoun', - RB: 'Adverb', - RBR: 'Adverb, comparative', - RBS: 'Adverb, superlative', - RP: 'Particle', - SYM: 'Symbol', - TO: '"to"', - UH: 'Interjection', - VB: 'Verb, base form', - VBD: 'Verb, past tense', - VBG: 'Verb, gerund', - VBN: 'Verb, past part', - VBP: 'Verb, present', - VBZ: 'Verb, present', - WDT: 'Wh-determiner', - WP: 'Wh pronoun', - WP$: 'Possessive-Wh', - WRB: 'Wh-adverb' -} - -var color = colors(Object.keys(map).length) - -var ceil = Math.ceil - -var processor = unified() - .use(english) - .use(pos) - -var main = doc.querySelector('main') - -var state = { - value: doc.querySelector('template').innerHTML -} - -win.matchMedia(darkQuery).addListener(onmediachange) - -var tree = render(state) -var dom = main.appendChild(createElement(tree)) - -function onchangevalue(ev) { - var prev = state.value - var next = ev.target.value - - if (prev !== next) { - state.value = next - onchange() - } -} - -function onchange() { - var next = render(state) - dom = patch(dom, diff(tree, next)) - tree = next -} - -function render(state) { - var tree = processor.runSync(processor.parse(state.value)) - var change = debounce(onchangevalue, 4) - var key = 0 - - var keys = Object.keys(map).reduce(function(all, cur) { - return all.concat( - h( - 'span', - {key: 'c-' + key, style: {backgroundColor: color(cur)}}, - map[cur] - ), - ' ' - ) - }, []) - - setTimeout(resize, 4) - - return h('div', [ - h('section.highlight', [h('h1', {key: 'title'}, 'POS: Part of Speech')]), - h('div', {key: 'editor', className: 'editor'}, [ - h('div', {key: 'draw', className: 'draw'}, pad(all(tree, []))), - h('textarea', { - key: 'area', - value: state.value, - oninput: change, - onpaste: change, - onkeyup: change, - onmouseup: change - }) - ]), - h('section.highlight', [h('p', {key: 'map'}, keys)]), - h('section.credits', {key: 'credits'}, [ - h('p', [ - h('a', {href: 'https://github.com/retextjs/retext-pos'}, 'Project'), - ' • ', - h( - 'a', - {href: 'https://github.com/retextjs/retext-pos/tree/website'}, - 'Fork this website' - ), - ' • ', - h( - 'a', - {href: 'https://github.com/retextjs/retext-pos/blob/website/license'}, - 'MIT' - ), - ' • ', - h('a', {href: 'https://wooorm.com'}, '@wooorm') - ]) - ]) - ]) - - function all(node, parentIds) { - var children = node.children - var length = children.length - var index = -1 - var results = [] - - while (++index < length) { - results = results.concat(one(children[index], parentIds.concat(index))) - } - - return results - } - - function one(node, parentIds) { - var result = 'value' in node ? node.value : all(node, parentIds) - var id = parentIds.join('-') + '-' + key - var tag = node.data && node.data.partOfSpeech - - if (tag && own.call(map, tag)) { - result = h( - 'span', - {key: id, id: id, style: {backgroundColor: color(tag)}}, - result - ) - key++ - } - - return result - } - - // Trailing white-space in a `textarea` is shown, but not in a `div` with - // `white-space: pre-wrap`. - // Add a `br` to make the last line feed explicit. - function pad(nodes) { - var tail = nodes[nodes.length - 1] - - if (typeof tail === 'string' && tail.charAt(tail.length - 1) === '\n') { - nodes.push(h('br', {key: 'break'})) - } - - return nodes - } -} - -function rows(node) { - if (node) { - return ( - ceil( - node.getBoundingClientRect().height / - parseInt(win.getComputedStyle(node).lineHeight, 10) - ) + 1 - ) - } -} - -function resize() { - dom.querySelector('textarea').rows = rows(dom.querySelector('.draw')) -} - -function colors(max) { - var cached = {} - var count = 0 - var step = 360 / max - var dark = win.matchMedia(darkQuery).matches - - return color - - function color(id) { - var lightness - var value - - if (own.call(cached, id)) { - return cached[id] - } - - lightness = dark ? '12.5%' : '90%' - value = 'hsl(' + count * step + ', 96%, ' + lightness + ')' - cached[id] = value - count++ - - return value - } -} - -function onmediachange() { - color = colors(Object.keys(map).length) - onchange() -} diff --git a/src/index.jsx b/src/index.jsx new file mode 100644 index 0000000..fbd441c --- /dev/null +++ b/src/index.jsx @@ -0,0 +1,179 @@ +/* eslint-env browser */ +/// + +/** + * @import {Nodes, Parents, Root} from 'nlcst' + */ + +import ReactDom from 'react-dom/client' +import React from 'react' +import retextEnglish from 'retext-english' +import retextPos from 'retext-pos' +import {unified} from 'unified' +import {VFile} from 'vfile' + +const $main = /** @type {HTMLElement} */ (document.querySelector('main')) +const $template = /** @type {HTMLTemplateElement} */ ( + document.querySelector('template') +) + +/** @type {Record} */ +const map = { + CC: 'Coord Conjunction', + CD: 'Cardinal number', + DT: 'Determiner', + EX: 'Existential there', + FW: 'Foreign Word', + IN: 'Preposition', + JJ: 'Adjective', + JJR: 'Adjective, comparative', + JJS: 'Adjective, superlative', + LS: 'List item marker', + MD: 'Modal', + NN: 'Noun, singular or mass', + NNP: 'Proper noun, singular', + NNPS: 'Proper noun, plural', + NNS: 'Noun, plural', + POS: 'Possessive ending', + PDT: 'Predeterminer', + PP$: 'Possessive pronoun', + PRP: 'Personal pronoun', + RB: 'Adverb', + RBR: 'Adverb, comparative', + RBS: 'Adverb, superlative', + RP: 'Particle', + SYM: 'Symbol', + TO: '"to"', + UH: 'Interjection', + VB: 'Verb, base form', + VBD: 'Verb, past tense', + VBG: 'Verb, gerund', + VBN: 'Verb, past part', + VBP: 'Verb, present', + VBZ: 'Verb, present', + WDT: 'Wh-determiner', + WP: 'Wh pronoun', + WP$: 'Possessive-Wh', + WRB: 'Wh-adverb' +} + +/** @type {Record} */ +const mapColors = {} +let count = 0 +const keys = Object.keys(map) +const step = 360 / keys.length + +for (const key of keys) { + const prefix = 'hsl(' + count * step + 'deg 96% ' + const suffix = ')' + mapColors[key] = [prefix + '25%' + suffix, prefix + '90%' + suffix] + count++ +} + +const processor = unified().use(retextEnglish).use(retextPos) + +const root = ReactDom.createRoot($main) + +root.render(React.createElement(Playground)) + +function Playground() { + const [text, setText] = React.useState($template.innerHTML) + const file = new VFile(text) + const darkLightIndex = window.matchMedia('(prefers-color-scheme: dark)') + .matches + ? 0 + : 1 + + const tree = /** @type {Root} */ ( + processor.runSync(processor.parse(file), file) + ) + + /** @type {Array} */ + const keys = [] + + for (const [key, value] of Object.entries(map)) { + const colors = mapColors[key] + const backgroundColor = colors[darkLightIndex] + keys.push({value}, ' ') + } + + return ( +
+
+

+ retext-pos +

+
+
+
+ {all(tree)} + {/* Trailing whitespace in a `textarea` is shown, + but not in a `div` with `white-space: pre-wrap`; + add a `br` to make the last newline explicit. */} + {/\n[ \t]*$/.test(text) ?
: undefined} +
+