Open stefnotch opened 1 year ago
Hi stefnotch, great to hear you are working on an equation editor!
I wrote this editor with the sole purpose to use it for my computer algebra system and for another project I work on in my day job.
In the beginning, I was indeed using MathQuill, which is the best equation editor I've used (at this point, my equation editor has all the features I loved in MathQuill). However, MathQuill came with three major drawbacks: 1) It requires a specialized math font, rather than work with arbitrary ones. Using the MathQuill font was infeasible for my professional uses. Not having to worry about serving math fonts was a major relief for me. 2) It did not support matrices out-of-the-box. This was fixed by someone in a fork on mathquill; I made a fork of that myself here: https://github.com/tmilev/mathquill. However, I don't think matrices are supported in the main branch of mathquill, which sounds like a maintainence headache. 3) MathQuill had a dependency on jquery. This was a no-go for my professional uses. Notice that my equation editor is 100% self-contained. It is also written as a single file as that makes its uses in varioius frameworks easiest (including the one I use at work).
All three of these are must-haves for me.
As far as your use of mathML goes: seems like a great idea so long as it works in Firefox and Chrome and that it supports matrices and does not introduce any code/build dependencies.
As far as running a fully blown parser on the input during editing
: indeed, that is a great choice! I take it it is a latex parser, correct? Or do you mean a mathML parser? If indeed you meant a latex editor, then yes, I do run a serious latex math parser in the calculator equation editor too. In this way, I can paste an arbitrary LaTeX snippet anywhere in the editor. As far as I know, so does MathQuill too, and so does at least one more equation editor I know of.
As far as the unique features of my editor: the previous 3 features are fairly unique (I don't know of any other editor that works with arbtirary fonts and that has 0 external dependencies). In addition, the calculator equation editor:
4) Supports SVG output. 5) Supports Canvas output.
However, please don't implement 4) and 5) unless you have a valid use case! I did implement svg and it turned out I had no valid use case. I do have however a canvas output use case.
For the computer algebra questions:
The calculator uses a frontend-backend architecture, that is quite similar to the use of sympy you proposed. As far as running in the web browser goes: indeed, my calculator compiles from C++ to web assembly and can be ran directly in the browser: in fact it is available in calculator-algebra here:
The option used to be hidden, but I just realized that maintaining it is not that hard. So, I've unhidden the option and updated the server. As far as running python in the browser: I am sure it's doable, I've never done it myself!
The computer algebra system uses its own C++ parser, which is much more powerful than the equation editor parser as it needs to make sense of the math semantics. However, so long as you restrict yourself to proper math notation - that means, no weird notation such as sin2x - instead of the proper sin(2x) - parsing on the backend is not too bad either.
Let me know if you are interested in some feature of the calculator. I could either explain how it works if you want to code it yourself, or I could help you use the calculator - if that's feasible within your project's constraints.
Awesome, that means that our goals are really similar!
Editor
My use cases for an editor are for putting it in front of a CAS (similar to CoWasm or Swift Calcs or MathCad). And the second use-case is purely for taking lecture notes. After all, typing \frac{}{}
for the 100th time in mathematics class gets tedious over time. The hard part is that in a university setting, one can stumble upon way more mathematical constructs that one would think. Like $A \mapsto {}^{"}x{}^{"} A \enspace | \enspace {}^{"}x{}^{"}$ makes sense when writing a formal grammar.
Yes, MathQuill is really sweet, until one needs anything advanced. There are quite a few other math editors out there, but they all suffer from one dealbreaker issue or another, as far as I can tell.
I did choose MathML, partly because I thought that I would not have to deal with any hard rendering code, and partly because it supports everything I could need. MathML Core, which has been implemented in Chrome very recently, supports
It does not support rare constructs, like long division. Those have to be created manually by messing around with tables.
Amusingly, using MathML Core means that Aftermath Editor does roughly have the same unique features. It doesn't need extra fonts, supports matrices and doesn't need external dependencies. Although, I do have a WASM bundle for one part of the editor.
Parsing About the parser part, how does your editor deal with ambiguity, like if I type $e$ or $i$? Those can be variables, or they can be Euler's number and the imaginary unit respectively. Or how does the editor distinguish units from variables? $cd$ is one fun unit that comes to mind.
CAS Woah, that's cool!
However, so long as you restrict yourself to proper math notation - that means, no weird notation such as sin2x - instead of the proper sin(2x) - parsing on the backend is not too bad either
Here's one of the things I really want from an editor: It'd be sweet if the editor could output a valid syntax tree or report the ambiguities to the user as errors. I think it's nicer when the user immediately gets feedback along the lines of "computer algebra system will interpret your formula as having the following semantics". Does that make sense?
Let me know if you are interested in some feature of the calculator. I could either explain how it works if you want to code it yourself, or I could help you use the calculator - if that's feasible within your project's constraints.
Awesome! At the moment, I'm most interested in the editor. I would love to use the calculator sometime in the future.
I think the easiest way for to get started with working on anything together would be by working on features that don't need as much knowledge about the rest of the code.
e.g. The UI for an autocomplete popup for all the commands and statistics functions. Like
(of course written without any frameworks, would be silly to add React/Vue to the toolchain just for that)
I'd be happy to talk about this in a video call (Zoom, Discord, or whatever platform works for your).
By the way, you asked me to advertise my equation editor, and I thought a picture's worth a thousand words. So, here we go: a video demo of what the keyboard can do:
https://github.com/tmilev/calculator/assets/12239750/8c42e6ec-6829-4776-ba8c-3c3a8aca3a6d
CAS. For the CAS part: in my system, the equation editor parser (written in javascript) is separate from the engine parser (written in C++). The two use the same techniques, but one aims at being a formula typesetter. The other one aims to be a programming language that can read and written in latex, but is also very tolerant to broken math such as 10^-1: https://calculator-algebra.org/app?%7B%22currentPage%22%3A%22calculator%22%2C%22calculatorInput%22%3A%2210%5E-1%22%2C%22examplesWantedShown%22%3A%22true%22%7D.
For ambiguity: I deal with it on a case by case basis as I see fit. I love calling the shots in my computer algebra system! (Not so in my daily work, hehe).
The equation editor is really dumb and does nothing but typesetting. All the semantics heavy lifting is done by my C++ backend.
Or how does the editor distinguish units from variables?
It doesn't distinguish! Distinguishing between variables and units is really hard: is m+s meters plus seconds or is it the variable m plus the variable s? Or meters plus the variable s?
Since I am a mathematician by training, I never have to deal with units. So, my CAS backend assumes that everything is a variable, except for e, pi and i. e, pi and i are assumed to be regular variables, but with some built in rules: ln e = 1, sin pi = 0, i^{4n+s} = i^s.
For the syntax tree: my backend does have a built-in function to draw the syntax tree of every expression: https://calculator-algebra.org/app?%7B%22currentPage%22%3A%22calculator%22%2C%22calculatorInput%22%3A%22PlotExpressionTree%20Freeze(d%2Fdx(%20f%7B%7D(x)%2Be%5Ez))%22%2C%22examplesWantedShown%22%3A%22true%22%7D
(pardon the graphics, they were never optimized for looks).
As far as suggestions for broken syntax go: I am a true believer in the philosphy of MathQuill that says that unbalanced parentheses should not be allowed at all (hence the parentheses autobalancing; that's quite tricky to code).
An autocomplete for the available commands is a really cool idea! Your screenshot's brilliant. I take it the screenshot you have is from the AfterMath Editor? Can you advertise more about your approach?
In my CAS, e is just a regular variable. But, when you differentiate it, there's an extra rule that says that log e = 1 (I use log for natural logarithm, as is the custom among people who've been through a complex analysis course).
You can see all the internal transformations that the CAS does here:
For ambiguity: I deal with it on a case by case basis as I see fit. I love calling the shots in my computer algebra system! (Not so in my daily work, hehe).
I did work with two editors in the past, and have ran into issues with simplistic approaches. Your approach definitely seems neat for symbolic computations! For completeness, here's what two other editors are doing. Both of those editors also allow for approximate numerical evaluation, so they actually need the $e$ $e$ distinction.
On the topic of parsing and editing, I noticed that you've implemented a pretty smart fraction insertion. Like $2ab/$ turns into $\frac{2ab}{}$. $x^2/$ turns into $\frac{x^2}{}$. So the editor clearly knows what a token is. To my surprise, $f(x)/$ does not turn into $\frac{f(x)}{}$. Is that a bug, or is that related to the editor only dealing with typesetting?
I personally have a programming background, and it's not that long ago that I've had to deal with physics in high school. So supporting units in some way is a must-have feature. Even if it means teaching the user a slightly different syntax when they try to enter a common unit for the first time. I think as long as one introduces a separate syntax for units, or some very clear rules, it should be possible to unambiguously and reasonably deal with them.
The syntax tree visualization is pretty sweet!
Error reporting, by looking at the syntax tree and then guessing the intent, can actually go further.
![]()
Screenshots from https://math-editor.com/semanticEditor.html - a commercial editor that doesn't seem to be too extensible. One can't even enter $\sum_{i > 0} i$ in that editor.
And yes, unbalanced brackets shouldn't happen. Unless cursed interval notations are introduced at least. $]a, b[$ is cursed.
An autocomplete for the available commands is a really cool idea! Your screenshot's brilliant. I take it the screenshot you have is from the AfterMath Editor? Can you advertise more about your approach?
Thank you, but the screenshot is from https://www.mathcha.io/editor . (A closed source editor) Mine just has the internals set up for autocompleting, I haven't gotten around to building the UI for it.
To my surprise, does not turn into . Is that a bug, or is that related to the editor only dealing with typesetting?
Not a bug: the editor only does typesetting. If you want \frac{f(x)}{}: type 1) f(x), 2) shift left arrow 2 times to select f(x) 3) Press division.
Indeed, I implemented a heuristic to split 12/34 into a fraction. The heuristic is whatever felt naturally to me at the time when I coded it, I wouldn't sell that as the best (or worst) decision ever. The heuristic is: split the contiguous sequence of characters; put the left part on the top of the fraction, the right part in the bottom of the fraction. When the cursor is on the edge of a contiguous sequence of chracters, use the previous/next block as the numerator/denominator.
So the editor clearly knows what a token is.
Actually, it keeps track of a MathNode. A MathNode is a single typesetting mathematical entity that can be used anywhere (as an exponent, fraction, numerator, matrix entry, limit subscript, etc). MathNodes need not be consistent mathematically: ( [placeholder] +[placeholder] ^{([placeholder)+7} ) is a valid MathNode.
The backend functions the exact same way, with the two major differences: 1) In the backend it's called Expression (in the frontend - MathNode). 2) Backend Expressions must be syntactically correct semantically complete expressions (they can still be non-sensical math). MathNode's can be gibberish.
Mathcad automatically assigns a "type" to each variable My CAS is more involved here: 1) Expressions can be atoms[leafs] - single Expressions without children. 1.1) Atoms come as predefined - I think about several hundred of them; these include all the built-in functions. 1.2) Every contiguous sequence of characters that is not predefined is user-defined. 2) Expressions can have arbitrary number of children. 2.1.) Example https://calculator-algebra.org/app?%7B%22currentPage%22%3A%22calculator%22%2C%22calculatorInput%22%3A%22a%3Df%7B%7D(x%20)%3B%5CnPlotExpressionTree(a)%22%7D 2.2) The syntax for making x a child of an expression is
f{} x
. The{}
indicates that f is the function. 2.3) Inf{}x
, both f and x are declared to be children of the non-atomic expressionf{}x
. So, functions are just expressions like everything else! Constants can be used as functions too:
2.4) Functions can be manipulated just like any other expression. You can type f{}x
, then set f=sin, and then f{}x automatically becomes sin x
!
2.5) Pluses and minuses are just regular atoms: https://calculator-algebra.org/app?%7B%22currentPage%22%3A%22calculator%22%2C%22calculatorInput%22%3A%22PlotExpressionTree(a%2Bb%2Bc)%22%7D
2.6) You can in fact replace plus with times! Here's how you do it: https://calculator-algebra.org/app?%7B%22currentPage%22%3A%22calculator%22%2C%22calculatorInput%22%3A%22%5Cnx%3Da%2Bb%2Bc%3B%5Cn%5Cn%7B%7Bm%7D%7D%2B%7B%7Bl%7D%7D%3D%20%7B%7Bm%7D%7D*%7B%7Bl%7D%7D%3B%5Cnx%22%7D
And yes, unbalanced brackets shouldn't happen. Unless cursed interval notations are introduced at least
Agree! The frontend supports unbalanced parentheses using the latex construct \left(a,b\right.
https://calculator-algebra.org/app?%7B%22currentPage%22%3A%22calculator%22%2C%22calculatorInput%22%3A%22%5Cn(%20a%2Bb%20%5C%5Cright.%20%3B%22%7D
The backend doesn't support it.
3) My CAS has a complicated type system built on top of the expressions. It supports all kinds of built in C++ types. For example, there's built in support for Polynomial with rational coefficients, algebraic numbers, polynomial with algebraic coefficients, polynomials with modular coefficients, matrices, matrices with algebraic coefficients.
3.1) A typed expression is non-atomic: https://calculator-algebra.org/app?%7B%22currentPage%22%3A%22calculator%22%2C%22calculatorInput%22%3A%22PlotExpressionTree(1%2Bx)%3B%5CnPlotExpressionTreeFull(1%2Bx)%22%7D
The binding of calculator expressions <-> internal C++ implementation quite involved - it uses C++ templates and related coding techniques.
For units: I have never thought of supporting them, so there's nothing I can add to the discussion. It can be done in my model by adding an additional built-in type of course, but no idea how much work would that be.
For the letter e: it is a regular atom in my system, with just a few extra rules that say ln e = 1 and similar.
You can treat e just as any other variable:
Yes, you can evaluate to floating point too. Just use the DoubleValue atom:
Thank you so much for that in depth explanation! I think I'm starting to understand how your editor and CAS work, and how they fit together so nicely.
For the letter e: it is a regular atom in my system, with just a few extra rules that say ln e = 1 and similar.
I suppose that works really nicely, since you're controlling both the editor and the CAS. If I were to use the editor with a CAS like sympy, then I believe it would no longer work, since sympy wants to know what $e$ is upfront.
That also means that my editor has a quite different approach. It should basically work on a semantic level. Or at least have a much faster "edit math -> parse -> result and error reporting" loop. Additionally, MathML needs a ton of semantic information, so building an editor using it means parsing the input anyways.
Back to the topic that I actually wanted to ask about: Collaborating! For example, I'm planning on building an autocomplete popup. And it should be reasonable to re-use that autocomplete. Which brings me to the questions:
Hi stefnotch, I am indeed interested in getting a comfortable autocomplete component.
In my CAS, I have to enter lots of commands in text form.
This is one of the longer math workbooks I have had to work on:
%HideLHS
%DontUsePredefinedWordSplits
TurnOffRules(CommonDenominator, AddTerms, DivideExpressionsAsIfPolynomial, CommuteIfUnivariate,CommuteAtimesBtimesCifUnivariate, ConstantExtraction, AssociativeRule, DistributeMultiplication);
a_1= MakeInputBox(name=a_1, value = 0);
a_2= MakeInputBox(name=a_2, value = 0);
b_1 = MakeInputBox(name=b_1, value = 1);
b_2 = MakeInputBox(name=b_2, value = 0);
c_1 = MakeInputBox(name=c_1, value = 0.9);
c_2 = MakeInputBox(name=c_2, value = 0.9);
A= (a_1, a_2);
B= (b_1, b_2);
C= (c_1, c_2);
t_a = ((A-C)(B-C)^t)_1 / ((B-C)(B-C)^t)_1;
t_b = ((B-A)(C-A)^t)_1 / ((C-A)(C-A)^t)_1;
t_c = ((C-B)(A-B)^t)_1 / ((A-B)(A-B)^t)_1;
h_a=( b_1 t_a +c_1(1-t_a), b_2 t_a + c_2(1-t_a));
h_b=(c_1 t_b +a_1(1-t_b), c_2 t_b + a_2(1-t_b));
h_c=(a_1 t_c +b_1(1-t_c), a_2 t_c + b_2(1-t_c));
tick = 0.03;
tickSmall = 0.027;
lengthC = sqrt( (a_1-b_1)^2+(a_2-b_2)^2);
lengthA = sqrt( (b_1-c_1)^2+(b_2-c_2)^2);
lengthB = sqrt( (c_1-a_1)^2+(c_2-a_2)^2);
ab = (B-A)/ lengthC;
bc = (C-B)/ lengthA;
ca = (A-C)/ lengthB;
abperp = (-ab_2, ab_1);
bcperp = (-bc_2, bc_1);
caperp = (-ca_2, ca_1);
DrawHeel{}({{tip}}, {{base}},{{height}}) = (tip + base*tick, tip + base*tick+height*tick, tip+height*tick);
heel_a = (h_a+bc*tick, h_a+bcperp*tick+bc*tick, h_a + bcperp*tick);
heel_b = (h_b+ca*tick, h_b+caperp*tick+ca*tick, h_b + caperp*tick);
heel_c = (h_c+ab*tick, h_c+abperp*tick+ab*tick, h_c + abperp*tick);
bisector_a = (B+C)/2 + bcperp;
bisector_b = (C+A)/2 + caperp;
bisector_c = (A+B)/2 + abperp;
bheel_c = ((A+B)/2+ab*tick, (A+B)/2+abperp*tick+ab*tick, (A+B)/2 + abperp*tick);
bheel_a = ((B+C)/2+bc*tick, (B+C)/2+bcperp*tick+bc*tick, (B+C)/2 + bcperp*tick);
bheel_b = ((C+A)/2+ca*tick, (C+A)/2+caperp*tick+ca*tick, (C+A)/2 + caperp*tick);
alpha_b = Arctan2(b_2-a_2, b_1-a_1) ;
alpha_c = Arctan2(c_2-a_2, c_1-a_1);
beta_c = Arctan2(c_2-b_2, c_1-b_1) ;
protobeta_a = Arctan2(-a_2+b_2,- a_1+b_1);
beta_a =protobeta_a+pi;
gamma_a =Arctan2(a_2-c_2, a_1-c_1) ;
gamma_b = Arctan2(b_2-c_2, b_1-c_1);
M = (A+B+C)/3;
D=2(a_1(b_2-c_2)+b_1(c_2-a_2)+c_1(a_2-b_2));
U= ( (a_1^2+a_2^2)(b_2-c_2)+(b_1^2+b_2^2)(c_2-a_2)+(c_1^2+c_2^2)(a_2-b_2), (a_1^2+a_2^2)(c_1-b_1)+(b_1^2+b_2^2)(a_1-c_1)+(c_1^2+c_2^2)(b_1-a_1))/D;
H=3M-2U;
N = (H+U)/2;
nl =sqrt( (N_1-(a_1+b_1)/2)^2 + (N_2-(a_2+b_2)/2)^2);
R =sqrt( (U_1-a_1)^2+(U_2-a_2)^2);
p = lengthA + lengthB + lengthC;
I = (A lengthA + B lengthB + C lengthC)/p;
touch_c= A+ (B-A) ((I-A) (B-A)^t )_1/((B-A)(B-A)^t)_1 ;
touch_a= B+ (C-B) ((I-B) (C-B)^t )_1/((C-B)(C-B)^t)_1 ;
touch_b= C+ (A-C) ((I-C) (A-C)^t )_1/((A-C)(A-C)^t)_1 ;
touchHeel_a =DrawHeel{}(touch_a, bc, bcperp);
touchHeel_b =DrawHeel{}(touch_b, ca, caperp);
touchHeel_c =DrawHeel{}(touch_c, ab, abperp);
r = sqrt(2(p/2 - lengthA )(p/2 - lengthB )(p/2 - lengthC )/p);
L_a = (B * lengthB + C* lengthC )/(lengthB+lengthC);
L_b = (C * lengthC + A* lengthA )/(lengthC+lengthA);
L_c = (A * lengthA + B* lengthB )/(lengthA+lengthB);
PlotPath((A,h_c, B,h_a, C,h_b, A), blue)
+LayerLabel(
PlotSegment(A, (B+C)/2, purple)
+ PlotSegment(B, (C+A)/2, purple)
+ PlotSegment(C, (A+B)/2, purple)
+ PlotPoint((A+B+C)/3, orange)
+ PlotLabel(M, "M")
, "medians")
+LayerLabel(
PlotPath(heel_a, red)
+ PlotPath(heel_b, red)
+ PlotPath(heel_c, red)
+ PlotSegment(A, h_a, green)
+ PlotSegment(B, h_b, green)
+ PlotSegment(C, h_c, green)
+ PlotPoint(H, purple)
+ PlotLabel(H, "H")
, "heights")
+LayerLabel(
PlotSegment(A, L_a, brown)
+PlotSegment(B, L_b, brown)
+PlotSegment(C, L_c, brown)
+PlotCurve( (a_1+tick*cos t, a_2 + tick*sin t), alpha_b, (alpha_c+alpha_b)/2, orange,3)
+PlotCurve( (a_1+tickSmall*cos t, a_2 + tickSmall*sin t), (alpha_c+alpha_b)/2, alpha_c, orange,3)
+PlotCurve( (b_1+tick*cos t, b_2 + tick*sin t), beta_c, (beta_a+beta_c)/2, orange,3)
+PlotCurve( (b_1+tickSmall*cos t, b_2 + tickSmall*sin t), (beta_a+beta_c)/2, beta_a, orange,3)
+PlotCurve( (c_1+tick*cos t, c_2 + tick*sin t), gamma_a, (gamma_b+gamma_a)/2, orange,3)
+PlotCurve( (c_1+tickSmall*cos t, c_2 + tickSmall*sin t), (gamma_b+gamma_a)/2, gamma_b, orange,3)
+ PlotPoint(I, red)
+PlotLabel(I, "I")
, "angle bisectors")
+LayerLabel(
PlotPoint(U, red)
+PlotLabel(U, "U")
+ PlotSegment(bisector_a, (B+C)/2, orange)
+ PlotSegment(bisector_b, (C+A)/2, orange)
+ PlotSegment(bisector_c, (A+B)/2, orange)
+ PlotPath(bheel_a, red)
+ PlotPath(bheel_b, red)
+ PlotPath(bheel_c, red)
, "perpendicular bisectors"
)
+PlotLabel(A, "A")
+PlotLabel(B, "B")
+PlotLabel(C, "C")
+LayerLabel(
PlotCurve((U_1+R cos t, U_2+R sin t), 0, 2pi)
,"circumscribed circle")
+LayerLabel(
PlotCurve(I+(r cos t, r sin t), 0, 2pi), "inscribed circle"
)
+LayerLabel(
PlotPoint(touch_a, green)
+PlotPoint(touch_b, green)
+PlotPoint(touch_c, green)
+PlotSegment(touch_a, I)
+PlotSegment(touch_b, I)
+PlotSegment(touch_c, I)
+PlotPath(touchHeel_a, red)
+PlotPath(touchHeel_b, red)
+PlotPath(touchHeel_c, red)
, "touch points")
+LayerLabel(PlotSegment(H, U, brown), "Euler line")
+LayerLabel(
PlotLabel(N, "N")
+ PlotPoint(N, red)
+PlotCurve(N+(nl cos t, nl sin t), 0, 2pi)
,"9 point circle"
)
+ PlotSelectablePoint((a_1, a_2))
+ PlotSelectablePoint((b_1, b_2))
+ PlotSelectablePoint((c_1, c_2))
+ PlotRemoveCoordinateAxes()
+PlotWindow(700, 700)
Here's what this workbook does:
Pardon, the whole evaluation takes some 5 seconds - I need to optimize some part of the CAS for speed.
So, I would love to have a component that can help edit long sequences of math such as this one. I am currently editing this particular workbook in plain text using gedit (the ubuntu version of notepad, if you are a windows user).
Things to note: 1) The expressions are quite long - an editor has to be able to handle multiple relatively long expressions without hiccups (not sure that is the case with my frontend right now - sometimes things don't run as fast as I'd like them too). 2) The workbook consists of multiple formulas
2.1) The formulas are separated with a semicolon (newline separation also works, or anything else that you think would be more appropriate from the standpoint of the Aftermath Editor.I don't have any plans to change the semicolon formula separator for my CAS, but I am sure that with a good configuration both your and my use cases could be covered.
2.2) I want to be able to manipulate the whole workbook like a regular textarea / editable div component. In other words, I want to be able to select the whole thing (multiple formulas) and copy+paste them freely, just like I did in this post.
2.3) Individual sub-formulas (say, everything enclosed between two semicolons) contain keywords specific to my computer algebra system. In fact, I do have some form of autocomplete that I coded quickly, but nothing I wouldn't scrap immediately for a better version:
As you can see from the screenshot, the autocomplete I have is not a proper VS-studio-like dropdown menu. Instead, it is a list of words in a parallel tab that you can click with the mouse or use ctrl+enter to insert. It's not very comfortable to use; any improvement on it would be great. It suggests off the following keywords (but of course, this is one of the main things you should let users of your editor configure):
https://calculator-algebra.org/calculator_html/autocomplete_keywords.js
2.4) In addition to keywords specific to my CAS, I also support arbitrary LaTeX-like expressions (often intertwined with keywords). It would be amazing to have some form of equation editor helper. In fact, I do have that implemented as well, in parallel with the autocomplete menu:
2.4.1) As you can notice, the LaTeX-like formulas are a mixture of keywords and algebraic expressions. 2.4.2) If a workbook contains multiple formulas, then the equation editor would make a good helper only for individual formulas, not for the entire workbook of semicolon-separated individual formulas. 2.4.3) I have no idea how to marry the dropdown menu with a good equation editor. I always thought that I wanted a drop-down equation editor helper that would write the latex for me, and at the same time serve as a Visual-Studio-like autocomplete dropdown menu. I have no idea how to marry the two ideas together (from a product standpoint; the design seems very hard too, not just the coding).
In other words, I imagined a google-docs-style equation editor (what you get when you click "insert formula" in google docs), that would, at the same time, have an additional autocomplete dropdown menu.
But when the editing is done, I'd still like to have a single piece of text that I can select-all and copy+paste into notepad.
Can any of this be done in MathML? No idea. Also so long as you can copy+paste back to and from plain text/latex, I don't think it matters what the underlying technology is (mathML, html+DOM, SVG, canvas, ...). Copy+paste can be implemented with all of these technologies of course!
2.4.4) My marriage of keyword autocompletion with math equation editor helper with a textarea doesn't work well at all. Reason: these are laid out side-by side. As soon as the formula becomes large enough, the autocomplete menu disappears into the distance. Also the autocompletion formulas get large very fast, and really get in the way of the editable textarea they help compose. In other words, it's far from a good user experience.
Of course many of the issues I have here are design ones, and I don't haave good answers. So, if you have any ideas on how to do any of this, I would love your input/code/etc.
tl;dr: summary in case you skimmed over the above.
The workbook above is an actual piece of CAS code I work on in my calculator. It contains a mixture of latex formulas and keywords. The individual lines could be thought of as separate commands/equation entities, and could/should be edited separately. It is still important to be able to copy +paste the entire text of the workbook from a single component, with a single ctrl+A, ctrl+C command.
Sweet! What would a good autocomplete component be for you?
It'll probably take a while until I get around to making an autocomplete. Right now, my top priority is finishing my current university semester.
That's quite a big workbook. I do love the idea of having an online editor/viewer for it, and really like the URLs!
I personally used MathCad at school for such tasks, including using MathCad for my "technical high school graduation exams". (aka "HTL Matura") But there are more than enough reasons why I'm not really satisfied with MathCad, so I'm really interested in building software that can compete with it.
Yeah, the evaluation taking a while was also an annoyance with MathCad. Though, the startup time was the bigger annoyance, especially when it took nearly a minute on an older machine.
What would a good web editor look like in your opinion? There are a few different editing models that I've seen, such as "list of formulas", "text with formulas mixed in" or "worksheet of formulas like MathCad". Of course having a good formula editor is a very important part of any math editor.
2.1) Would creating a separate equation editor component for every formula be a good option?
2.2.) That would work with every editing model, as long as keyboard shortcuts and such are properly handled. For the purpose of "ctrl+a", "ctrl+c" and other shortcuts it does not really matter if it's displayed in a textbox or not. I actually made use of a few browser APIs while building my QuantumSheet prototype!
2.3) Ah, nice :)
2.4) I'm not sure I understand everything. Especially 2.4.3. Chances are that I'll stumble upon the same design difficulties once I actually work on the autocomplete. I do have a few friends who are pretty competent designers, so I'm reasonably confident that I'll eventually find a good design.
Finally, on the topic of MathML: There are a few reasons why I decided to try it out. There are also a few difficulties with it.
On 2.1) - yes a separate component for every formula is a good option: this looks like what you did in the QuantumSheet prototype, correct? Something along the lines of QuantumSheet would be very nice! My CAS assumes reevaluation of the entire expression every time (no state is kept server-side), but that shouldn't be an issue. I can simply send the entire workbook and just show the evaluation last formula; if that becomes a computational problem, I can think of a way to store partially computed state on the server side.
If you come up with a simple to use component, I could try to give you a clean API to accept commands in and ship results back - let me know if you need something in particular!
Yep, I have a separate component for every formula in QuantumSheet. (QuantumSheet also keeps track of variables, stores computed results, and then only recomputes whatever actually needs to be recomputed.)
I'm currently working on an autocomplete component!
I'm also working on a few technical things that make sense in the context of my editor. Like, I want input to be as fast and easy as possible. So typing sum i
should result in $\sum i$. That, of course, needs to work according to some rules and needs to be communicated to the user.
Quick update from the future: I got mildly sidetracked with rewriting some of my code, and also agonized over some trickier design decisions like
/
is the shortcut to insert a fraction. ^
is the shortcut for exponents. _
is the shortcut for subscripts.sum
makes sense.sum x
(note the space), then they definitely mean $\sum x$./=
? And how would one stop that from conflicting with the fraction shortcut?I didn't really find a perfect solution, so I went for the option where I just say "that's probably not a problem worth worrying about".
Suffice to say, my autocomplete component is not there yet. On the bright side, I got stuff like selecting parts of a matrix to work.
Makes total sense to me! How do you decide how you go in and out of math mode?
I actually only have a math mode, and end up designing all the other features around it. For example, text is simply anything in quotes, like "text"
. Autocompletions are also done in math mode.
Hello!
I saw your repository, and as I've been working on similar problems, I figured I'd reach out to you. It's super interesting to see the approach that you've been taking.
I'd love to collaborate, be it on core technologies, or just on small utilities. A example of a useful utility would be an accessible autocomplete popup for inserting special symbols.
As I understand it, this project has multiple parts.
Math Editor The math editor listens for keyboard events, then updates the internal representation, and outputs the result. And I'm guessing that it is possible to get a useful syntax tree from the editor. I'd be very interested in what unique features your editor has, compared to existing ones like Mathquill and Mathlive.
On that note, I am working on my own math editor, where I try my best to make it useful for use with a computer algebra system. Aftermath Editor, very much a work in progress. It has a bunch of interesting design choices, like using MathML Core for rendering, and running a fully blown parser on the input during editing.
What sort of features would you need for a math editor to be useful to you?
Computer Algebra System This is one part where I cannot be of much help, as my knowledge is not enough to work on a computer algebra system.
So far, I always took the easier approach, which is taking an existing computer algebra system and getting it to run in the web. e.g. Sympy can run in the browser using Pyodide.