Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
251 commits
Select commit Hold shift + click to select a range
128102e
Solve each machine on its own frames, matched by name
KohmeiK Aug 12, 2026
815f6de
Say which of the seven ways a mechanism failed
KohmeiK Aug 12, 2026
11b65d5
Give each mechanism its own speed, and stop saving what is selected
KohmeiK Aug 12, 2026
ed67a46
Keep the rails a slot is cut into
KohmeiK Aug 12, 2026
a9a8104
Put the modes across the top and fold the file actions away
KohmeiK Aug 12, 2026
f09feab
Answer "why can't I analyse this?" with a list
KohmeiK Aug 12, 2026
29dfcd9
Float the transport over the grid, and say which mode you are in
KohmeiK Aug 12, 2026
90b01e0
Show one analysis at a time
KohmeiK Aug 12, 2026
28eded7
Lock the geometry in both analysis modes, not just one
KohmeiK Aug 12, 2026
6749d20
Let each mechanism be driven at its own speed from the panel it is on
KohmeiK Aug 12, 2026
022ab06
Say what a mechanism is, not only what is wrong with it
KohmeiK Aug 12, 2026
7efd80d
Let the machines run apart, or together
KohmeiK Aug 12, 2026
b584530
Fix six defects an outside review found, three of them reproduced
KohmeiK Aug 12, 2026
6601782
Name the four modes the tabs now spell
KohmeiK Aug 12, 2026
7dc9dfb
Pin the two partition defects the review reproduced
KohmeiK Aug 12, 2026
648dd5d
Stop the clock before announcing it, and fit the strip on a phone
KohmeiK Aug 12, 2026
2283700
Give the playback flag back to the service that owns playback
KohmeiK Aug 12, 2026
51f9ef0
Take the design team's corrections to the chrome
KohmeiK Aug 12, 2026
2ec0a9c
Take the Edit and Analysis help panels from the mock
KohmeiK Aug 12, 2026
f82fb72
Give each analysis mode its own setup drawer, and a way out
KohmeiK Aug 12, 2026
9b4da09
Let the selection be a whole machine
KohmeiK Aug 12, 2026
35fead1
Rebuild the bottom cluster from the mock's own measurements
KohmeiK Aug 12, 2026
3429d71
Fix the four visual defects an independent sweep found
KohmeiK Aug 12, 2026
33872a4
Point the tour at the chrome that exists now
KohmeiK Aug 12, 2026
9641e72
Call the cylinder a cylinder all the way down the panel
KohmeiK Aug 12, 2026
a2e59e9
Draw the way back to the start instead of cutting to it
KohmeiK Aug 12, 2026
65d6c17
Say what each graph plots, to a reader who cannot see it
KohmeiK Aug 12, 2026
6f0e499
Tell a narrow window what its four icons are
KohmeiK Aug 12, 2026
7f86f32
Make the transport say which way, and mean it
KohmeiK Aug 12, 2026
286f343
Undo a unit change without moving the view
KohmeiK Aug 12, 2026
d5298f0
One white, one shadow, one gap
KohmeiK Aug 12, 2026
f388b4c
Let the edit panel reach the bottom, and keep its head above water
KohmeiK Aug 12, 2026
8adf24f
Synthesise a machine into the drawing, not over it
KohmeiK Aug 12, 2026
3069cb3
Read the last two card values from the tokens too
KohmeiK Aug 12, 2026
7a385fa
Six the second reviewer found
KohmeiK Aug 12, 2026
a2fe498
Reverse without moving, and put every gap on one number
KohmeiK Aug 12, 2026
198f9c8
Take the short way home, and stop one row speaking for the others
KohmeiK Aug 12, 2026
d4fd878
The transport measures the input, not the clock
KohmeiK Aug 12, 2026
b1aaa2f
Give every machine its own everything
KohmeiK Aug 13, 2026
8b756c5
Let the others keep running while one is dragged
KohmeiK Aug 13, 2026
c7b583f
Let the others keep running while one is dragged
KohmeiK Aug 13, 2026
2067a9b
Read the strip's state off the strip
KohmeiK Aug 13, 2026
2879ea6
Draw the grid as a background, on a one-two-five ladder
KohmeiK Aug 13, 2026
a98a26d
Climb the ladder every graph paper climbs
KohmeiK Aug 13, 2026
7bbf888
Put the number in the header, and the graph behind it
KohmeiK Aug 13, 2026
8481a2c
Put the panel back to the width its spacing was tuned for
KohmeiK Aug 13, 2026
bc46658
Offer to land a drag on the grid
KohmeiK Aug 13, 2026
600ae52
Let the plot start where the numbers stop
KohmeiK Aug 13, 2026
b603a69
Let the drawing follow the hand across a combined drag
KohmeiK Aug 13, 2026
6c843c4
Line the card up with the paragraph above it
KohmeiK Aug 13, 2026
3b0ec8d
Name every link on the link, and quieten the mass marks
KohmeiK Aug 13, 2026
23787db
Let a slotted bar own its channel, and drag again
KohmeiK Aug 13, 2026
9459598
Measure the chart against the box it ended up in
KohmeiK Aug 13, 2026
5417578
Give a joint's name the halo the grid's numbers wear
KohmeiK Aug 13, 2026
f886a72
Stop cutting the bottom off the shorter graphs
KohmeiK Aug 13, 2026
9a012b6
Put the trace's time under its own line
KohmeiK Aug 13, 2026
1c8d9ad
Square a dragged body up the way a dragged joint already does
KohmeiK Aug 13, 2026
08d8feb
Nothing weighs anything until someone says so
KohmeiK Aug 13, 2026
9033a19
Let a ram square up against its own far mount
KohmeiK Aug 13, 2026
6444bfa
Give the dev drawings real content, and faces
KohmeiK Aug 13, 2026
db63d09
Stand the horse up, and let the cards be read
KohmeiK Aug 13, 2026
b7c5372
Merge the analysis graph preview
KohmeiK Aug 13, 2026
49f01f6
Merge the grid visuals and snapping
KohmeiK Aug 13, 2026
c3f4e95
Reconcile the merged panels with the rest of the app
KohmeiK Aug 13, 2026
596fce6
Point the animation suite at the fixture it means
KohmeiK Aug 13, 2026
ff8040e
Twenty-one small things, and the two that made them slow
KohmeiK Aug 13, 2026
db69209
Redraw the one thumbnail that was captured mid-reload
KohmeiK Aug 13, 2026
5019a76
Say it once, in a voice that fits what happened
KohmeiK Aug 13, 2026
b11d903
Give the welded guide its couple, and the cylinder its reaction
KohmeiK Aug 13, 2026
a8c47b4
Close what the review found: the term no test carried, and two unsett…
KohmeiK Aug 13, 2026
253cda4
Give each machine its own rates, its own clock and its own grey
KohmeiK Aug 13, 2026
20ff0c6
Let Ctrl+Z put back a joint you deleted
KohmeiK Aug 13, 2026
f87a435
Say less, stack what is left, and offer the fix
KohmeiK Aug 13, 2026
df4f06c
Close the delete-undo question the inventory was holding open
KohmeiK Aug 13, 2026
1027738
Let a ram grow to reach a mount another ram carried off
KohmeiK Aug 13, 2026
be0852d
Two ways out of a badly sized drawing, on one centre line
KohmeiK Aug 13, 2026
a611603
Merge remote-tracking branch 'origin/notification-overhaul' into mult…
KohmeiK Aug 13, 2026
c9e14e4
Setup help that names the fix: gravity as a load, warnings that warn,…
KohmeiK Aug 13, 2026
5b41f84
Let the zoom warning's own fix reach the link outlines
KohmeiK Aug 13, 2026
41ae012
Close what the review found: stale views, block weight, and tracer mi…
KohmeiK Aug 13, 2026
bc98e6e
A fresh link starts with no inertia, to match its lack of mass
KohmeiK Aug 14, 2026
e0ef327
Merge force-analysis (PR 236): cylinder and welded-slide force analys…
KohmeiK Aug 14, 2026
8384ee3
Teach left-nav-modes that weight is now a load
KohmeiK Aug 14, 2026
6f80e79
Remove dead code: two unused solvers, three unused components, 40 orp…
KohmeiK Aug 14, 2026
fe5a6c5
Remove written-but-never-read state and orphaned imports
KohmeiK Aug 14, 2026
e246994
Migrate constructor injection to inject()
KohmeiK Aug 14, 2026
b5807c5
Convert the app to standalone components
KohmeiK Aug 14, 2026
5b49be3
Tighten TypeScript: ES2022 target, zero any in app code, second dead-…
KohmeiK Aug 14, 2026
2279909
Adopt signal inputs, outputs and view queries where the migrations pr…
KohmeiK Aug 14, 2026
8dc6925
Drop the one component import the standalone migration over-provisioned
KohmeiK Aug 14, 2026
863244d
Teach detail-fixes that viewChild is a signal now
KohmeiK Aug 14, 2026
c605ae9
Close out the review's findings: honest undefined, unboxed strings, o…
KohmeiK Aug 14, 2026
a111fc4
Merge codebase-cleanup: standalone, inject(), signals, ES2022, −3,300…
KohmeiK Aug 14, 2026
ba3007f
Unify the three view switches, and let the setup drawer stop where it…
KohmeiK Aug 14, 2026
d165477
One view-button component, and icons that take the colour they are given
KohmeiK Aug 14, 2026
f648a3f
Split the view controls three and three, and tell the graphs when the…
KohmeiK Aug 14, 2026
b6d0842
Reverse a rotating drive by turning the cycle round, not solving it a…
KohmeiK Aug 14, 2026
aede5fc
Stop announcing the cylinder floor, and stop spending letters inside …
KohmeiK Aug 14, 2026
d69d49a
Turn the machine round on the cycle, instead of turning the cycle round
KohmeiK Aug 14, 2026
f1159c4
Lock a part in place, and a drag that meets one learns to swing
KohmeiK Aug 15, 2026
1fddfbd
A weld cannot hide a lock, and a click is not a drag
KohmeiK Aug 15, 2026
1262438
Merge remote-tracking branch 'origin/multi-mechanism-redesign' into p…
KohmeiK Aug 15, 2026
cb2151c
Close the two doors the reviewer found, and bar the rest at the hinge
KohmeiK Aug 15, 2026
9434f44
One kind of lock, and dashes that share the outline with the selection
KohmeiK Aug 15, 2026
6db5de5
The lock is a badge on the joint it holds, and the buttons keep their…
KohmeiK Aug 15, 2026
ea05a9c
Darker, larger badges, and icons that sit where their buttons think t…
KohmeiK Aug 15, 2026
afb0d0c
A quieter badge that steps aside, and a button row that holds still
KohmeiK Aug 15, 2026
ca4706d
Draw the answer, not the way to it: no chart tween, and no rounded cu…
KohmeiK Aug 15, 2026
ebc1724
Merge part-locking (PR 238): lock a part in place, and drags that mee…
KohmeiK Aug 15, 2026
89c1ac9
Let the two lock glyphs take their button's colour, like every other …
KohmeiK Aug 15, 2026
6ed3109
Do not count clip-path geometry as a glyph painting itself
KohmeiK Aug 15, 2026
3640f78
Close the review's findings: a stale reversal, two id assumptions, an…
KohmeiK Aug 15, 2026
1b38e59
Mass properties that follow the shape, until somebody chooses otherwise
KohmeiK Aug 14, 2026
e03b1b1
A mass table in the force setup, and the cylinder's three bodies get …
KohmeiK Aug 14, 2026
34ce436
Close what the review found, from the weld that weighed a small car o…
KohmeiK Aug 14, 2026
205a008
Design 2a: the dot says where a number came from, and the panel speak…
KohmeiK Aug 15, 2026
2ada1a7
Close the review's four: the offset survives units, deletions, and ba…
KohmeiK Aug 15, 2026
3c5b043
Visual sweep: one field anatomy, ink where a person typed, room for t…
KohmeiK Aug 15, 2026
e611396
Close the visual review's eight, from the clipped button on down
KohmeiK Aug 15, 2026
f44401c
Adapt to the standalone migration underneath
KohmeiK Aug 16, 2026
ab135f9
Match the revised mock exactly: units typed into the box, like every …
KohmeiK Aug 16, 2026
f9d985c
The mass fields ARE the app's input fields now, and the legend sits f…
KohmeiK Aug 16, 2026
df5e314
Per-field markers in real Material fields, and the panel column matches
KohmeiK Aug 16, 2026
cab1e92
Round the numbers, keep a constant line, and pin the drawer's X
KohmeiK Aug 16, 2026
e06e5ca
The four things a look would have caught, and the look itself
KohmeiK Aug 16, 2026
d777cbd
Every edge on its line: the panel measured, not squinted at
KohmeiK Aug 16, 2026
6ceeaf3
The mass panel answers nine notes, and a massless body greys out whole
KohmeiK Aug 16, 2026
825eba4
Shadow what is passing under a title, and stop tying the X on z-index
KohmeiK Aug 16, 2026
c23c316
Inertia takes the room it needs, and a CoM field shows its distance d…
KohmeiK Aug 16, 2026
abba3cc
Suffix dots centre in every state, and the ram lights the part you mean
KohmeiK Aug 16, 2026
7d6c075
Confine a card's sticky title to the card, so it stops covering the X
KohmeiK Aug 16, 2026
b64769d
The CoM mark becomes a handle, and a zero-length measure stays unsaid
KohmeiK Aug 16, 2026
771671f
Centre the drawer's close button on the title, not on the title's box
KohmeiK Aug 16, 2026
52e34f7
The dragged centre snaps to the lines a reader would put it on
KohmeiK Aug 16, 2026
ca96da1
Give the drawer's X the same air on all three sides
KohmeiK Aug 16, 2026
8263e9b
The abandoned centroid stays visible, and the vertical label steps aside
KohmeiK Aug 16, 2026
2722181
Make Help one card with a section each, like Settings
KohmeiK Aug 16, 2026
c9ddf9d
Stop the feedback form reserving room for an error that is not there
KohmeiK Aug 16, 2026
7e0cbd1
Massless dims as one thought, closing the reviewer's finding
KohmeiK Aug 16, 2026
d573dc5
The centroid is a snap target, lbm is not a typo, and Mass gets the room
KohmeiK Aug 17, 2026
c89c46c
Let a link dragged by its body go where it is put
KohmeiK Aug 17, 2026
d0fdd6f
Stop phase1-drag looking for a snackbar the app no longer uses
KohmeiK Aug 17, 2026
0fdfbc6
The Masses header states where editing lives, inside the drawer's edge
KohmeiK Aug 17, 2026
b310129
Masses folds like its neighbours, and leads the force drawer
KohmeiK Aug 17, 2026
83ba2da
The warnings speak the reader's names, in fewer words
KohmeiK Aug 17, 2026
7eb74cc
One telling of the massless story at a time
KohmeiK Aug 17, 2026
07498c8
Pin the no-solution banner the solvers can no longer reach
KohmeiK Aug 17, 2026
d22e913
The banner's last real trigger, found and pinned where it lives
KohmeiK Aug 17, 2026
26a62ba
One browser's last bit no longer decides whether a pose has an answer
KohmeiK Aug 17, 2026
916ab51
Let a locked block hold its place on the slot, not its place on the grid
KohmeiK Aug 17, 2026
7da24c0
Let a hand grab a slotted bar by its slot
KohmeiK Aug 17, 2026
f4ed540
A cycle ends when the whole drawing is home, and Show position waits …
KohmeiK Aug 17, 2026
3769b5a
Stop a block's riders swinging round while it is dropped into a slot
KohmeiK Aug 17, 2026
450b4d4
Give a link attached to another link the colour it was promised
KohmeiK Aug 17, 2026
6dfdc65
The whole cycle spans the track, however many turns it takes
KohmeiK Aug 17, 2026
7f5a181
A picture to build a mechanism on top of
KohmeiK Aug 17, 2026
e05e9d3
Put the app on the keyboard, and print the keys on the controls
KohmeiK Aug 17, 2026
2660df2
Two turns read as 0 to 720, and the track's right edge is the end
KohmeiK Aug 17, 2026
a0d87bd
Open the Help drawer with its shortcut list folded away
KohmeiK Aug 18, 2026
cb4647b
A note about one joint stops shouting over the panel it sits in
KohmeiK Aug 18, 2026
1957818
A joint with nothing to graph gets only the note that says so
KohmeiK Aug 18, 2026
341efbb
Let the picture be placed by hand, and hold it to the grid it sits under
KohmeiK Aug 18, 2026
2d5bd2d
The empty force panel borrows the help card's voice and its clothes
KohmeiK Aug 18, 2026
8dae47e
The cause moves up under the title, the way out keeps the icon
KohmeiK Aug 18, 2026
36911ab
Merge mass-settings (#237) into multi-mechanism-redesign
KohmeiK Aug 18, 2026
3c76965
Merge force-gaps-and-two-turn-cycles (#240) into multi-mechanism-rede…
KohmeiK Aug 18, 2026
fdb6a45
Merge background-image (#239) into multi-mechanism-redesign
KohmeiK Aug 18, 2026
c9c9302
Tidy two blank lines left by the merge resolution
KohmeiK Aug 18, 2026
6100c9a
Aim a keyed click only once the joint has stopped moving
KohmeiK Aug 18, 2026
f93b573
Call a body what the reader has seen it called, and hold the legend s…
KohmeiK Aug 18, 2026
38300dd
Graph the effort a ram's own drive has to supply, where the ram is
KohmeiK Aug 18, 2026
3dbe8f5
Let a right-click on a link's centre-of-mass mark still open the link…
KohmeiK Aug 18, 2026
3d76a2a
Hold the canvas's keys while something stands over the canvas
KohmeiK Aug 18, 2026
acaa0dd
Keep the editing keys inside Edit, and let Delete mean a force too
KohmeiK Aug 18, 2026
0061fd0
Share the machines, not the frame one of them was paused on
KohmeiK Aug 18, 2026
b3db489
Read noise as the zero it means, and hold the legend to thirds
KohmeiK Aug 18, 2026
b3f9e08
Cut a rocking input's arc into a full cycle of samples, not a degree …
KohmeiK Aug 18, 2026
13fbe73
Draw a grounded crank as the disc it sweeps
KohmeiK Aug 18, 2026
1eaa615
Score a pose in the units its tolerance is written in
KohmeiK Aug 18, 2026
f4d82f8
Drive a synthesised four-bar with a crank that can actually turn
KohmeiK Aug 18, 2026
eff9b4c
Give every legend entry the hover hitbox, and re-solve the kept coars…
KohmeiK Aug 18, 2026
fc8b84e
Say what a placed centre of mass is held against, and mean it
KohmeiK Aug 18, 2026
3728531
Draw the two shape icons so both surfaces draw them the same
KohmeiK Aug 18, 2026
26d23d9
Say what the handle does at the end of the track, and let the row hol…
KohmeiK Aug 18, 2026
b081bc3
Let the fill be the whole of the selected state, as the mock draws it
KohmeiK Aug 18, 2026
47a80e8
Say why a link cannot be drawn as a disc, where it can be read
KohmeiK Aug 18, 2026
5513d4a
Give back the pointerdown Safari owes us after a native menu closes
KohmeiK Aug 18, 2026
c0e3004
Put the reason a link cannot be a disc back on hover, where a reason …
KohmeiK Aug 18, 2026
2dede8a
Draw the handle's bar rather than let each platform theme it
KohmeiK Aug 18, 2026
f46b10a
Give the handle the mock's own size, and a bar that reads on a chosen…
KohmeiK Aug 18, 2026
9cceef2
Square off the transport's play button
KohmeiK Aug 18, 2026
a4d5998
Ask what to export, instead of guessing from the selection
KohmeiK Aug 19, 2026
051f934
Say the unit an angular column is actually in, and reach the parts th…
KohmeiK Aug 19, 2026
f31cabd
Fill the report's pages, and say on the row what the row will write
KohmeiK Aug 19, 2026
09ee230
Leave the report's heads room, and count its pages from the plan that…
KohmeiK Aug 19, 2026
7058b0c
Ask the browser how wide a number is, instead of holding a table of g…
KohmeiK Aug 19, 2026
fd3bcad
Take the list into the drawing, and the report into the canvas
KohmeiK Aug 19, 2026
263fa82
Point at one thing at a time, and put the report's picture on the mac…
KohmeiK Aug 20, 2026
613433e
Fill the first page with graphs, and spread a wide table evenly
KohmeiK Aug 20, 2026
91bee38
Ask the canvas what it is holding, and say what is about to arrive
KohmeiK Aug 20, 2026
2c3c933
Tell a shared joint's two machines apart, and make the chrome one set…
KohmeiK Aug 20, 2026
18916df
Give every picture its own name, and break a graph where the solver did
KohmeiK Aug 20, 2026
5f0ccd3
Ask the three positions for every four-bar they admit, not just one
KohmeiK Aug 21, 2026
b08464c
Keep the answer out of the drawing until someone asks for it
KohmeiK Aug 21, 2026
255b959
Rebuild the Synthesis panel around choosing between solutions
KohmeiK Aug 21, 2026
010209b
Put the design on the grid, and let the pointer take hold of it
KohmeiK Aug 21, 2026
0aa520e
Carry the design in the URL, so it can be undone and shared
KohmeiK Aug 21, 2026
b723f78
Walk the whole of Synthesis in a browser, from empty grid to inserted…
KohmeiK Aug 21, 2026
9ae5a23
Say what Synthesis is waiting for on the strip that says what everyth…
KohmeiK Aug 21, 2026
7401303
Ask a ram about both its ends, and stop re-solving a cycle to watch i…
KohmeiK Aug 21, 2026
62d54a7
Ask forces as a question, not as a tab, and make one help mark of the…
KohmeiK Aug 21, 2026
d1f05f0
Fix six things that made the panel harder to use than it needed to be
KohmeiK Aug 21, 2026
c5b176e
Leave a slot off a list nobody can point at, and space the rule by it…
KohmeiK Aug 21, 2026
ffc6533
Let Synthesis keep hold of the linkage it made, and keep the position…
KohmeiK Aug 21, 2026
985bd79
Give a slider one row, and a reaction one column
KohmeiK Aug 21, 2026
b728e51
Draw the preview the way the drawing draws, and stop sending a nudge …
KohmeiK Aug 21, 2026
75350e9
Say the same thing about a slider on the panel as in the file
KohmeiK Aug 21, 2026
6ae3147
Draw a proposed solution broken, and take back two lines that said no…
KohmeiK Aug 21, 2026
f6d158a
Weigh the slider from its own panel, and let the drawing name the car…
KohmeiK Aug 21, 2026
de2d16e
Let the setup drawer turn gravity on, instead of pointing at Settings
KohmeiK Aug 21, 2026
54493b2
Say one word per thing, and give the panel back the space it was wasting
KohmeiK Aug 21, 2026
cbbe5a3
Frame the machine, not the mark hanging ninety thousand units off it
KohmeiK Aug 21, 2026
18359e3
Sample the export in slices, and tell it when the drive turns round
KohmeiK Aug 21, 2026
2fc2061
Put the driver's ceiling where the comment says it is
KohmeiK Aug 21, 2026
e5dd057
Merge synthesis-and-circular-links (#241) into multi-mechanism-redesign
KohmeiK Aug 21, 2026
3cabf23
Merge export-flow (#242) into multi-mechanism-redesign
KohmeiK Aug 21, 2026
bead4e5
Stop offering solutions that stop at the first position
KohmeiK Aug 21, 2026
f35f158
Drive the six-bar from its own crank, and stop listing one linkage twice
KohmeiK Aug 21, 2026
3386a58
Teach the first build on the drawing, not the chrome (#244)
KohmeiK Aug 21, 2026
6199e63
One button for the next step, and a driver switch that says when it i…
KohmeiK Aug 21, 2026
fff8457
Let the bar speak for itself, go to the answer, and give headers thei…
KohmeiK Aug 21, 2026
10a48fc
Merge remote-tracking branch 'origin/export-flow' into land-tutorial
KohmeiK Aug 21, 2026
d2aebc4
Let the offer be turned down, and stop it sitting so far from the panel
KohmeiK Aug 21, 2026
8097aa4
Draw the coupler's path through the solve the preview uses, not a dif…
KohmeiK Aug 21, 2026
730fcaf
Take the review's four correctness findings, and the test that could …
KohmeiK Aug 21, 2026
3c5ceed
Draw the letters the panel refers to, and let Space press a button
KohmeiK Aug 21, 2026
6ba873f
Size the ghost before it is drawn, stop re-solving the far pin, and w…
KohmeiK Aug 21, 2026
7fe0e17
One commit per press, one way of arming, and the short way home
KohmeiK Aug 21, 2026
c1a926f
One press, one entry in the history
KohmeiK Aug 21, 2026
44e40ac
A pin keeps its name, and a press builds what it was aimed at
KohmeiK Aug 21, 2026
8d3827a
Carry the letters onto the grid with the pins
KohmeiK Aug 21, 2026
4c5e8c4
Remember that the linkage was cut into, and notice when the driver ch…
KohmeiK Aug 21, 2026
12ac2b9
Do not claim to be entangled with nothing
KohmeiK Aug 21, 2026
8c39e0a
Hand out the letters the linkage is about to give back
KohmeiK Aug 21, 2026
11ed094
Three checks that were reporting on nothing
KohmeiK Aug 21, 2026
6e90162
Write down where the linkage was put, so a link knows what has moved
KohmeiK Aug 21, 2026
9e1451b
Merge multi-mechanism-redesign into synthesis-redesign
KohmeiK Aug 21, 2026
8768a4d
Merge synthesis-redesign (#243) into multi-mechanism-redesign
KohmeiK Aug 21, 2026
39f88c4
Right-click menu: a ladder, and a reason on every greyed row (#245)
KohmeiK Aug 22, 2026
8cb83b5
Loose ends: frame the canvas you can see, colour a joint or a force, …
KohmeiK Aug 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Animation (`animate()`) steps through the precomputed timesteps on a ~16 ms setT

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[conventions · confirmed] AGENTS.md still describes the architecture this PR deleted, on five load-bearing points: :74 says tsconfig.spec.json deliberately includes src/app/app.module.ts (this PR deleted both the file and the entry); :99 says "The mechanisms array only ever uses index 0 (multi-mechanism support was planned, never built)" — the PR's headline feature; :112 documents Link → RealLink / Piston (no Piston class exists); :119 describes three left-panel tabs (there are four); :122 points at AnimationBarComponent.animate (deleted). The PR touched this file (one line) while CLAUDE.md's rewrite corrects every one of these — so the repo's two governing agent docs now contradict each other, and an agent reading this one will re-add a deleted file to tsconfig, hunt for a Piston class, and assume index 0. Worth the same rewrite pass CLAUDE.md got, or collapsing AGENTS.md to a pointer at CLAUDE.md.

### Solvers (`src/app/model/mechanism/`)

Pure computation, mostly static classes: `loop-solver` (finds kinematic loops), `position-solver`, `kinematic-solver` (velocity/acceleration), `force-solver`, `ic-solver` (instant centers). `app.component.spec.ts` numerically verifies these against MATLAB results (`SixBarVerification.m`) for a sixbar linkage — treat it as the regression test for solver changes.
Pure computation, mostly static classes: `loop-solver` (finds kinematic loops), `position-solver`, `kinematic-solver` (velocity/acceleration), `force-solver`. `app.component.spec.ts` numerically verifies these against MATLAB results (`SixBarVerification.m`) for a sixbar linkage — treat it as the regression test for solver changes.

### Model classes (`src/app/model/`)

Expand Down
46 changes: 35 additions & 11 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ The same command is how to get a second opinion on a hard non-UI problem.
`Model metadata for 'gpt-5.6' not found. Defaulting to fallback metadata` warning that reads like
it worked. Verified against codex-cli 0.146.0.

Tests are Vitest but written in Jasmine style (globals via `vitest/globals`). `tsconfig.spec.json` deliberately includes `src/app/app.module.ts` — without it, NgModule-declared components lose their template scope under the per-file test compile and fail with NG8001. Vitest errors on spec files containing no tests.
Tests are Vitest but written in Jasmine style (globals via `vitest/globals`). Vitest errors on spec files containing no tests.

The app is **fully standalone** — `src/main.ts` calls `bootstrapApplication`, there is no `AppModule` and no `NgModule` anywhere, and components declare their own `imports`. A component spec must therefore import the component itself rather than a declaring module. (`tsconfig.spec.json` used to include `src/app/app.module.ts` to keep NgModule-declared components' template scope under the per-file test compile; that file and that workaround are both gone.)

Unit specs stay co-located in `src/**/*.spec.ts`; browser-driven E2E tests are Playwright scripts in `e2e/*.mjs` (run directly — see above), with outputs in gitignored `artifacts/`. Details in `e2e/README.md`.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[docs · confirmed live] The documented branch-deploy pattern https://[BRANCHNAME]--pmks.netlify.app returns a Netlify "Site not found" 404 — the real deploys are on --pmksprod (this branch: https://multi-mechanism-redesign--pmksprod.netlify.app, verified 200). Anyone following the doc to review a PR gets a dead link.

Expand Down Expand Up @@ -79,31 +81,53 @@ Changing the encoding format breaks previously shared URLs — the transcoder fo

### MechanismService is the central hub

`services/mechanism.service.ts` (root singleton) owns the **editable** mechanism: `joints`, `links`, `forces` arrays representing the state at timestep 0. All create/delete/weld/ground/slider/input mutations live here. `updateMechanism()` rebuilds `this.mechanisms[0]` — a `Mechanism` (`model/mechanism/mechanism.ts`) that deep-copies the current state and, if the linkage is valid (DOF = 1 with an input joint), precomputes joint/link/force positions for **all** timesteps up front. The `mechanisms` array only ever uses index 0 (multi-mechanism support was planned, never built).
`services/mechanism.service.ts` (root singleton) owns the **editable** drawing: `joints`, `links`, `forces` arrays representing the state at timestep 0. All create/delete/weld/ground/slider/input mutations live here.

`updateMechanism()` first **partitions** the drawing into independently solvable machines (`model/mechanism/mechanism-partition.ts`), then builds one `Mechanism` (`model/mechanism/mechanism.ts`) per partition — each deep-copies its own part of the state and, if that machine is valid (DOF = 1 with an input joint), precomputes joint/link/force positions for **all** its timesteps up front. `partitions[i]` and `mechanisms[i]` are the same machine seen two ways, and stay in the same order. One drawing can therefore hold several machines (M1, M2…), each with its own input, speed, direction and playback row — so **do not assume index 0**; ask `partitions`. Note `partition.joints` is everything the solver must be handed (shared frame pieces included) while `partition.ownJoints` is what that machine is actually made of — the one to ask "is this mine?".

`onMechUpdateState` (BehaviorSubject<number>) broadcasts mechanism state: 0 = normal, 1 = being dragged (graphs disabled), 2 = pending graph redraw, 3 = pending analysis after add/remove.

Animation (`animate()`) steps through the precomputed timesteps on a ~16 ms setTimeout loop, mutating the editable joints/links/forces in place. Editing is only allowed when the animation is paused at timestep 0.
Animation ticks on a ~16 ms setTimeout (`FRAME_INTERVAL_MS`) but advances by **elapsed real time**, not one precomputed timestep per tick: samples are spaced a fixed amount of input travel apart (1 degree of crank rotation for a pin), so how much simulated time one sample covers depends on the input speed. That is what makes a faster input speed animate faster and one revolution take 60/RPM seconds regardless of frame rate. `animate(progress, playing)` mutates the editable joints/links/forces in place; any external call is treated as a seek. Machines run on one shared clock by default, and can be unsynced to run on their own. Editing is only allowed when the animation is paused at timestep 0 (`isPlaying || mechanismTimeStep !== 0` gates the Edit panel).

### Solvers (`src/app/model/mechanism/`)

Pure computation, mostly static classes: `loop-solver` (finds kinematic loops), `position-solver`, `kinematic-solver` (velocity/acceleration), `force-solver`, `ic-solver` (instant centers). `app.component.spec.ts` numerically verifies these against MATLAB results (`SixBarVerification.m`) for a sixbar linkage — treat it as the regression test for solver changes.
Pure computation, mostly static classes: `loop-solver` (finds kinematic loops), `position-solver`, `kinematic-solver` (velocity/acceleration), `force-solver`. `app.component.spec.ts` numerically verifies these against MATLAB results (`SixBarVerification.m`) for a sixbar linkage — treat it as the regression test for solver changes.

### Model classes (`src/app/model/`)

- Joints: `Joint` → `RealJoint` → `RevJoint` (revolute) / `PrisJoint` (prismatic). Code frequently narrows with `instanceof RealJoint` guards.
- Links: `Link` → `RealLink` / `Piston`. A **welded** compound link is a `RealLink` whose `subset` holds its constituent sub-links; weld/unweld logic in MechanismService restructures joints' `links`/`connectedJoints` arrays and link IDs (link IDs are the concatenated, sorted joint letters).
- Links: `Link` → `RealLink` / `SliderBlock` (the block that rides a slot; there is no `Piston` class any more). A **welded** compound link is a `RealLink` whose `subset` holds its constituent sub-links; weld/unweld logic in MechanismService restructures joints' `links`/`connectedJoints` arrays and link IDs (link IDs are the concatenated, sorted joint letters).
- A driven joint carries its own speed: `Joint.driveSpeed`, signed for direction, in rpm for a pin and length/second for a slider. Zero means "use the document-wide default" — which is what every URL written before this existed says. A drawing with several machines needs one speed per machine, so it lives on the joint rather than in settings.
- `mechanism/readiness.ts` produces the per-machine blocker/warning list the mode chips and setup drawers show; `mechanism/actuator.ts` decides what can be driven.
- Joint IDs are single letters assigned alphabetically (`determineNextLetter`).
- `utils.ts` is a large grab-bag: interaction state enums (`gridStates`, `jointStates`, `linkStates`, `forceStates`), unit enums (`LengthUnit`, `GlobalUnit`, ...), and geometry helpers.

### UI layer

- `AppComponent` is just a shell that registers SVG icons; `component/new-grid/new-grid.component.ts` is the real center — the SVG canvas handling the mouse/touch interaction state machine and context menus, with pan/zoom via `SvgGridService` (svg-pan-zoom + hammerjs).
- Left panel tabs (Synthesis / Edit / Analyze) are coordinated by `SelectedTabService` (`TabID` enum); the Edit and Analyze panels operate on whatever `ActiveObjService` says is selected (joint, link, force, or synthesis pose).
- `SettingsService` exposes global settings as RxJS BehaviorSubjects (units, input speed, gravity, grid visibility).
- `component/BLOCKS/` holds the reusable form primitives (input, toggle, radio, dual-input, panel-section, ...) that the panels are composed from; `component/MODALS/` holds dialogs.
- Components also communicate through static members (e.g. `NewGridComponent.sendNotification()`, `AnimationBarComponent.animate`) — grep for the static before assuming a service is the only channel.
- Four-bar synthesis (generating a linkage from desired end-effector poses) lives in `services/synthesis/`.
**Where things are on screen.** `app.component.html` is the whole layout, and it is worth reading before describing the UI — the arrangement below replaced an earlier one with a horizontal file toolbar and a *vertical mode rail down the left*, and stale descriptions of that older layout have outlived it in more than one place.

| Region | Component | Holds |
| --- | --- | --- |
| Top strip | `app-top-bar` | project menu + logo · the four **mode tabs**, each with a readiness chip · a corner card that is Undo/Redo in Synthesis/Edit and swaps to **Export Data** in the analysis modes |
| Left card (below the strip, `left: 0`) | `app-left-tabs` | the current mode's panel — Synthesis form, Edit properties, or Analysis graphs. 250px, widening to 400px in analysis |
| Canvas (full bleed, behind everything) | `app-new-grid` | grid, right-click context menus, drag |
| Bottom centre | `app-playback-bar` | transport card (speed · play/pause · stop-to-start) and a scrub card with **one row per machine** |
| Bottom right | `app-view-controls` | centre of mass · joint IDs · traced paths ‖ zoom out · zoom in · reset view |
| Bottom strip | `app-bottombar` | mode name · status · cursor coords · units |
| Right drawer | `app-right-panel` | Settings, Help/Feedback, the two analysis setups, Export Data |

The **modes are tabs in the top strip, not a left rail**, and there are four of them — Synthesis, Edit, Kinematic Analysis, Force Analysis (`TabID`) — not three. The left card is that mode's panel.

- `AppComponent` is just a shell that registers SVG icons; `component/new-grid/new-grid.component.ts` is the real center — the SVG canvas handling the mouse/touch interaction state machine, with pan/zoom via `SvgGridService` (svg-pan-zoom + hammerjs).
- The right-click menu is **built in a service, not in the canvas**: `services/context-menu-builder.service.ts` turns whatever was right-clicked, plus the current mode, into a `ContextMenuModel` (`component/context-menu/menu-model.ts`), and `component/context-menu/` renders it. Every greyed row quotes the model that enforces it — `describeActuatorRefusal` in `model/actuator.ts`, `weldRefusal` in `grid-utils`, `locksHolding` in `model/lock-set.ts` — rather than restating the rule, so the menu, the panel and the drag ring cannot disagree. New rows belong in the builder; the canvas only supplies the gesture handlers (`MenuHandlers`).
- `SelectedTabService` (`TabID` enum) coordinates the four modes; the Edit and analysis panels operate on whatever `ActiveObjService` says is selected (joint, link, force, mechanism, background image, or synthesis pose).
- The right drawer is addressed by number through statics on `RightPanelComponent`: 1 Settings, 3 Help, 4 Debug (dev only), 5 `KINEMATIC_SETUP_TAB`, 6 `FORCE_SETUP_TAB`, 7 `EXPORT_TAB`. **Tab 2 (`app-equation-panel`) is unreachable** — nothing calls `tabClicked(2)` and its content is placeholder images. It is unfinished work, not a feature.
- `SettingsService` exposes document-wide settings as RxJS BehaviorSubjects (units, gravity, grid and snap visibility, object scale). Input **speed and direction are not global** — they belong to the driven joint (`Joint.driveSpeed`), because a drawing can hold several machines; the SettingsService values are only the default a joint falls back to.
- `component/BLOCKS/` holds the reusable form primitives (input, toggle, radio, dual-input, panel-section, ...) that the panels are composed from; `component/MODALS/` holds the two dialogs (Templates, touchscreen warning).
- Messages to the user go through `NotificationService`, which replaced the old `NewGridComponent.sendNotification()` static. Some components still talk through statics (e.g. `RightPanelComponent.openTab` / `insistOn`) — grep for the static before assuming a service is the only channel.
- Four-bar synthesis (generating a linkage from three desired coupler poses) lives in `services/synthesis/`.
- Onboarding is the **tutorial**: `services/tutorial.service.ts` with `model/tutorial-steps.ts`, shown by `component/tutorial-panel/` as a card *pinned* in the right drawer above whatever page is open (it is not one of the numbered pages). Its step is derived from the drawing by `progressFor`, never counted, which is what lets it start on a half-built mechanism and follow an undo backwards. It is offered from the Edit panel's empty state, reopened from the project menu, and remembers in `localStorage` (`tutorialSeen`) that it has been finished, dismissed or walked out of. The `intro.js` overlay tour it replaced is gone, dependency and all.
- The tutorial card asks the drawing for its step from `ngDoCheck`, not a subscription: every edit ends in `updateMechanism`, which publishes on nothing that could be listened to — `onMechUpdateState` carries the *analysis* state, which is why caches elsewhere key on `poseRevision` instead.

### Misc

Expand Down
Loading