diff --git a/.github/workflows/build-github-pages.yaml b/.github/workflows/build-github-pages.yaml new file mode 100644 index 0000000..1258456 --- /dev/null +++ b/.github/workflows/build-github-pages.yaml @@ -0,0 +1,28 @@ +# https://github.com/marketplace/actions/deploy-to-github-pages +name: Build and Deploy to Github Pages + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + build-and-deploy: + concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession. + runs-on: ubuntu-latest + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + + - name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built. + run: | + npm ci + npm run build + + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4 + with: + folder: dist # The folder the action should deploy. \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md index 27ea9ea..d0e7ac0 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ # Web Thermodynamics -This is a simple example of how to run CoolProp Javascript wrapper with units handled by mathjs. + +This is an example of how to run CoolProp Javascript wrapper with units handled by mathjs, including a code editor. [![interface](interface.png)](https://dvd101x.github.io/web-thermodynamics/ ) # To-Do -- [ ] Migrate to codemirror 6 with a build tool like Vite +- [x] Migrate to codemirror 6 with a build tool like Vite - [ ] Migrate the language definition to CM6 -- [ ] Scroll outputs into view and highlight them -- [ ] Not only text outputs (allow for latex) +- [x] Scroll outputs into view and highlight them +- [x] Not only text outputs (allow for latex) - [ ] More efficient math evaluation (only update state when inputs change) - [ ] Evaluate expression by expression (not necessarly block by block) -- [ ] Use Alpinejs to reduce code +- [x] Use Alpinejs to reduce code +- [ ] Dynamic autocomplete # Basic example @@ -226,12 +228,12 @@ cond_COP = Q_h/W_comp # # Final results -print('Compressor power : $0 \t$1\t$2', W_comp to [W, BTU/h, TR], 4) -print('Condenser heat out : $0 \t$1\t$2', Q_h to [W, BTU/h, TR], 4) -print('Evaporator heat in : $0 \t$1\t$2', Q_c to [W, BTU/h, TR], 4) +print('Compressor power : $1 \t$2\t$3', W_comp to [W, BTU/h, TR], 4) +print('Condenser heat out : $1 \t$2\t$3', Q_h to [W, BTU/h, TR], 4) +print('Evaporator heat in : $1 \t$2\t$3', Q_c to [W, BTU/h, TR], 4) -print('COP(cooling) : $0', [evap_COP], 3) -print('COP(heating) : $0', [cond_COP], 3) +print('COP(cooling) : $1', [evap_COP], 3) +print('COP(heating) : $1', [cond_COP], 3) ``` Shall return: @@ -253,7 +255,7 @@ COP(heating) : 3.57 Here is a similar project [Engineering-Solver](https://github.com/dvd101x/Engineering-Solver) that includes additional features: * Saves in the browser (you can continue where you left off) -* 9 workspaces, so you can try different things +* 20 workspaces, so you can try different things * Uses a webworker to avoid freezing during big calculaitons * A few more examples focused on the many features of mathjs @@ -265,7 +267,7 @@ Uses the following js libraries * [Mathjs](https://mathjs.org/) * [CoolProp](http://www.coolprop.org/) * Editing - * [CodeMirror 5](https://codemirror.net/5/) + * [CodeMirror](https://codemirror.net/) * Rendering results * [Markdown-it](https://github.com/markdown-it/markdown-it) * [markdown-it-katex](https://github.com/waylonflinn/markdown-it-katex) diff --git a/fluidProperties.js b/fluidProperties.js index cfa2a94..0dc8fdb 100644 --- a/fluidProperties.js +++ b/fluidProperties.js @@ -1,3 +1,5 @@ +import {number, unit} from 'mathjs' + // List of units from coolprop const propUnit = { '': '', @@ -253,12 +255,12 @@ function calcPropUnits(prop) { } } -// This is created only bacuse math.number(value,'') is not valid as '' can't be the unit -toValue = (v, u) => u ? math.number(v, u) : math.number(v) -// This is crate only becous math.unit(value,'') is not valid as '' can't be the unit -toUnit = (v, u) => u ? math.unit(v, u) : math.unit(v) +// This is created only because number(value,'') is not valid as '' can't be the unit +const toValue = (v, u) => u ? number(v, u) : number(v) +// This is crate only because unit(value,'') is not valid as '' can't be the unit +const toUnit = (v, u) => u ? unit(v, u) : unit(v) -function props(desiredProperty, fluidName, fluidProperties) { +export function props(desiredProperty, fluidName, fluidProperties) { const calcPropUnit = calcPropUnits(desiredProperty) let prop = Object.keys(fluidProperties).slice(0,2) let value = Object.values(fluidProperties).slice(0,2) @@ -280,7 +282,7 @@ function props(desiredProperty, fluidName, fluidProperties) { return toUnit(calcValue, calcPropUnit) } -function HAprops(calcProp, fluidProperties) { +export function HAprops(calcProp, fluidProperties) { const calcPropUnit = HApropUnit[calcProp] const arrayProperties = Object.entries(fluidProperties) @@ -304,8 +306,8 @@ function HAprops(calcProp, fluidProperties) { return toUnit(calcValue, calcPropUnit) } -function phase(fluid, fluidProperties) { +export function phase(fluid, fluidProperties) { return phases[props('Phase', fluid, fluidProperties)] -} +} \ No newline at end of file diff --git a/getExpressions.js b/getExpressions.js new file mode 100644 index 0000000..e61ed19 --- /dev/null +++ b/getExpressions.js @@ -0,0 +1,59 @@ +import { parse } from 'mathjs' + +/** + * Extracts parsable expressions from a multiline string. + * + * @param {string} str - The multiline string containing expressions. + * @returns {Array<{from: number, to: number, source: string}>} An array of objects, + * where each object represents a parsable expression and contains: + * - from: The starting line number of the expression within the original string. + * - to: The ending line number of the expression within the original string. + * - source: The actual string content of the expression. + */ +export default function getExpressions(str) { + const lines = str.split('\n'); + let nextLineToParse = 0; + const result = []; + + for (let lineID = 0; lineID < lines.length; lineID++) { + const linesToTest = lines.slice(nextLineToParse, lineID + 1).join('\n'); + if (canBeParsed(linesToTest)) { + if (!isEmptyString(linesToTest)) { + result.push({ from: nextLineToParse, to: lineID, source: linesToTest }); + } + // Start the next parsing attempt from the line after the successfully parsed expression. + nextLineToParse = lineID + 1; + } + } + // Handle any remaining lines that couldn't be parsed as expressions. + const linesToTest = lines.slice(nextLineToParse).join('\n'); + if (!isEmptyString(linesToTest)) { + result.push({ from: nextLineToParse, to: lines.length - 1, source: linesToTest }); + } + return result; +} + +/** + * Determines whether a given expression can be successfully parsed. + * + * @param {string} expression - The expression to parse. + * @returns {boolean} True if the expression can be parsed, false otherwise. + */ +function canBeParsed(expression) { + try { + parse(expression) + return true + } catch (error) { + return false + } +} + +/** + * Checks if a given string is empty or only contains whitespace characters. + * + * @param {string} str - The string to check. + * @returns {boolean} True if the string is empty or only contains whitespace, false otherwise. + */ +function isEmptyString(str) { + return str.trim() === "" +} \ No newline at end of file diff --git a/index.html b/index.html index be5cbf6..66353c5 100644 --- a/index.html +++ b/index.html @@ -1,125 +1,27 @@ - + - - - - - - Web thermodynamics - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

