diff --git a/package-lock.json b/package-lock.json index f875752b..cd185d65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2799,22 +2799,6 @@ "dev": true, "license": "MIT" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/bonjour-service": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", @@ -2834,9 +2818,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { @@ -4420,15 +4404,15 @@ } }, "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "dev": true, "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "~1.20.3", + "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", @@ -4447,7 +4431,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "~6.14.0", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", @@ -4535,9 +4519,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -7468,9 +7452,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9533,9 +9517,9 @@ } }, "node_modules/webpack-dev-server": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz", - "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.4.tgz", + "integrity": "sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA==", "dev": true, "license": "MIT", "dependencies": { @@ -9782,9 +9766,9 @@ } }, "node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", "dev": true, "license": "MIT", "engines": { diff --git a/src/abstractFields.ts b/src/abstractFields.ts index 4528d92a..c39b6a74 100644 --- a/src/abstractFields.ts +++ b/src/abstractFields.ts @@ -162,8 +162,10 @@ export class EditableField extends AbstractMathQuill { if (klass) { const newCmd = new klass(cmd); if (cursor.selection) newCmd.replaces(cursor.replaceSelection()); - newCmd.createLeftOf(cursor.show()); - this.__controller.scrollHoriz(); + if (cursor.parent?.prepareCommandInsertion(cursor, newCmd)) { + newCmd.createLeftOf(cursor.show()); + this.__controller.scrollHoriz(); + } } else { // TODO: API needs better error reporting } diff --git a/src/commands/mathBlock.ts b/src/commands/mathBlock.ts index c9eac8d9..40fd1d77 100644 --- a/src/commands/mathBlock.ts +++ b/src/commands/mathBlock.ts @@ -12,6 +12,7 @@ import { VanillaSymbol, MathElement, MathCommand, Letter, Digit, latexMathParser export const writeMethodMixin = >(Base: TBase) => class extends Base { writeHandler?: (cursor: Cursor, ch: string) => boolean; + writeLatexHandler?: (cursor: Cursor, block: MathBlock) => MathBlock | boolean; chToCmd(ch: string, options?: Options): TNode { const cons = (CharCmds[ch] as Constructor | undefined) || LatexCmds[ch]; @@ -178,16 +179,19 @@ export class MathBlock extends BlockFocusBlur(writeMethodMixin(MathElement)) { const all = Parser.all; const eof = Parser.eof; - const block = latexMathParser.skip(eof).or(all.result(false)).parse(latex); + const block = latexMathParser.skip(eof).or(all.result(false)).parse(latex); if (block && !block.isEmpty() && block.prepareInsertionAt(cursor)) { - if (cursor.parent) block.children().adopt(cursor.parent, cursor.left, cursor.right); - const elements = block.domify(); + const handlerResult = this.writeLatexHandler?.(cursor, block); + if (handlerResult === true) return; + const blockToInsert = handlerResult || block; + if (cursor.parent) blockToInsert.children().adopt(cursor.parent, cursor.left, cursor.right); + const elements = blockToInsert.domify(); cursor.element.before(...elements.contents); - cursor.left = block.ends.right; - block.finalizeInsert(cursor.options, cursor); - block.ends.right?.right?.siblingCreated?.(cursor.options, 'left'); - block.ends.left?.left?.siblingCreated?.(cursor.options, 'right'); + cursor.left = blockToInsert.ends.right; + blockToInsert.finalizeInsert(cursor.options, cursor); + blockToInsert.ends.right?.right?.siblingCreated?.(cursor.options, 'left'); + blockToInsert.ends.left?.left?.siblingCreated?.(cursor.options, 'right'); cursor.parent?.bubble('reflow'); } } diff --git a/src/commands/mathElements.ts b/src/commands/mathElements.ts index 372b70e7..adc31485 100644 --- a/src/commands/mathElements.ts +++ b/src/commands/mathElements.ts @@ -529,7 +529,7 @@ export class Digit extends VanillaSymbol { } export class Variable extends Symbol { - isItalic = false; + isItalic = true; isPartOfOperator = false; constructor(ch: string, html?: string) { @@ -1431,8 +1431,6 @@ const BracketMixin = >(Base: TBase) => } replaceBracket(brackFrag: HTMLElement, side: Direction) { - if (!(this instanceof Bracket) && !(this instanceof MathFunction)) - throw new Error('can only replace bracket for a Bracket or MathFunction'); const symbol = this.getSymbol(side); brackFrag.innerHTML = symbol.html; @@ -1660,6 +1658,36 @@ export class MathFunction extends BracketMixin(MathCommand) { return false; }; + // Only allow a SubSub to be inserted into the block before the parentheses. If anything else is contained in + // the block, then move to the content block so it will be inserted there. + this.blocks[0].writeLatexHandler = (cursor: Cursor, block: MathBlock) => { + if ( + block.ends.left === block.ends.right && + block.ends.left instanceof Bracket && + block.ends.left.ctrlSeq === '\\left(' + ) { + this.enterContentBlock('left', cursor); + const bracketContents = block.ends.left.blocks[0].children().disown(); + block.ends.left.disown(); + bracketContents.adopt(block); + return block; + } + + block.eachChild((node: TNode) => { + if (node instanceof SupSub) return true; + this.enterContentBlock('left', cursor); + return false; + }); + + return false; + }; + + this.blocks[0].prepareCommandInsertion = (cursor: Cursor, cmd: TNode) => { + if (cmd instanceof SupSub) return true; + this.enterContentBlock('left', cursor); + return true; + }; + return super.html(); } diff --git a/src/controller.ts b/src/controller.ts index 09952c97..2493d797 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -404,7 +404,6 @@ export class Controller extends ExportText( selectAll() { this.notify('move').cursor.insAtRightEnd(this.root); - while (this.cursor.left) this.selectLeft(); this.withIncrementalSelection((selectDir) => { while (this.cursor.left) selectDir('left'); }); diff --git a/src/tree/node.ts b/src/tree/node.ts index 30b5b0a6..641c2b6e 100644 --- a/src/tree/node.ts +++ b/src/tree/node.ts @@ -391,6 +391,9 @@ export class TNode { replaces(_fragment?: string | Fragment) { /* do nothing */ } + prepareCommandInsertion(_cursor: Cursor, _cmd: TNode): boolean { + return true; + } setOptions(_options: { text?: () => string; htmlTemplate?: string; latex?: () => string }) { return this; }