Type and get results - just like that!

-
-
- - - - + + + + + Web thermodynamics + + + +
+
+
+ +
+
+ + + \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..996ced1 --- /dev/null +++ b/main.js @@ -0,0 +1,91 @@ +import './style.css' + +import 'github-markdown-css/github-markdown-light.css' + +import 'katex/dist/katex.min.css' + +import Alpine from 'alpinejs' +import makeDoc from './makeDoc.js' + +import { EditorState } from "@codemirror/state" +import { EditorView, basicSetup } from "codemirror" +import { + StreamLanguage +} from '@codemirror/language' + +import { mathjsLang } from './mathjs.js' + +import math from './mathSetup.js' + +const editorDOM = document.querySelector('#editor') +const docChanged = new CustomEvent('docChanged') +const selectionChanged = new CustomEvent('selectionChanged') + +const example = [ + "# # Examples of props", + "# ", + "# *Density* $\\frac{kg}{m^3}$ of **nitrogen** at a *temperature* **25 °C** ", + "# and a *pressure* **1 atmosphere**:", + "props('D', 'Nitrogen', {T:25 degC, P:1 atm})", + "", + "# Saturated vapor enthalpy $\\frac{J}{kg}$ of **R134a** at **25 °C**", + "props('H', 'R134a', {T: 25 degC, Q:1})", + "", + "# # Example of Phase", + "# ", + "# *Phase* of **water** at a *pressure* of **1 atmosphere** and **0%** *Quality*:", + "phase('Water', {P:1 atm, Q: 0 %})", + "", + "# # Examples of HAprops", + "# ", + "# * *Enthalpy* $\\frac{J}{{kg}_{dry\\ air}}$ as a function of *temperature*, *pressure* and *relative humidity* at STP ", + "h = HAprops('H', {T: 25 degC, P:1 atm, R:50 %})", + "", + "# * *Temperature* of **saturated air** at the previous *enthalpy*", + "HAprops('T', {P:1 atm, H:h, R:1.0})" +] + +let startState = EditorState.create({ + doc: example.join('\n'), + extensions: [ + basicSetup, + StreamLanguage.define(mathjsLang(math)), + EditorView.lineWrapping, + EditorView.updateListener.of((update) => { + if (update.docChanged) { + editorDOM.dispatchEvent(docChanged) + if(update.selectionSet){ + editorDOM.dispatchEvent(selectionChanged) + } + } else if (update.selectionSet) { + editorDOM.dispatchEvent(selectionChanged) + } + }) + ], +}) + +const editor = new EditorView({ + state: startState, + parent: editorDOM, + lineWrapping: true, +}) + +window.Alpine = Alpine + +Alpine.data( + 'app', + () => ({ + expressions: makeDoc(editor.state.doc.toString()), + currentLine: 1, + get calcExpressions() { + this.expressions = makeDoc(editor.state.doc.toString()) + }, + get getCurrentLine() { + this.currentLine = editor.state.doc.lineAt( + editor.state.selection.ranges[editor.state.selection.mainIndex].from + ).number - 1 + } + }) +) + +Alpine.start() \ No newline at end of file diff --git a/makeDoc.js b/makeDoc.js new file mode 100644 index 0000000..173e6ee --- /dev/null +++ b/makeDoc.js @@ -0,0 +1,128 @@ +// Math calculations +import math from './mathSetup.js' + +// Formating and css +import 'github-markdown-css/github-markdown-light.css' +import katex from 'katex' +import 'katex/dist/katex.min.css' +import 'markdown-it-texmath/css/texmath.css' +import texmath from 'markdown-it-texmath' +import markdownit from 'markdown-it' + +import getExpressions from './getExpressions.js' + +const digits = 14 + +export default makeDoc + +// Setup markdown +const md = markdownit({ html: true }) + .use(texmath, { + engine: katex, + delimiters: ['dollars', 'beg_end'], + katexOptions: { macros: { "\\RR": "\\mathbb{R}" } } + }) + +// Setup math parser +const parser = math.parser() + +function makeDoc(code) { + const splitCode = code.split('\n'); + const lineTypes = splitCode.map(line => line.startsWith('# ') ? 'md' : 'math'); + let cells = []; + let lastType = ''; + parser.clear() + + // {from, to, source, outputs, visible, type} + splitCode + .forEach((line, lineNum) => { + const thisCell = cells.length - 1 + if (lastType === lineTypes[lineNum]) { + cells[thisCell].source.push(line) + cells[thisCell].to = lineNum + } + else { + cells.push({ from: lineNum, cell_type: lineTypes[lineNum], source: [line] }) + } + lastType = lineTypes[lineNum] + }) + + const outputCells = []; + cells.forEach(cell => { + if (cell.cell_type === 'md') { + const mdCode = cell.source.map(line => line.slice(2)).join('\n') + const mdRender = md.render(mdCode) + outputCells.push({ ...cell, visible: true, outputs: mdRender }) + } + else { + const mathResults = processExpressions(getExpressions(cell.source.join('\n'))) + mathResults.forEach(result => { + outputCells.push({ + from: cell.from + result.from, + to: cell.from + result.to, + source: cell.source, + visible: result.visible, outputs: result.outputs + }) + }) + } + }) + return outputCells +} + +/** + * Evaluates a given expression using a parser. + * + * @param {string} expression - The expression to evaluate. + * @returns {any} The result of the evaluation, or the error message if an error occurred. +*/ +function calc(expression) { + let result + try { + result = parser.evaluate(expression) + } catch (error) { + result = error.toString() + } + return result +} + +/** + * Formats result depending on the type of result + * + * @param {number, string, Help, any} result - The result to format + * @returns {string} The string in HTML with the formated result + */ +const formatResult = math.typed({ + 'number': result => math.format(result, { precision: digits }), + 'string': result => `${result}`, + 'Help': result => `
${math.format(result)}
`, + 'any': math.typed.referTo( + 'number', + fnumber => result => katex.renderToString(math.parse(fnumber(result)).toTex()) + ) +}) + +/** + * Processes an array of expressions by evaluating them, formatting the results, + * and determining their visibility. + * + * @param {Array<{from: number, to: number, source: string}>} expressions - An array of objects representing expressions, + * where each object has `from`, `to`, and `source` properties. + * @returns {Array<{from: number, to: number, source: string, outputs: any, visible: boolean}>} An array of processed expressions, + * where each object has additional `outputs` and `visible` properties. + */ +function processExpressions(expressions) { + return expressions.map(expression => { + const result = calc(expression.source) + const outputs = formatResult(result) + // Determine visibility based on the result type: + // - Undefined results are hidden. + // - Results with an `isResultSet` property are hidden when empty. + // - All other results are visible. + const visible = result === undefined ? false : (result.isResultSet && result.entries.length === 0) ? false : true + return ({ + ...expression, + outputs, + visible + }) + }) +} \ No newline at end of file diff --git a/mathSetup.js b/mathSetup.js index 20b703c..65eb218 100644 --- a/mathSetup.js +++ b/mathSetup.js @@ -1,3 +1,7 @@ +import {create, all} from "mathjs" +import {props, phase, HAprops} from './fluidProperties.js' +const math = create(all) + // this is a setup of mathjs that can use vectorized operations and thermodynamic properties function mapped(f) { @@ -32,3 +36,5 @@ math.import( ) math.createUnit('TR', '12e3 BTU/h') + +export default math \ No newline at end of file diff --git a/mathjs.js b/mathjs.js index 7b42e59..137086e 100644 --- a/mathjs.js +++ b/mathjs.js @@ -1,161 +1,244 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/5/LICENSE - -(function (mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function (CodeMirror) { - "use strict"; - - CodeMirror.defineMode("mathjs", function () { - function wordRegexp(words) { - return new RegExp("^((" + words.join(")|(") + "))\\b"); - } - - let singleOperators = new RegExp("^[\\+\\-\\*/&|\\^~<>!%']"); - let singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;\\.?]'); - let doubleOperators = new RegExp("^((==)|(!=)|(<=)|(>=)|(<<)|(>>)|(\\.[\\+\\-\\*/\\^]))"); - let doubleDelimiters = new RegExp("^((!=)|(\^\\|))"); - let tripleDelimiters = new RegExp("^((>>>)|(<<<))"); - let expressionEnd = new RegExp("^[\\]\\)]"); - let identifiers = new RegExp("^[_A-Za-z\xa1-\uffff][_A-Za-z0-9\xa1-\uffff]*"); - - const mathFunctions = [] - const mathPhysicalConstants = [] - const mathIgnore = ['expr', 'type'] - const numberLiterals = ['e', 'E', 'i', 'Infinity', 'LN2', 'LN10', 'LOG2E', 'LOG10E', 'NaN', - 'null', 'phi', 'pi', 'PI', 'SQRT1_2', 'SQRT2', 'tau', 'undefined', 'version'] - - // based on https://github.com/josdejong/mathjs/blob/develop/bin/cli.js - for (const expression in math.expression.mathWithTransform) { - if (!mathIgnore.includes(expression)) { - if (typeof math[expression] === "function") { - mathFunctions.push(expression) - } else if(!numberLiterals.includes(expression)) { - mathPhysicalConstants.push(expression) - } +/** + * Create mathjs syntax highlighting for CodeMirror + * + * TODO: this is using CodeMirror v5 functionality, upgrade this to v6 + * + * @param {Object} math A mathjs instance + */ +export function mathjsLang(math) { + function wordRegexp(words) { + return new RegExp('^((' + words.join(')|(') + '))\\b') + } + + const singleOperators = new RegExp("^[-+*/&|^~<>!%']") + const singleDelimiters = new RegExp('^[([{},:=;.?]') + const doubleOperators = new RegExp('^((==)|(!=)|(<=)|(>=)|(<<)|(>>)|(\\.[-+*/^]))') + const doubleDelimiters = new RegExp('^((!=)|(^\\|))') + const tripleDelimiters = new RegExp('^((>>>)|(<<<))') + const expressionEnd = new RegExp('^[\\])]') + const identifiers = new RegExp('^[_A-Za-z\xa1-\uffff][_A-Za-z0-9\xa1-\uffff]*') + + const mathFunctions = [] + const mathPhysicalConstants = [] + const mathIgnore = ['expr', 'type'] + const numberLiterals = [ + 'e', + 'E', + 'i', + 'Infinity', + 'LN2', + 'LN10', + 'LOG2E', + 'LOG10E', + 'NaN', + 'null', + 'phi', + 'pi', + 'PI', + 'SQRT1_2', + 'SQRT2', + 'tau', + 'undefined', + 'version' + ] + + // based on https://github.com/josdejong/mathjs/blob/develop/bin/cli.js + for (const expr in math.expression.mathWithTransform) { + if (!mathIgnore.includes(expr)) { + if (typeof math[expr] === 'function') { + mathFunctions.push(expr) + } else if (!numberLiterals.includes(expr)) { + mathPhysicalConstants.push(expr) } } + } - let builtins = wordRegexp(mathFunctions); - - let keywords = wordRegexp(['to', 'in', 'and', 'not', 'or', 'xor', 'mod']); - - // generates a list of all valid units in mathjs - let listOfUnits = [] - for (const unit in math.Unit.UNITS) { - for (const prefix in math.Unit.UNITS[unit].prefixes) { - listOfUnits.push(prefix + unit) - } + // generates a list of all valid units in mathjs + const listOfUnits = [] + for (const unit in math.Unit.UNITS) { + for (const prefix in math.Unit.UNITS[unit].prefixes) { + listOfUnits.push(prefix + unit) } + } - // remove duplicates - let units = wordRegexp(Array.from(new Set(listOfUnits))) - - // physicalCOnstants https://mathjs.org/docs/datatypes/units.html#physical-constants - let physicalConstants = wordRegexp(mathPhysicalConstants) + const builtins = wordRegexp(mathFunctions) - // tokenizers - function tokenTranspose(stream, state) { - if (!stream.sol() && stream.peek() === '\'') { - stream.next(); - state.tokenize = tokenBase; - return 'operator'; - } - state.tokenize = tokenBase; - return tokenBase(stream, state); - } + const keywords = wordRegexp(['to', 'in', 'and', 'not', 'or', 'xor', 'mod']) + const units = wordRegexp(Array.from(new Set(listOfUnits))) + const physicalConstants = wordRegexp(mathPhysicalConstants) - function tokenComment(stream, state) { - if (stream.match(/^.*#}/)) { - state.tokenize = tokenBase; - return 'comment'; - }; - stream.skipToEnd(); - return 'comment'; + // tokenizers + function tokenTranspose(stream, state) { + if (!stream.sol() && stream.peek() === "'") { + stream.next() + state.tokenize = tokenBase + return 'operator' + } + state.tokenize = tokenBase + return tokenBase(stream, state) + } + + function tokenComment(stream, state) { + if (stream.match(/^.*#}/)) { + state.tokenize = tokenBase + return 'comment' + } + stream.skipToEnd() + return 'comment' + } + + function tokenBase(stream, state) { + // whitespaces + if (stream.eatSpace()) return null + + // Handle one line Comments + if (stream.match('#{')) { + state.tokenize = tokenComment + stream.skipToEnd() + return 'comment' } - function tokenBase(stream, state) { - // whitespaces - if (stream.eatSpace()) return null; + if (stream.match(/^#/)) { + stream.skipToEnd() + return 'comment' + } - // Handle one line Comments - if (stream.match('#{')) { - state.tokenize = tokenComment; - stream.skipToEnd(); - return 'comment'; + // Handle Number Literals + if (stream.match(/^[0-9.+-]/, false)) { + if (stream.match(/^[+-]?0x[0-9a-fA-F]+[ij]?/)) { + stream.tokenize = tokenBase + return 'number' } - - if (stream.match(/^[#]/)) { - stream.skipToEnd(); - return 'comment'; + if (stream.match(/^[+-]?\d*\.\d+([EeDd][+-]?\d+)?[ij]?/)) { + return 'number' } - - // Handle Number Literals - if (stream.match(/^[0-9\.+-]/, false)) { - if (stream.match(/^[+-]?0x[0-9a-fA-F]+[ij]?/)) { - stream.tokenize = tokenBase; - return 'number'; - }; - if (stream.match(/^[+-]?\d*\.\d+([EeDd][+-]?\d+)?[ij]?/)) { return 'number'; }; - if (stream.match(/^[+-]?\d+([EeDd][+-]?\d+)?[ij]?/)) { return 'number'; }; + if (stream.match(/^[+-]?\d+([EeDd][+-]?\d+)?[ij]?/)) { + return 'number' } - if (stream.match(wordRegexp(numberLiterals))) { return 'number'; }; + } + if (stream.match(wordRegexp(numberLiterals))) { + return 'number' + } + // Handle Strings + let m = stream.match(/^"(?:[^"]|"")*("|$)/) || stream.match(/^'(?:[^']|'')*('|$)/) + if (m) { + return m[1] ? 'string' : 'string error' + } + // Handle words + if (stream.match(keywords)) { + return 'keyword' + } + if (stream.match(builtins)) { + return 'builtin' + } + if (stream.match(physicalConstants)) { + return 'tag' + } + if (stream.match(units)) { + return 'attribute' + } + if (stream.match(identifiers)) { + return 'variable' + } + if (stream.match(singleOperators) || stream.match(doubleOperators)) { + return 'operator' + } + if ( + stream.match(singleDelimiters) || + stream.match(doubleDelimiters) || + stream.match(tripleDelimiters) + ) { + return null + } + if (stream.match(expressionEnd)) { + state.tokenize = tokenTranspose + return null + } + // Handle non-detected items + stream.next() + return 'error' + } + return { + name: 'mathjs', - // Handle Strings - let m = stream.match(/^"(?:[^"]|"")*("|$)/) || stream.match(/^'(?:[^']|'')*('|$)/) - if (m) { return m[1] ? 'string' : "string error"; } + startState: function () { + return { + tokenize: tokenBase + } + }, - // Handle words - if (stream.match(keywords)) { return 'keyword'; }; - if (stream.match(builtins)) { return 'builtin'; }; - if (stream.match(physicalConstants)) { return 'tag'; }; - if (stream.match(units)) { return 'attribute'; }; - if (stream.match(identifiers)) { return 'variable'; }; + token: function (stream, state) { + const style = state.tokenize(stream, state) + if (style === 'number' || style === 'variable') { + state.tokenize = tokenTranspose + } + return style + }, - if (stream.match(singleOperators) || stream.match(doubleOperators)) { return 'operator'; }; - if (stream.match(singleDelimiters) || stream.match(doubleDelimiters) || stream.match(tripleDelimiters)) { return null; }; + languageData: { + commentTokens: { line: '#' }, + autocomplete: myCompletions + } + } - if (stream.match(expressionEnd)) { - state.tokenize = tokenTranspose; - return null; - }; + function myCompletions(context) { + let word = context.matchBefore(/\w*/) + if (word.from == word.to && !context.explicit) return null + let options = [] + mathFunctions.forEach((func) => options.push({ label: func, type: 'function' })) + mathPhysicalConstants.forEach((constant) => options.push({ label: constant, type: 'constant' })) - // Handle non-detected items - stream.next(); - return 'error'; - }; + numberLiterals.forEach((number) => options.push({ label: number, type: 'variable' })) - return { - startState: function () { - return { - tokenize: tokenBase - }; - }, - - token: function (stream, state) { - let style = state.tokenize(stream, state); - if (style === 'number' || style === 'variable') { - state.tokenize = tokenTranspose; + // units as enum + for (const name in math.Unit.UNITS) { + if (hasOwnPropertySafe(math.Unit.UNITS, name)) { + if (name.startsWith(word.text)) { + options.push({ label: name, type: 'enum' }) } - return style; - }, - - lineComment: '#', - - fold: 'indent' - }; - }); - - CodeMirror.defineMIME("text/x-mathjs", "mathjs"); + } + } + for (const name in math.Unit.PREFIXES) { + if (hasOwnPropertySafe(math.Unit.PREFIXES, name)) { + const prefixes = math.Unit.PREFIXES[name] + for (const prefix in prefixes) { + if (hasOwnPropertySafe(prefixes, prefix)) { + if (prefix.startsWith(word.text)) { + options.push({ label: prefix, type: 'enum' }) + } else if (word.text.startsWith(prefix)) { + const unitKeyword = word.text.substring(prefix.length) + for (const n in math.Unit.UNITS) { + const fullUnit = prefix + n + if (hasOwnPropertySafe(math.Unit.UNITS, n)) { + if ( + !options.includes(fullUnit) && + n.startsWith(unitKeyword) && + math.Unit.isValuelessUnit(fullUnit) + ) { + options.push({ label: fullUnit, type: 'enum' }) + } + } + } + } + } + } + } + } -}); + return { + from: word.from, + options + } + } +} + +// helper function to safely check whether an object has a property +// copy from the function in object.js which is ES6 +function hasOwnPropertySafe(object, property) { + return object && Object.hasOwnProperty.call(object, property) +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..72da573 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1106 @@ +{ + "name": "web-thermodynamics", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web-thermodynamics", + "version": "0.0.0", + "dependencies": { + "alpinejs": "^3.13.5", + "codemirror": "^6.0.1", + "github-markdown-css": "^5.5.1", + "katex": "^0.16.9", + "markdown-it": "^14.0.0", + "markdown-it-texmath": "^1.0.0", + "mathjs": "^12.4.0" + }, + "devDependencies": { + "vite": "^5.1.4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.12.0.tgz", + "integrity": "sha512-r4IjdYFthwbCQyvqnSlx0WBHRHi8nBvU+WjJxFUij81qsBfhNudf/XKKmmC2j3m0LaOYUQTf3qiEK1J8lO1sdg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", + "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.0.tgz", + "integrity": "sha512-2vaNn9aPGCRFKWcHPFksctzJ8yS5p7YoaT+jHpc0UGKzNuAIx4qy6R5wiqbP+heEEdyaABA582mNqSHzSoYdmg==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", + "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.5.tgz", + "integrity": "sha512-PIEN3Ke1buPod2EHbJsoQwlbpkz30qGZKcnmH1eihq9+bPQx8gelauUwLYaY4vBOuBAuEhmpDLii4rj/uO0yMA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz", + "integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==" + }, + "node_modules/@codemirror/view": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.0.tgz", + "integrity": "sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ==", + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@lezer/common": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", + "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz", + "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz", + "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz", + "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz", + "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz", + "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz", + "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz", + "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz", + "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz", + "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz", + "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz", + "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz", + "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz", + "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==" + }, + "node_modules/alpinejs": { + "version": "3.13.5", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.5.tgz", + "integrity": "sha512-1d2XeNGN+Zn7j4mUAKXtAgdc4/rLeadyTMWeJGXF5DzwawPBxwTiBhFFm6w/Ei8eJxUZeyNWWSD9zknfdz1kEw==", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/complex.js": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.1.tgz", + "integrity": "sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, + "node_modules/fraction.js": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.4.tgz", + "integrity": "sha512-pwiTgt0Q7t+GHZA4yaLjObx4vXmmdcS0iSJ19o8d/goUGgItX9UZWKWNnLHehxviD8wU2IWRsnR8cD5+yOJP2Q==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/github-markdown-css": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/github-markdown-css/-/github-markdown-css-5.5.1.tgz", + "integrity": "sha512-2osyhNgFt7DEHnGHbgIifWawAqlc68gjJiGwO1xNw/S48jivj8kVaocsVkyJqUi3fm7fdYIDi4C6yOtcqR/aEQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, + "node_modules/katex": { + "version": "0.16.9", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.9.tgz", + "integrity": "sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/markdown-it": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.0.0.tgz", + "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.0.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-texmath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-texmath/-/markdown-it-texmath-1.0.0.tgz", + "integrity": "sha512-4hhkiX8/gus+6e53PLCUmUrsa6ZWGgJW2XCW6O0ASvZUiezIK900ZicinTDtG3kAO2kon7oUA/ReWmpW2FByxg==" + }, + "node_modules/mathjs": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-12.4.0.tgz", + "integrity": "sha512-4Moy0RNjwMSajEkGGxNUyMMC/CZAcl87WBopvNsJWB4E4EFebpTedr+0/rhqmnOSTH3Wu/3WfiWiw6mqiaHxVw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "complex.js": "^2.1.1", + "decimal.js": "^10.4.3", + "escape-latex": "^1.2.0", + "fraction.js": "4.3.4", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^4.1.1" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/rollup": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz", + "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.6", + "@rollup/rollup-android-arm64": "4.9.6", + "@rollup/rollup-darwin-arm64": "4.9.6", + "@rollup/rollup-darwin-x64": "4.9.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.6", + "@rollup/rollup-linux-arm64-gnu": "4.9.6", + "@rollup/rollup-linux-arm64-musl": "4.9.6", + "@rollup/rollup-linux-riscv64-gnu": "4.9.6", + "@rollup/rollup-linux-x64-gnu": "4.9.6", + "@rollup/rollup-linux-x64-musl": "4.9.6", + "@rollup/rollup-win32-arm64-msvc": "4.9.6", + "@rollup/rollup-win32-ia32-msvc": "4.9.6", + "@rollup/rollup-win32-x64-msvc": "4.9.6", + "fsevents": "~2.3.2" + } + }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-mod": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", + "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "node_modules/typed-function": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.1.1.tgz", + "integrity": "sha512-Pq1DVubcvibmm8bYcMowjVnnMwPVMeh0DIdA8ad8NZY2sJgapANJmiigSUwlt+EgXxpfIv8MWrQXTIzkfYZLYQ==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/uc.micro": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.0.0.tgz", + "integrity": "sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==" + }, + "node_modules/vite": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", + "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..189f584 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "web-thermodynamics", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^5.1.4" + }, + "dependencies": { + "alpinejs": "^3.13.5", + "codemirror": "^6.0.1", + "github-markdown-css": "^5.5.1", + "katex": "^0.16.9", + "markdown-it": "^14.0.0", + "markdown-it-texmath": "^1.0.0", + "mathjs": "^12.4.0" + } +} diff --git a/coolprop.js b/public/coolprop.js similarity index 100% rename from coolprop.js rename to public/coolprop.js diff --git a/coolprop.wasm b/public/coolprop.wasm similarity index 100% rename from coolprop.wasm rename to public/coolprop.wasm diff --git a/style.css b/style.css index ccf6924..ab503f7 100644 --- a/style.css +++ b/style.css @@ -1,53 +1,54 @@ -html, body { - margin:0; - height: 100vh; - overflow: hidden; +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; } -.CodeMirror-focused .cm-matchhighlight { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==); - background-position: bottom; - background-repeat: repeat-x; +a:hover { + color: #535bf2; } -.cm-matchhighlight { - background-color: rgba(144, 238, 144, 0.4) +html, +body { + margin: 0; + padding: 0; + height: 100vh; + overflow: hidden; } -.CodeMirror-selection-highlight-scrollbar { - background-color: rgba(0, 180, 0, 0.4) +h1 { + line-height: 1.1; } body { display: flex; - overflow: hidden; - margin:0; - height: 100vh; flex-direction: column; } -main{ +main { display: flex; overflow: hidden; flex: 1; + flex-direction: row; } -main > div.CodeMirror{ - flex:1; - height: auto; +#editor { + flex: 1; + overflow: scroll; } -main > article.markdown-body{ - flex:1; +#output { + flex: 1; overflow: auto; box-sizing: border-box; -} - -main > .markdown-body { + margin: 0 auto; + padding: 1em; min-width: 200px; max-width: 980px; - overflow: scroll; - padding: 1em; +} + +article.markdown-body>.highligted { + background-color: rgba(0, 150, 255, 0.2); } @media (max-width: 767px) { diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..4249dfc --- /dev/null +++ b/vite.config.js @@ -0,0 +1,6 @@ +export default { + base: './', + build: { + chunkSizeWarningLimit: 1024 // kB + } +} \ No newline at end of file