Skip to content

Releases: msavox/cleecode

v0.13.1

Choose a tag to compare

@github-actions github-actions released this 24 Aug 13:16

What's new in 0.12.5

The Octave frame speedup from 0.12.3 is reverted. It wrote gnuplot's own chatter into the
transcript of a session over ssh — multiplot> set style increment default; line 0: warning: deprecated command, once per frame of an animation — and, on that machine, a figure window
where there should have been a tab. drawnow (TERM, FILE) draws through the figure's live
gnuplot stream, whose stderr is the shell's and whose terminal is whatever the display offers;
print runs a gnuplot of its own and says nothing. On the machine it was developed on that
stream never opened at all, which is precisely why every measurement looked clean.

Frames are back to where they were before 0.12.3 — slower, quiet, and windowless. The route is
not wrong in principle and the note in cleecode_figs.m records what it cost and what would have
to be proved before it comes back: a Linux box with a display forwarded over ssh, which is the
case that broke.

What's new in 0.12.4

Where plots go now takes effect without restarting CleeCode. Reported as "the switch is
ignored unless I restart clee", and it was half true: a shell opened after the toggle did get
the new answer — but a shell that was already open could not, because a process's environment is
a copy taken when it started and nothing outside can change it. So the octave or python3 you
typed next, at the prompt you had been working in all day, went on doing what the old setting
said. CleeCode now also writes the answer to a file both hooks read as the interpreter starts,
so the next one honours the switch wherever it is started from. A session already running keeps
what it was born with, which is not a policy but a fact about processes — and the status line now
says "from the next Octave or Python you start" rather than the vaguer "next session".

A figure's buttons say when they cannot work. The six on the left of a figure's bar — pan,
rotate, reset, save — do not touch the picture: they ask the session that drew it to redraw. With
that session gone, which is the normal state of a figure that came from ▶ Run, they could do
nothing, and they were drawn exactly like the buttons that still worked. Their refusal was a line
in the status bar, the easiest thing on screen to miss. They are now dimmed when there is no
interpreter behind the figure.

And the bar is easier to hit. No button is narrower than five cells — + was three, which is
a target the size of a full stop — and the single column of space between two buttons now belongs
to one of them instead of to neither, so a click a column wide of the mark still lands. Nothing
moved on screen; the targets grew.

What's new in 0.12.3

Octave animations are four to five times faster. Reported from a session over ssh, where
Python animated smoothly and Octave crawled — and it was not ssh, and not the toolkit. It was
print: measured at 155 ms a frame for a 500-point line at 800x600, and 150 ms at 400x300, and
159 ms at half the resolution. The cost is not in the pixels, it is print's own machinery, which
copies the figure into a hidden one and takes the toolkit right round again every frame.
matplotlib's savefig does the same job in 11 ms.

Under gnuplot — which is what a session with no display gets, so every ssh session — there is a
way round it: drawnow (TERM, FILE) hands the figure to the toolkit's png terminal with none of
that in between. Through the real hook, on the same machine and the same figures:

before now
animated line, 800x600 243 ms/frame 55 ms/frame
3-D surface with a colorbar, 640x480 345 ms 74 ms
image, 500x400 233 ms 47 ms

The picture is the same picture: the grid is still drawn by CleeCode as real line objects, so it
survives the change of route. Only under gnuplot, and only when what lands on disk is the PNG
that was asked for — under qt the terminal argument is ignored and the file written is PostScript
with a .png name, so the magic bytes and the width and height are read back before the file is
accepted. Either check failing is a print, exactly as before.

What's new in 0.12.2

A markdown file with a picture in it becomes a document again. Reported as "the README of
CleeCode only previews as text, the small notes render fine", and the difference between the two
was a picture. pandoc extracts a document's images into a temporary directory and hands typst
absolute paths to them — and typst resolves an absolute path against its root, not against the
filesystem, so it looked for /private/var/…/media/docs/demo.gif under the working directory and
reported it missing. The engine is now told where its root is. Every markdown file with an image
was affected, and the fallback to styled text is silent by design, which is why it read as
"the graphical preview stopped working".

And when a preview does fail, the status line says why. It showed pandoc's last line —
"Error producing PDF." — which is true and useless. The engine's own report is above it: typst
opens with error:, TeX with !, and either now reaches the status line.

Open outside CleeCode. New first entry in the file tree's right-click menu (and in the
command palette): it hands the file to whatever the desktop opens that kind with — a PDF to a
reader, a .md to a browser, anything CleeCode can only show. open on macOS, xdg-open on
Linux, start on Windows. Over ssh it refuses and says so, rather than opening a window on the
machine at the other end.

What's new in 0.12.1

Three settings were drawn off the bottom of the box. The settings modal is sized from one
number and the cursor wraps on it, and that number had said nine since the first commit while the
list grew to twelve. Where plots open, Mouse enabled and Language were below the fold:
invisible, unreachable by the arrows, and not clickable either — which is why the only way to
change the plot destination was the Run menu or the file on disk. The box now takes its size from
the rows it has, a test fails if the two ever disagree again, and each row's value is set against
the right-hand edge instead of a fixed column that the language-server label had outgrown.

A change made in the settings modal now takes effect, and survives. Picking a row wrote the
struct and nothing else: the plot destination also lives in an atomic the shells are started
from, so the row nobody could reach would not have worked if they had. Nothing was written to
disk until a clean quit either, so a change made there and a terminal closed by its own button
cancelled each other out. Both are fixed for every row at once.

The plot switch says which way it is set. Plots: tabs or windows in the Run menu asked the
question and answered neither — you had to flip it and read the status line, which is a question
you cannot ask without also answering it. It now reads out tabs or windows on the right, in
the column the shortcuts use, and it reads out the effective destination: on a machine with no
screen it says tabs, because that is where the figures are going whatever the setting says.

The splash screen is a setting. An argument on the command line has always skipped it, so
clee src/main.rs went straight to work while a bare clee showed the turtle for one and
eight tenths of a second. Splash screen at startup, off, skips it for the bare clee too.

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave...

Read more

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 24 Aug 12:48

What's new in 0.12.5

The Octave frame speedup from 0.12.3 is reverted. It wrote gnuplot's own chatter into the
transcript of a session over ssh — multiplot> set style increment default; line 0: warning: deprecated command, once per frame of an animation — and, on that machine, a figure window
where there should have been a tab. drawnow (TERM, FILE) draws through the figure's live
gnuplot stream, whose stderr is the shell's and whose terminal is whatever the display offers;
print runs a gnuplot of its own and says nothing. On the machine it was developed on that
stream never opened at all, which is precisely why every measurement looked clean.

Frames are back to where they were before 0.12.3 — slower, quiet, and windowless. The route is
not wrong in principle and the note in cleecode_figs.m records what it cost and what would have
to be proved before it comes back: a Linux box with a display forwarded over ssh, which is the
case that broke.

What's new in 0.12.4

Where plots go now takes effect without restarting CleeCode. Reported as "the switch is
ignored unless I restart clee", and it was half true: a shell opened after the toggle did get
the new answer — but a shell that was already open could not, because a process's environment is
a copy taken when it started and nothing outside can change it. So the octave or python3 you
typed next, at the prompt you had been working in all day, went on doing what the old setting
said. CleeCode now also writes the answer to a file both hooks read as the interpreter starts,
so the next one honours the switch wherever it is started from. A session already running keeps
what it was born with, which is not a policy but a fact about processes — and the status line now
says "from the next Octave or Python you start" rather than the vaguer "next session".

A figure's buttons say when they cannot work. The six on the left of a figure's bar — pan,
rotate, reset, save — do not touch the picture: they ask the session that drew it to redraw. With
that session gone, which is the normal state of a figure that came from ▶ Run, they could do
nothing, and they were drawn exactly like the buttons that still worked. Their refusal was a line
in the status bar, the easiest thing on screen to miss. They are now dimmed when there is no
interpreter behind the figure.

And the bar is easier to hit. No button is narrower than five cells — + was three, which is
a target the size of a full stop — and the single column of space between two buttons now belongs
to one of them instead of to neither, so a click a column wide of the mark still lands. Nothing
moved on screen; the targets grew.

What's new in 0.12.3

Octave animations are four to five times faster. Reported from a session over ssh, where
Python animated smoothly and Octave crawled — and it was not ssh, and not the toolkit. It was
print: measured at 155 ms a frame for a 500-point line at 800x600, and 150 ms at 400x300, and
159 ms at half the resolution. The cost is not in the pixels, it is print's own machinery, which
copies the figure into a hidden one and takes the toolkit right round again every frame.
matplotlib's savefig does the same job in 11 ms.

Under gnuplot — which is what a session with no display gets, so every ssh session — there is a
way round it: drawnow (TERM, FILE) hands the figure to the toolkit's png terminal with none of
that in between. Through the real hook, on the same machine and the same figures:

before now
animated line, 800x600 243 ms/frame 55 ms/frame
3-D surface with a colorbar, 640x480 345 ms 74 ms
image, 500x400 233 ms 47 ms

The picture is the same picture: the grid is still drawn by CleeCode as real line objects, so it
survives the change of route. Only under gnuplot, and only when what lands on disk is the PNG
that was asked for — under qt the terminal argument is ignored and the file written is PostScript
with a .png name, so the magic bytes and the width and height are read back before the file is
accepted. Either check failing is a print, exactly as before.

What's new in 0.12.2

A markdown file with a picture in it becomes a document again. Reported as "the README of
CleeCode only previews as text, the small notes render fine", and the difference between the two
was a picture. pandoc extracts a document's images into a temporary directory and hands typst
absolute paths to them — and typst resolves an absolute path against its root, not against the
filesystem, so it looked for /private/var/…/media/docs/demo.gif under the working directory and
reported it missing. The engine is now told where its root is. Every markdown file with an image
was affected, and the fallback to styled text is silent by design, which is why it read as
"the graphical preview stopped working".

And when a preview does fail, the status line says why. It showed pandoc's last line —
"Error producing PDF." — which is true and useless. The engine's own report is above it: typst
opens with error:, TeX with !, and either now reaches the status line.

Open outside CleeCode. New first entry in the file tree's right-click menu (and in the
command palette): it hands the file to whatever the desktop opens that kind with — a PDF to a
reader, a .md to a browser, anything CleeCode can only show. open on macOS, xdg-open on
Linux, start on Windows. Over ssh it refuses and says so, rather than opening a window on the
machine at the other end.

What's new in 0.12.1

Three settings were drawn off the bottom of the box. The settings modal is sized from one
number and the cursor wraps on it, and that number had said nine since the first commit while the
list grew to twelve. Where plots open, Mouse enabled and Language were below the fold:
invisible, unreachable by the arrows, and not clickable either — which is why the only way to
change the plot destination was the Run menu or the file on disk. The box now takes its size from
the rows it has, a test fails if the two ever disagree again, and each row's value is set against
the right-hand edge instead of a fixed column that the language-server label had outgrown.

A change made in the settings modal now takes effect, and survives. Picking a row wrote the
struct and nothing else: the plot destination also lives in an atomic the shells are started
from, so the row nobody could reach would not have worked if they had. Nothing was written to
disk until a clean quit either, so a change made there and a terminal closed by its own button
cancelled each other out. Both are fixed for every row at once.

The plot switch says which way it is set. Plots: tabs or windows in the Run menu asked the
question and answered neither — you had to flip it and read the status line, which is a question
you cannot ask without also answering it. It now reads out tabs or windows on the right, in
the column the shortcuts use, and it reads out the effective destination: on a machine with no
screen it says tabs, because that is where the figures are going whatever the setting says.

The splash screen is a setting. An argument on the command line has always skipped it, so
clee src/main.rs went straight to work while a bare clee showed the turtle for one and
eight tenths of a second. Splash screen at startup, off, skips it for the bare clee too.

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave...

Read more

v0.12.5

Choose a tag to compare

@github-actions github-actions released this 24 Aug 08:28

What's new in 0.12.5

The Octave frame speedup from 0.12.3 is reverted. It wrote gnuplot's own chatter into the
transcript of a session over ssh — multiplot> set style increment default; line 0: warning: deprecated command, once per frame of an animation — and, on that machine, a figure window
where there should have been a tab. drawnow (TERM, FILE) draws through the figure's live
gnuplot stream, whose stderr is the shell's and whose terminal is whatever the display offers;
print runs a gnuplot of its own and says nothing. On the machine it was developed on that
stream never opened at all, which is precisely why every measurement looked clean.

Frames are back to where they were before 0.12.3 — slower, quiet, and windowless. The route is
not wrong in principle and the note in cleecode_figs.m records what it cost and what would have
to be proved before it comes back: a Linux box with a display forwarded over ssh, which is the
case that broke.

What's new in 0.12.4

Where plots go now takes effect without restarting CleeCode. Reported as "the switch is
ignored unless I restart clee", and it was half true: a shell opened after the toggle did get
the new answer — but a shell that was already open could not, because a process's environment is
a copy taken when it started and nothing outside can change it. So the octave or python3 you
typed next, at the prompt you had been working in all day, went on doing what the old setting
said. CleeCode now also writes the answer to a file both hooks read as the interpreter starts,
so the next one honours the switch wherever it is started from. A session already running keeps
what it was born with, which is not a policy but a fact about processes — and the status line now
says "from the next Octave or Python you start" rather than the vaguer "next session".

A figure's buttons say when they cannot work. The six on the left of a figure's bar — pan,
rotate, reset, save — do not touch the picture: they ask the session that drew it to redraw. With
that session gone, which is the normal state of a figure that came from ▶ Run, they could do
nothing, and they were drawn exactly like the buttons that still worked. Their refusal was a line
in the status bar, the easiest thing on screen to miss. They are now dimmed when there is no
interpreter behind the figure.

And the bar is easier to hit. No button is narrower than five cells — + was three, which is
a target the size of a full stop — and the single column of space between two buttons now belongs
to one of them instead of to neither, so a click a column wide of the mark still lands. Nothing
moved on screen; the targets grew.

What's new in 0.12.3

Octave animations are four to five times faster. Reported from a session over ssh, where
Python animated smoothly and Octave crawled — and it was not ssh, and not the toolkit. It was
print: measured at 155 ms a frame for a 500-point line at 800x600, and 150 ms at 400x300, and
159 ms at half the resolution. The cost is not in the pixels, it is print's own machinery, which
copies the figure into a hidden one and takes the toolkit right round again every frame.
matplotlib's savefig does the same job in 11 ms.

Under gnuplot — which is what a session with no display gets, so every ssh session — there is a
way round it: drawnow (TERM, FILE) hands the figure to the toolkit's png terminal with none of
that in between. Through the real hook, on the same machine and the same figures:

before now
animated line, 800x600 243 ms/frame 55 ms/frame
3-D surface with a colorbar, 640x480 345 ms 74 ms
image, 500x400 233 ms 47 ms

The picture is the same picture: the grid is still drawn by CleeCode as real line objects, so it
survives the change of route. Only under gnuplot, and only when what lands on disk is the PNG
that was asked for — under qt the terminal argument is ignored and the file written is PostScript
with a .png name, so the magic bytes and the width and height are read back before the file is
accepted. Either check failing is a print, exactly as before.

What's new in 0.12.2

A markdown file with a picture in it becomes a document again. Reported as "the README of
CleeCode only previews as text, the small notes render fine", and the difference between the two
was a picture. pandoc extracts a document's images into a temporary directory and hands typst
absolute paths to them — and typst resolves an absolute path against its root, not against the
filesystem, so it looked for /private/var/…/media/docs/demo.gif under the working directory and
reported it missing. The engine is now told where its root is. Every markdown file with an image
was affected, and the fallback to styled text is silent by design, which is why it read as
"the graphical preview stopped working".

And when a preview does fail, the status line says why. It showed pandoc's last line —
"Error producing PDF." — which is true and useless. The engine's own report is above it: typst
opens with error:, TeX with !, and either now reaches the status line.

Open outside CleeCode. New first entry in the file tree's right-click menu (and in the
command palette): it hands the file to whatever the desktop opens that kind with — a PDF to a
reader, a .md to a browser, anything CleeCode can only show. open on macOS, xdg-open on
Linux, start on Windows. Over ssh it refuses and says so, rather than opening a window on the
machine at the other end.

What's new in 0.12.1

Three settings were drawn off the bottom of the box. The settings modal is sized from one
number and the cursor wraps on it, and that number had said nine since the first commit while the
list grew to twelve. Where plots open, Mouse enabled and Language were below the fold:
invisible, unreachable by the arrows, and not clickable either — which is why the only way to
change the plot destination was the Run menu or the file on disk. The box now takes its size from
the rows it has, a test fails if the two ever disagree again, and each row's value is set against
the right-hand edge instead of a fixed column that the language-server label had outgrown.

A change made in the settings modal now takes effect, and survives. Picking a row wrote the
struct and nothing else: the plot destination also lives in an atomic the shells are started
from, so the row nobody could reach would not have worked if they had. Nothing was written to
disk until a clean quit either, so a change made there and a terminal closed by its own button
cancelled each other out. Both are fixed for every row at once.

The plot switch says which way it is set. Plots: tabs or windows in the Run menu asked the
question and answered neither — you had to flip it and read the status line, which is a question
you cannot ask without also answering it. It now reads out tabs or windows on the right, in
the column the shortcuts use, and it reads out the effective destination: on a machine with no
screen it says tabs, because that is where the figures are going whatever the setting says.

The splash screen is a setting. An argument on the command line has always skipped it, so
clee src/main.rs went straight to work while a bare clee showed the turtle for one and
eight tenths of a second. Splash screen at startup, off, skips it for the bare clee too.

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave...

Read more

v0.12.4

Choose a tag to compare

@github-actions github-actions released this 24 Aug 08:11

What's new in 0.12.4

Where plots go now takes effect without restarting CleeCode. Reported as "the switch is
ignored unless I restart clee", and it was half true: a shell opened after the toggle did get
the new answer — but a shell that was already open could not, because a process's environment is
a copy taken when it started and nothing outside can change it. So the octave or python3 you
typed next, at the prompt you had been working in all day, went on doing what the old setting
said. CleeCode now also writes the answer to a file both hooks read as the interpreter starts,
so the next one honours the switch wherever it is started from. A session already running keeps
what it was born with, which is not a policy but a fact about processes — and the status line now
says "from the next Octave or Python you start" rather than the vaguer "next session".

A figure's buttons say when they cannot work. The six on the left of a figure's bar — pan,
rotate, reset, save — do not touch the picture: they ask the session that drew it to redraw. With
that session gone, which is the normal state of a figure that came from ▶ Run, they could do
nothing, and they were drawn exactly like the buttons that still worked. Their refusal was a line
in the status bar, the easiest thing on screen to miss. They are now dimmed when there is no
interpreter behind the figure.

And the bar is easier to hit. No button is narrower than five cells — + was three, which is
a target the size of a full stop — and the single column of space between two buttons now belongs
to one of them instead of to neither, so a click a column wide of the mark still lands. Nothing
moved on screen; the targets grew.

What's new in 0.12.3

Octave animations are four to five times faster. Reported from a session over ssh, where
Python animated smoothly and Octave crawled — and it was not ssh, and not the toolkit. It was
print: measured at 155 ms a frame for a 500-point line at 800x600, and 150 ms at 400x300, and
159 ms at half the resolution. The cost is not in the pixels, it is print's own machinery, which
copies the figure into a hidden one and takes the toolkit right round again every frame.
matplotlib's savefig does the same job in 11 ms.

Under gnuplot — which is what a session with no display gets, so every ssh session — there is a
way round it: drawnow (TERM, FILE) hands the figure to the toolkit's png terminal with none of
that in between. Through the real hook, on the same machine and the same figures:

before now
animated line, 800x600 243 ms/frame 55 ms/frame
3-D surface with a colorbar, 640x480 345 ms 74 ms
image, 500x400 233 ms 47 ms

The picture is the same picture: the grid is still drawn by CleeCode as real line objects, so it
survives the change of route. Only under gnuplot, and only when what lands on disk is the PNG
that was asked for — under qt the terminal argument is ignored and the file written is PostScript
with a .png name, so the magic bytes and the width and height are read back before the file is
accepted. Either check failing is a print, exactly as before.

What's new in 0.12.2

A markdown file with a picture in it becomes a document again. Reported as "the README of
CleeCode only previews as text, the small notes render fine", and the difference between the two
was a picture. pandoc extracts a document's images into a temporary directory and hands typst
absolute paths to them — and typst resolves an absolute path against its root, not against the
filesystem, so it looked for /private/var/…/media/docs/demo.gif under the working directory and
reported it missing. The engine is now told where its root is. Every markdown file with an image
was affected, and the fallback to styled text is silent by design, which is why it read as
"the graphical preview stopped working".

And when a preview does fail, the status line says why. It showed pandoc's last line —
"Error producing PDF." — which is true and useless. The engine's own report is above it: typst
opens with error:, TeX with !, and either now reaches the status line.

Open outside CleeCode. New first entry in the file tree's right-click menu (and in the
command palette): it hands the file to whatever the desktop opens that kind with — a PDF to a
reader, a .md to a browser, anything CleeCode can only show. open on macOS, xdg-open on
Linux, start on Windows. Over ssh it refuses and says so, rather than opening a window on the
machine at the other end.

What's new in 0.12.1

Three settings were drawn off the bottom of the box. The settings modal is sized from one
number and the cursor wraps on it, and that number had said nine since the first commit while the
list grew to twelve. Where plots open, Mouse enabled and Language were below the fold:
invisible, unreachable by the arrows, and not clickable either — which is why the only way to
change the plot destination was the Run menu or the file on disk. The box now takes its size from
the rows it has, a test fails if the two ever disagree again, and each row's value is set against
the right-hand edge instead of a fixed column that the language-server label had outgrown.

A change made in the settings modal now takes effect, and survives. Picking a row wrote the
struct and nothing else: the plot destination also lives in an atomic the shells are started
from, so the row nobody could reach would not have worked if they had. Nothing was written to
disk until a clean quit either, so a change made there and a terminal closed by its own button
cancelled each other out. Both are fixed for every row at once.

The plot switch says which way it is set. Plots: tabs or windows in the Run menu asked the
question and answered neither — you had to flip it and read the status line, which is a question
you cannot ask without also answering it. It now reads out tabs or windows on the right, in
the column the shortcuts use, and it reads out the effective destination: on a machine with no
screen it says tabs, because that is where the figures are going whatever the setting says.

The splash screen is a setting. An argument on the command line has always skipped it, so
clee src/main.rs went straight to work while a bare clee showed the turtle for one and
eight tenths of a second. Splash screen at startup, off, skips it for the bare clee too.

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave's plot for every example beside it. And Run with no tab open says there is nothing to
run instead of hitting the panic shield.

What's new in 0.10.2

grid minor draws again on a machine with no display. A plot made over ssh came back
without a grid — not a faint one, none at all — while the same script on a desktop drew it.
The difference is not the operating system, it is the toolkit: with no display to open a
window on, CleeCode starts Octave on gnuplot, and Octave's gnuplot backend states its axis
ticks as an explicit list, which is precisely the case where gnuplot will not place minor
ticks between them. Nothing settable from Octave could have changed that. CleeCode now draws
those grid lines itself at the positions Octave would have used, prints, and removes them
again — under gnuplot only, and leaving the session's figure exactly as it found it.

What's new in 0.10.1

Three bugs, all of them silent, all of them found in a real session rath...

Read more

v0.12.3

Choose a tag to compare

@github-actions github-actions released this 24 Aug 07:50

What's new in 0.12.3

Octave animations are four to five times faster. Reported from a session over ssh, where
Python animated smoothly and Octave crawled — and it was not ssh, and not the toolkit. It was
print: measured at 155 ms a frame for a 500-point line at 800x600, and 150 ms at 400x300, and
159 ms at half the resolution. The cost is not in the pixels, it is print's own machinery, which
copies the figure into a hidden one and takes the toolkit right round again every frame.
matplotlib's savefig does the same job in 11 ms.

Under gnuplot — which is what a session with no display gets, so every ssh session — there is a
way round it: drawnow (TERM, FILE) hands the figure to the toolkit's png terminal with none of
that in between. Through the real hook, on the same machine and the same figures:

before now
animated line, 800x600 243 ms/frame 55 ms/frame
3-D surface with a colorbar, 640x480 345 ms 74 ms
image, 500x400 233 ms 47 ms

The picture is the same picture: the grid is still drawn by CleeCode as real line objects, so it
survives the change of route. Only under gnuplot, and only when what lands on disk is the PNG
that was asked for — under qt the terminal argument is ignored and the file written is PostScript
with a .png name, so the magic bytes and the width and height are read back before the file is
accepted. Either check failing is a print, exactly as before.

What's new in 0.12.2

A markdown file with a picture in it becomes a document again. Reported as "the README of
CleeCode only previews as text, the small notes render fine", and the difference between the two
was a picture. pandoc extracts a document's images into a temporary directory and hands typst
absolute paths to them — and typst resolves an absolute path against its root, not against the
filesystem, so it looked for /private/var/…/media/docs/demo.gif under the working directory and
reported it missing. The engine is now told where its root is. Every markdown file with an image
was affected, and the fallback to styled text is silent by design, which is why it read as
"the graphical preview stopped working".

And when a preview does fail, the status line says why. It showed pandoc's last line —
"Error producing PDF." — which is true and useless. The engine's own report is above it: typst
opens with error:, TeX with !, and either now reaches the status line.

Open outside CleeCode. New first entry in the file tree's right-click menu (and in the
command palette): it hands the file to whatever the desktop opens that kind with — a PDF to a
reader, a .md to a browser, anything CleeCode can only show. open on macOS, xdg-open on
Linux, start on Windows. Over ssh it refuses and says so, rather than opening a window on the
machine at the other end.

What's new in 0.12.1

Three settings were drawn off the bottom of the box. The settings modal is sized from one
number and the cursor wraps on it, and that number had said nine since the first commit while the
list grew to twelve. Where plots open, Mouse enabled and Language were below the fold:
invisible, unreachable by the arrows, and not clickable either — which is why the only way to
change the plot destination was the Run menu or the file on disk. The box now takes its size from
the rows it has, a test fails if the two ever disagree again, and each row's value is set against
the right-hand edge instead of a fixed column that the language-server label had outgrown.

A change made in the settings modal now takes effect, and survives. Picking a row wrote the
struct and nothing else: the plot destination also lives in an atomic the shells are started
from, so the row nobody could reach would not have worked if they had. Nothing was written to
disk until a clean quit either, so a change made there and a terminal closed by its own button
cancelled each other out. Both are fixed for every row at once.

The plot switch says which way it is set. Plots: tabs or windows in the Run menu asked the
question and answered neither — you had to flip it and read the status line, which is a question
you cannot ask without also answering it. It now reads out tabs or windows on the right, in
the column the shortcuts use, and it reads out the effective destination: on a machine with no
screen it says tabs, because that is where the figures are going whatever the setting says.

The splash screen is a setting. An argument on the command line has always skipped it, so
clee src/main.rs went straight to work while a bare clee showed the turtle for one and
eight tenths of a second. Splash screen at startup, off, skips it for the bare clee too.

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave's plot for every example beside it. And Run with no tab open says there is nothing to
run instead of hitting the panic shield.

What's new in 0.10.2

grid minor draws again on a machine with no display. A plot made over ssh came back
without a grid — not a faint one, none at all — while the same script on a desktop drew it.
The difference is not the operating system, it is the toolkit: with no display to open a
window on, CleeCode starts Octave on gnuplot, and Octave's gnuplot backend states its axis
ticks as an explicit list, which is precisely the case where gnuplot will not place minor
ticks between them. Nothing settable from Octave could have changed that. CleeCode now draws
those grid lines itself at the positions Octave would have used, prints, and removes them
again — under gnuplot only, and leaving the session's figure exactly as it found it.

What's new in 0.10.1

Three bugs, all of them silent, all of them found in a real session rather than in a test.

clear did nothing in a terminal pane, and neither did the startup-banner scrub. A pane
inherited TERM from whatever terminal CleeCode was displayed in — and the pane is not that
terminal, it is CleeCode's own parser. Reached over ssh from Ghostty, an Ubuntu box was told it
was an xterm-ghostty, an entry it has never heard of, so everything that goes through terminfo
stopped working inside the panes. They are told xterm-256color now, which is what the parser
actually implements, plus COLORTERM=truecolor for the 24-bit colour it does carry.

Figures could lose their titles and axis labels. Not in the plot — the picture on disk was
always right. CleeCode asks the terminal at startup whether it can draw real pixels, and that
question reads the terminal's reply off stdin. It was asked after mouse reporting was switched
on, so a hand resting on the trackpad during startup could bury the answer under mouse events;
CleeCode then fell back to half-blocks, where a pane twenty rows tall is forty pixels of vertical
resolution and every label on a plot disappears. The question is asked first now.

A session over ssh -X could die outright, leaving an unusable terminal. CleeCode opened a
system clipboard at startup, always; on Linux that holds an X11 connection for the whole session.
Under ssh -X the display is forwarded, and forwarding expires — after twenty minutes, by
default. When it goes, libxcb ends the process without unwinding, so none of the terminal
teardown runs. Over ssh that clipboard was never usable anyway (it is the server's, which is
why copying already went out through OSC 52), so it is no longer opened there.

What's new in 0.10.0

**...

Read more

v0.12.2

Choose a tag to compare

@github-actions github-actions released this 23 Aug 21:57

What's new in 0.12.2

A markdown file with a picture in it becomes a document again. Reported as "the README of
CleeCode only previews as text, the small notes render fine", and the difference between the two
was a picture. pandoc extracts a document's images into a temporary directory and hands typst
absolute paths to them — and typst resolves an absolute path against its root, not against the
filesystem, so it looked for /private/var/…/media/docs/demo.gif under the working directory and
reported it missing. The engine is now told where its root is. Every markdown file with an image
was affected, and the fallback to styled text is silent by design, which is why it read as
"the graphical preview stopped working".

And when a preview does fail, the status line says why. It showed pandoc's last line —
"Error producing PDF." — which is true and useless. The engine's own report is above it: typst
opens with error:, TeX with !, and either now reaches the status line.

Open outside CleeCode. New first entry in the file tree's right-click menu (and in the
command palette): it hands the file to whatever the desktop opens that kind with — a PDF to a
reader, a .md to a browser, anything CleeCode can only show. open on macOS, xdg-open on
Linux, start on Windows. Over ssh it refuses and says so, rather than opening a window on the
machine at the other end.

What's new in 0.12.1

Three settings were drawn off the bottom of the box. The settings modal is sized from one
number and the cursor wraps on it, and that number had said nine since the first commit while the
list grew to twelve. Where plots open, Mouse enabled and Language were below the fold:
invisible, unreachable by the arrows, and not clickable either — which is why the only way to
change the plot destination was the Run menu or the file on disk. The box now takes its size from
the rows it has, a test fails if the two ever disagree again, and each row's value is set against
the right-hand edge instead of a fixed column that the language-server label had outgrown.

A change made in the settings modal now takes effect, and survives. Picking a row wrote the
struct and nothing else: the plot destination also lives in an atomic the shells are started
from, so the row nobody could reach would not have worked if they had. Nothing was written to
disk until a clean quit either, so a change made there and a terminal closed by its own button
cancelled each other out. Both are fixed for every row at once.

The plot switch says which way it is set. Plots: tabs or windows in the Run menu asked the
question and answered neither — you had to flip it and read the status line, which is a question
you cannot ask without also answering it. It now reads out tabs or windows on the right, in
the column the shortcuts use, and it reads out the effective destination: on a machine with no
screen it says tabs, because that is where the figures are going whatever the setting says.

The splash screen is a setting. An argument on the command line has always skipped it, so
clee src/main.rs went straight to work while a bare clee showed the turtle for one and
eight tenths of a second. Splash screen at startup, off, skips it for the bare clee too.

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave's plot for every example beside it. And Run with no tab open says there is nothing to
run instead of hitting the panic shield.

What's new in 0.10.2

grid minor draws again on a machine with no display. A plot made over ssh came back
without a grid — not a faint one, none at all — while the same script on a desktop drew it.
The difference is not the operating system, it is the toolkit: with no display to open a
window on, CleeCode starts Octave on gnuplot, and Octave's gnuplot backend states its axis
ticks as an explicit list, which is precisely the case where gnuplot will not place minor
ticks between them. Nothing settable from Octave could have changed that. CleeCode now draws
those grid lines itself at the positions Octave would have used, prints, and removes them
again — under gnuplot only, and leaving the session's figure exactly as it found it.

What's new in 0.10.1

Three bugs, all of them silent, all of them found in a real session rather than in a test.

clear did nothing in a terminal pane, and neither did the startup-banner scrub. A pane
inherited TERM from whatever terminal CleeCode was displayed in — and the pane is not that
terminal, it is CleeCode's own parser. Reached over ssh from Ghostty, an Ubuntu box was told it
was an xterm-ghostty, an entry it has never heard of, so everything that goes through terminfo
stopped working inside the panes. They are told xterm-256color now, which is what the parser
actually implements, plus COLORTERM=truecolor for the 24-bit colour it does carry.

Figures could lose their titles and axis labels. Not in the plot — the picture on disk was
always right. CleeCode asks the terminal at startup whether it can draw real pixels, and that
question reads the terminal's reply off stdin. It was asked after mouse reporting was switched
on, so a hand resting on the trackpad during startup could bury the answer under mouse events;
CleeCode then fell back to half-blocks, where a pane twenty rows tall is forty pixels of vertical
resolution and every label on a plot disappears. The question is asked first now.

A session over ssh -X could die outright, leaving an unusable terminal. CleeCode opened a
system clipboard at startup, always; on Linux that holds an X11 connection for the whole session.
Under ssh -X the display is forwarded, and forwarding expires — after twenty minutes, by
default. When it goes, libxcb ends the process without unwinding, so none of the terminal
teardown runs. Over ssh that clipboard was never usable anyway (it is the server's, which is
why copying already went out through OSC 52), so it is no longer opened there.

What's new in 0.10.0

The completion popup gains its second source. Where a language server is installed, its
suggestions drop into the list already on screen — in magenta, ranked by the server's own
judgement of what belongs at that position. After a dot those are the only names that mean
anything, and no amount of reading the file could have found them. The list never waits for
them: it opens on the words in your buffers and gets better a moment later, and a row you have
already arrowed down to stays where it is. The diagnostics setting is now language_server
it governs the underlines and the list both — and the old spelling is still read.

The git panel writes. A new Status tab lists every changed file with git's own two letters in
front of it, the index's and the working tree's. S stages the file under the cursor, U takes
it back out, A stages everything, C asks for a message and commits, Enter opens the file;
on Branches, Enter moves to one. X throws away a file's changes behind a question that takes
one letter and reads every other key as no — it is the only thing here that destroys work.
Everything goes through git on PATH, so hooks run, commits are signed and credential helpers
are asked, exactly as in the terminal beside it. Push and pull are deliberately absent: they can
stop to ask for a password, and a panel has no terminal to ask it in.

The last tab can be closed. It used to be replaced by an identical untitle...

Read more

v0.12.2

Choose a tag to compare

@github-actions github-actions released this 23 Aug 21:57

What's new in 0.12.2

A markdown file with a picture in it becomes a document again. Reported as "the README of
CleeCode only previews as text, the small notes render fine", and the difference between the two
was a picture. pandoc extracts a document's images into a temporary directory and hands typst
absolute paths to them — and typst resolves an absolute path against its root, not against the
filesystem, so it looked for /private/var/…/media/docs/demo.gif under the working directory and
reported it missing. The engine is now told where its root is. Every markdown file with an image
was affected, and the fallback to styled text is silent by design, which is why it read as
"the graphical preview stopped working".

And when a preview does fail, the status line says why. It showed pandoc's last line —
"Error producing PDF." — which is true and useless. The engine's own report is above it: typst
opens with error:, TeX with !, and either now reaches the status line.

Open outside CleeCode. New first entry in the file tree's right-click menu (and in the
command palette): it hands the file to whatever the desktop opens that kind with — a PDF to a
reader, a .md to a browser, anything CleeCode can only show. open on macOS, xdg-open on
Linux, start on Windows. Over ssh it refuses and says so, rather than opening a window on the
machine at the other end.

What's new in 0.12.1

Three settings were drawn off the bottom of the box. The settings modal is sized from one
number and the cursor wraps on it, and that number had said nine since the first commit while the
list grew to twelve. Where plots open, Mouse enabled and Language were below the fold:
invisible, unreachable by the arrows, and not clickable either — which is why the only way to
change the plot destination was the Run menu or the file on disk. The box now takes its size from
the rows it has, a test fails if the two ever disagree again, and each row's value is set against
the right-hand edge instead of a fixed column that the language-server label had outgrown.

A change made in the settings modal now takes effect, and survives. Picking a row wrote the
struct and nothing else: the plot destination also lives in an atomic the shells are started
from, so the row nobody could reach would not have worked if they had. Nothing was written to
disk until a clean quit either, so a change made there and a terminal closed by its own button
cancelled each other out. Both are fixed for every row at once.

The plot switch says which way it is set. Plots: tabs or windows in the Run menu asked the
question and answered neither — you had to flip it and read the status line, which is a question
you cannot ask without also answering it. It now reads out tabs or windows on the right, in
the column the shortcuts use, and it reads out the effective destination: on a machine with no
screen it says tabs, because that is where the figures are going whatever the setting says.

The splash screen is a setting. An argument on the command line has always skipped it, so
clee src/main.rs went straight to work while a bare clee showed the turtle for one and
eight tenths of a second. Splash screen at startup, off, skips it for the bare clee too.

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave's plot for every example beside it. And Run with no tab open says there is nothing to
run instead of hitting the panic shield.

What's new in 0.10.2

grid minor draws again on a machine with no display. A plot made over ssh came back
without a grid — not a faint one, none at all — while the same script on a desktop drew it.
The difference is not the operating system, it is the toolkit: with no display to open a
window on, CleeCode starts Octave on gnuplot, and Octave's gnuplot backend states its axis
ticks as an explicit list, which is precisely the case where gnuplot will not place minor
ticks between them. Nothing settable from Octave could have changed that. CleeCode now draws
those grid lines itself at the positions Octave would have used, prints, and removes them
again — under gnuplot only, and leaving the session's figure exactly as it found it.

What's new in 0.10.1

Three bugs, all of them silent, all of them found in a real session rather than in a test.

clear did nothing in a terminal pane, and neither did the startup-banner scrub. A pane
inherited TERM from whatever terminal CleeCode was displayed in — and the pane is not that
terminal, it is CleeCode's own parser. Reached over ssh from Ghostty, an Ubuntu box was told it
was an xterm-ghostty, an entry it has never heard of, so everything that goes through terminfo
stopped working inside the panes. They are told xterm-256color now, which is what the parser
actually implements, plus COLORTERM=truecolor for the 24-bit colour it does carry.

Figures could lose their titles and axis labels. Not in the plot — the picture on disk was
always right. CleeCode asks the terminal at startup whether it can draw real pixels, and that
question reads the terminal's reply off stdin. It was asked after mouse reporting was switched
on, so a hand resting on the trackpad during startup could bury the answer under mouse events;
CleeCode then fell back to half-blocks, where a pane twenty rows tall is forty pixels of vertical
resolution and every label on a plot disappears. The question is asked first now.

A session over ssh -X could die outright, leaving an unusable terminal. CleeCode opened a
system clipboard at startup, always; on Linux that holds an X11 connection for the whole session.
Under ssh -X the display is forwarded, and forwarding expires — after twenty minutes, by
default. When it goes, libxcb ends the process without unwinding, so none of the terminal
teardown runs. Over ssh that clipboard was never usable anyway (it is the server's, which is
why copying already went out through OSC 52), so it is no longer opened there.

What's new in 0.10.0

The completion popup gains its second source. Where a language server is installed, its
suggestions drop into the list already on screen — in magenta, ranked by the server's own
judgement of what belongs at that position. After a dot those are the only names that mean
anything, and no amount of reading the file could have found them. The list never waits for
them: it opens on the words in your buffers and gets better a moment later, and a row you have
already arrowed down to stays where it is. The diagnostics setting is now language_server
it governs the underlines and the list both — and the old spelling is still read.

The git panel writes. A new Status tab lists every changed file with git's own two letters in
front of it, the index's and the working tree's. S stages the file under the cursor, U takes
it back out, A stages everything, C asks for a message and commits, Enter opens the file;
on Branches, Enter moves to one. X throws away a file's changes behind a question that takes
one letter and reads every other key as no — it is the only thing here that destroys work.
Everything goes through git on PATH, so hooks run, commits are signed and credential helpers
are asked, exactly as in the terminal beside it. Push and pull are deliberately absent: they can
stop to ask for a password, and a panel has no terminal to ask it in.

The last tab can be closed. It used to be replaced by an identical untitle...

Read more

v0.12.1

Choose a tag to compare

@github-actions github-actions released this 23 Aug 21:41

What's new in 0.12.1

Three settings were drawn off the bottom of the box. The settings modal is sized from one
number and the cursor wraps on it, and that number had said nine since the first commit while the
list grew to twelve. Where plots open, Mouse enabled and Language were below the fold:
invisible, unreachable by the arrows, and not clickable either — which is why the only way to
change the plot destination was the Run menu or the file on disk. The box now takes its size from
the rows it has, a test fails if the two ever disagree again, and each row's value is set against
the right-hand edge instead of a fixed column that the language-server label had outgrown.

A change made in the settings modal now takes effect, and survives. Picking a row wrote the
struct and nothing else: the plot destination also lives in an atomic the shells are started
from, so the row nobody could reach would not have worked if they had. Nothing was written to
disk until a clean quit either, so a change made there and a terminal closed by its own button
cancelled each other out. Both are fixed for every row at once.

The plot switch says which way it is set. Plots: tabs or windows in the Run menu asked the
question and answered neither — you had to flip it and read the status line, which is a question
you cannot ask without also answering it. It now reads out tabs or windows on the right, in
the column the shortcuts use, and it reads out the effective destination: on a machine with no
screen it says tabs, because that is where the figures are going whatever the setting says.

The splash screen is a setting. An argument on the command line has always skipped it, so
clee src/main.rs went straight to work while a bare clee showed the turtle for one and
eight tenths of a second. Splash screen at startup, off, skips it for the bare clee too.

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave's plot for every example beside it. And Run with no tab open says there is nothing to
run instead of hitting the panic shield.

What's new in 0.10.2

grid minor draws again on a machine with no display. A plot made over ssh came back
without a grid — not a faint one, none at all — while the same script on a desktop drew it.
The difference is not the operating system, it is the toolkit: with no display to open a
window on, CleeCode starts Octave on gnuplot, and Octave's gnuplot backend states its axis
ticks as an explicit list, which is precisely the case where gnuplot will not place minor
ticks between them. Nothing settable from Octave could have changed that. CleeCode now draws
those grid lines itself at the positions Octave would have used, prints, and removes them
again — under gnuplot only, and leaving the session's figure exactly as it found it.

What's new in 0.10.1

Three bugs, all of them silent, all of them found in a real session rather than in a test.

clear did nothing in a terminal pane, and neither did the startup-banner scrub. A pane
inherited TERM from whatever terminal CleeCode was displayed in — and the pane is not that
terminal, it is CleeCode's own parser. Reached over ssh from Ghostty, an Ubuntu box was told it
was an xterm-ghostty, an entry it has never heard of, so everything that goes through terminfo
stopped working inside the panes. They are told xterm-256color now, which is what the parser
actually implements, plus COLORTERM=truecolor for the 24-bit colour it does carry.

Figures could lose their titles and axis labels. Not in the plot — the picture on disk was
always right. CleeCode asks the terminal at startup whether it can draw real pixels, and that
question reads the terminal's reply off stdin. It was asked after mouse reporting was switched
on, so a hand resting on the trackpad during startup could bury the answer under mouse events;
CleeCode then fell back to half-blocks, where a pane twenty rows tall is forty pixels of vertical
resolution and every label on a plot disappears. The question is asked first now.

A session over ssh -X could die outright, leaving an unusable terminal. CleeCode opened a
system clipboard at startup, always; on Linux that holds an X11 connection for the whole session.
Under ssh -X the display is forwarded, and forwarding expires — after twenty minutes, by
default. When it goes, libxcb ends the process without unwinding, so none of the terminal
teardown runs. Over ssh that clipboard was never usable anyway (it is the server's, which is
why copying already went out through OSC 52), so it is no longer opened there.

What's new in 0.10.0

The completion popup gains its second source. Where a language server is installed, its
suggestions drop into the list already on screen — in magenta, ranked by the server's own
judgement of what belongs at that position. After a dot those are the only names that mean
anything, and no amount of reading the file could have found them. The list never waits for
them: it opens on the words in your buffers and gets better a moment later, and a row you have
already arrowed down to stays where it is. The diagnostics setting is now language_server
it governs the underlines and the list both — and the old spelling is still read.

The git panel writes. A new Status tab lists every changed file with git's own two letters in
front of it, the index's and the working tree's. S stages the file under the cursor, U takes
it back out, A stages everything, C asks for a message and commits, Enter opens the file;
on Branches, Enter moves to one. X throws away a file's changes behind a question that takes
one letter and reads every other key as no — it is the only thing here that destroys work.
Everything goes through git on PATH, so hooks run, commits are signed and credential helpers
are asked, exactly as in the terminal beside it. Push and pull are deliberately absent: they can
stop to ask for a password, and a panel has no terminal to ask it in.

The last tab can be closed. It used to be replaced by an identical untitled buffer, which
made it the one tab you could not close. What is left is an empty frame that says how to open
something.

Figures redraw again on a desktop. Zoom, pan and reset sent their command and the session
really did move the plot, but the picture never changed: Octave only marks a figure modified
under gnuplot, and every machine with a display uses qt. Anyone not on a headless box had a plot
they could not move. Its Python counterpart had the mirror of the same fault — matplotlib's
staleness flag was never cleared, so every figure was re-rendered at every prompt.

A Python that calls itself Python. Homebrew's Python on macOS is a framework build and the
process is named with a capital P, so CleeCode did not recognise it as a session: sending a cell
typed a shell command at the Python prompt instead. Every brew python3 on macOS was affected.

What's new in 0.9.2

Plots can go back to their own windows. Capture was the only way plotting worked: every
Octave and matplotlib session was told to open no window and hand over a picture. That is still
the default, and still the right one — a live Qt window cannot be moved into a terminal, so
without it the figure appears behind the terminal, which is the worst of both. But on a desktop a
real figure window zooms, pans and rotates with the toolkit's own tools, and wanting that was not
something you could ask for....

Read more

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 21:07

What's new in 0.12.0

Animations do not flicker any more, and it was four separate things. The pane emptied
between frames, because re-reading a figure put the tab back into its loading state while the
new picture was decoded on a thread — a picture, a blank, a picture, ten times a second. Every
frame was also a new image to the terminal: a fresh kitty protocol per frame means a fresh
image id, the id is written into the cells as a colour, so every cell changed and the terminal
was told to drop one image and place another over the whole area. Nothing held the screen still
while a frame was written, which synchronised update (DEC 2026) now does — the vertical retrace
a TUI never had. And the picture was being read while it was still being written: both hooks
print beside the name and rename onto it now, which within a directory is atomic.

Octave animations move. A figure tab was re-read only when the snapshot changed, and the
snapshot is written by a hook that runs while the interpreter waits for a command — a loop never
waits. cleecode_frame reprints the figures without rebuilding a snapshot, so an Octave
animation moved only when the loop happened to let the hook in. The picture's own timestamp is
what says a figure was redrawn.

A figure is the same figure next time. Running a script again used to leave a second set of
tabs beside the first, because both languages hand out the next free number and neither
plt.subplots() nor a bare figure() names one. Run now closes the figures that file's previous
run opened, and only those — a plot made by hand at the prompt survives a rerun. Octave and
matplotlib also stopped writing fig1.png over each other: one directory per language.

Plots reach a tab without the presets. python3 plot.py — what Run does when no prompt is
open — installed no hook, because PYTHONSTARTUP is read only by an interactive interpreter, while
matplotlib was already pointed at CleeCode's windowless backend. The plot existed nowhere at all.
It works from any Python CleeCode starts now, session or script, which is what Octave has done
since its hook moved onto the load path.

Smaller things. The three built-in workspaces are coloured in the chooser, so it is visible
that they cannot be deleted rather than only true. The first menu is called CleeCode again under
every workspace. examples/plot.m is examples/grafico.m: Octave looks for a function among the
files of the directory it is working in before its own library, so shipping a plot.m renamed
Octave's plot for every example beside it. And Run with no tab open says there is nothing to
run instead of hitting the panic shield.

What's new in 0.10.2

grid minor draws again on a machine with no display. A plot made over ssh came back
without a grid — not a faint one, none at all — while the same script on a desktop drew it.
The difference is not the operating system, it is the toolkit: with no display to open a
window on, CleeCode starts Octave on gnuplot, and Octave's gnuplot backend states its axis
ticks as an explicit list, which is precisely the case where gnuplot will not place minor
ticks between them. Nothing settable from Octave could have changed that. CleeCode now draws
those grid lines itself at the positions Octave would have used, prints, and removes them
again — under gnuplot only, and leaving the session's figure exactly as it found it.

What's new in 0.10.1

Three bugs, all of them silent, all of them found in a real session rather than in a test.

clear did nothing in a terminal pane, and neither did the startup-banner scrub. A pane
inherited TERM from whatever terminal CleeCode was displayed in — and the pane is not that
terminal, it is CleeCode's own parser. Reached over ssh from Ghostty, an Ubuntu box was told it
was an xterm-ghostty, an entry it has never heard of, so everything that goes through terminfo
stopped working inside the panes. They are told xterm-256color now, which is what the parser
actually implements, plus COLORTERM=truecolor for the 24-bit colour it does carry.

Figures could lose their titles and axis labels. Not in the plot — the picture on disk was
always right. CleeCode asks the terminal at startup whether it can draw real pixels, and that
question reads the terminal's reply off stdin. It was asked after mouse reporting was switched
on, so a hand resting on the trackpad during startup could bury the answer under mouse events;
CleeCode then fell back to half-blocks, where a pane twenty rows tall is forty pixels of vertical
resolution and every label on a plot disappears. The question is asked first now.

A session over ssh -X could die outright, leaving an unusable terminal. CleeCode opened a
system clipboard at startup, always; on Linux that holds an X11 connection for the whole session.
Under ssh -X the display is forwarded, and forwarding expires — after twenty minutes, by
default. When it goes, libxcb ends the process without unwinding, so none of the terminal
teardown runs. Over ssh that clipboard was never usable anyway (it is the server's, which is
why copying already went out through OSC 52), so it is no longer opened there.

What's new in 0.10.0

The completion popup gains its second source. Where a language server is installed, its
suggestions drop into the list already on screen — in magenta, ranked by the server's own
judgement of what belongs at that position. After a dot those are the only names that mean
anything, and no amount of reading the file could have found them. The list never waits for
them: it opens on the words in your buffers and gets better a moment later, and a row you have
already arrowed down to stays where it is. The diagnostics setting is now language_server
it governs the underlines and the list both — and the old spelling is still read.

The git panel writes. A new Status tab lists every changed file with git's own two letters in
front of it, the index's and the working tree's. S stages the file under the cursor, U takes
it back out, A stages everything, C asks for a message and commits, Enter opens the file;
on Branches, Enter moves to one. X throws away a file's changes behind a question that takes
one letter and reads every other key as no — it is the only thing here that destroys work.
Everything goes through git on PATH, so hooks run, commits are signed and credential helpers
are asked, exactly as in the terminal beside it. Push and pull are deliberately absent: they can
stop to ask for a password, and a panel has no terminal to ask it in.

The last tab can be closed. It used to be replaced by an identical untitled buffer, which
made it the one tab you could not close. What is left is an empty frame that says how to open
something.

Figures redraw again on a desktop. Zoom, pan and reset sent their command and the session
really did move the plot, but the picture never changed: Octave only marks a figure modified
under gnuplot, and every machine with a display uses qt. Anyone not on a headless box had a plot
they could not move. Its Python counterpart had the mirror of the same fault — matplotlib's
staleness flag was never cleared, so every figure was re-rendered at every prompt.

A Python that calls itself Python. Homebrew's Python on macOS is a framework build and the
process is named with a capital P, so CleeCode did not recognise it as a session: sending a cell
typed a shell command at the Python prompt instead. Every brew python3 on macOS was affected.

What's new in 0.9.2

Plots can go back to their own windows. Capture was the only way plotting worked: every
Octave and matplotlib session was told to open no window and hand over a picture. That is still
the default, and still the right one — a live Qt window cannot be moved into a terminal, so
without it the figure appears behind the terminal, which is the worst of both. But on a desktop a
real figure window zooms, pans and rotates with the toolkit's own tools, and wanting that was not
something you could ask for. Run ▸ Plots as tabs turns capture off, and the settings panel
carries the same row: off, Octave keeps its qt windows and matplotlib its usual backend, exactly
as they behave outside CleeCode. It applies to sessions started afterwards, because an
interpreter picks its backend once, at startup.

Where there is no screen, the choice is not offered. Over ssh without X forwarding, or on a Linux
box with no DISPLAY, asking for a window means no plot at all. The row stays visible and reads
"on — no display" rather than flipping to "off" while the tabs keep arriving, which is what a
broken switch looks like. ssh -X with an X server at your end is a display like any other, so
there the choice is yours again — the question is whether a window can open, never whether the
connection is remote.

A figure tab you closed came back. The snapshot lists every figure the session is holding,
and the poll read that as "show these" — so plotting into figure 3 reopened figure 1's tab, closed
a minute ago because you were done with it. A tab now stays closed until that figure is drawn
again.

The Run button typed a shell command at a live Octave prompt. On a headless Linux build
/usr/bin/octave execs octave-cli-11.3.0, and Linux shows that name cut to fifteen characters.
Neither spelling was recognised as an Octave, so Run decided no prompt was open and sent
octave --persist file.m, which at an Octave prompt is only error: 'octave' undefined.

Octave's advice about gnuplot arrived at your first plot. Nine lines recommending qt instead,
printed the first time the toolkit is used — so on a machine whose only toolkit is gnuplot they
landed in the middle of your own output rather than at startup. They are about a choice CleeCode
made on your behalf, recommending a toolkit that needs the display the machine does not have, so
that one warning is off for the sessi...

Read more

v0.11.2

Choose a tag to compare

@github-actions github-actions released this 23 Aug 16:10

What's new in 0.10.2

grid minor draws again on a machine with no display. A plot made over ssh came back
without a grid — not a faint one, none at all — while the same script on a desktop drew it.
The difference is not the operating system, it is the toolkit: with no display to open a
window on, CleeCode starts Octave on gnuplot, and Octave's gnuplot backend states its axis
ticks as an explicit list, which is precisely the case where gnuplot will not place minor
ticks between them. Nothing settable from Octave could have changed that. CleeCode now draws
those grid lines itself at the positions Octave would have used, prints, and removes them
again — under gnuplot only, and leaving the session's figure exactly as it found it.

What's new in 0.10.1

Three bugs, all of them silent, all of them found in a real session rather than in a test.

clear did nothing in a terminal pane, and neither did the startup-banner scrub. A pane
inherited TERM from whatever terminal CleeCode was displayed in — and the pane is not that
terminal, it is CleeCode's own parser. Reached over ssh from Ghostty, an Ubuntu box was told it
was an xterm-ghostty, an entry it has never heard of, so everything that goes through terminfo
stopped working inside the panes. They are told xterm-256color now, which is what the parser
actually implements, plus COLORTERM=truecolor for the 24-bit colour it does carry.

Figures could lose their titles and axis labels. Not in the plot — the picture on disk was
always right. CleeCode asks the terminal at startup whether it can draw real pixels, and that
question reads the terminal's reply off stdin. It was asked after mouse reporting was switched
on, so a hand resting on the trackpad during startup could bury the answer under mouse events;
CleeCode then fell back to half-blocks, where a pane twenty rows tall is forty pixels of vertical
resolution and every label on a plot disappears. The question is asked first now.

A session over ssh -X could die outright, leaving an unusable terminal. CleeCode opened a
system clipboard at startup, always; on Linux that holds an X11 connection for the whole session.
Under ssh -X the display is forwarded, and forwarding expires — after twenty minutes, by
default. When it goes, libxcb ends the process without unwinding, so none of the terminal
teardown runs. Over ssh that clipboard was never usable anyway (it is the server's, which is
why copying already went out through OSC 52), so it is no longer opened there.

What's new in 0.10.0

The completion popup gains its second source. Where a language server is installed, its
suggestions drop into the list already on screen — in magenta, ranked by the server's own
judgement of what belongs at that position. After a dot those are the only names that mean
anything, and no amount of reading the file could have found them. The list never waits for
them: it opens on the words in your buffers and gets better a moment later, and a row you have
already arrowed down to stays where it is. The diagnostics setting is now language_server
it governs the underlines and the list both — and the old spelling is still read.

The git panel writes. A new Status tab lists every changed file with git's own two letters in
front of it, the index's and the working tree's. S stages the file under the cursor, U takes
it back out, A stages everything, C asks for a message and commits, Enter opens the file;
on Branches, Enter moves to one. X throws away a file's changes behind a question that takes
one letter and reads every other key as no — it is the only thing here that destroys work.
Everything goes through git on PATH, so hooks run, commits are signed and credential helpers
are asked, exactly as in the terminal beside it. Push and pull are deliberately absent: they can
stop to ask for a password, and a panel has no terminal to ask it in.

The last tab can be closed. It used to be replaced by an identical untitled buffer, which
made it the one tab you could not close. What is left is an empty frame that says how to open
something.

Figures redraw again on a desktop. Zoom, pan and reset sent their command and the session
really did move the plot, but the picture never changed: Octave only marks a figure modified
under gnuplot, and every machine with a display uses qt. Anyone not on a headless box had a plot
they could not move. Its Python counterpart had the mirror of the same fault — matplotlib's
staleness flag was never cleared, so every figure was re-rendered at every prompt.

A Python that calls itself Python. Homebrew's Python on macOS is a framework build and the
process is named with a capital P, so CleeCode did not recognise it as a session: sending a cell
typed a shell command at the Python prompt instead. Every brew python3 on macOS was affected.

What's new in 0.9.2

Plots can go back to their own windows. Capture was the only way plotting worked: every
Octave and matplotlib session was told to open no window and hand over a picture. That is still
the default, and still the right one — a live Qt window cannot be moved into a terminal, so
without it the figure appears behind the terminal, which is the worst of both. But on a desktop a
real figure window zooms, pans and rotates with the toolkit's own tools, and wanting that was not
something you could ask for. Run ▸ Plots as tabs turns capture off, and the settings panel
carries the same row: off, Octave keeps its qt windows and matplotlib its usual backend, exactly
as they behave outside CleeCode. It applies to sessions started afterwards, because an
interpreter picks its backend once, at startup.

Where there is no screen, the choice is not offered. Over ssh without X forwarding, or on a Linux
box with no DISPLAY, asking for a window means no plot at all. The row stays visible and reads
"on — no display" rather than flipping to "off" while the tabs keep arriving, which is what a
broken switch looks like. ssh -X with an X server at your end is a display like any other, so
there the choice is yours again — the question is whether a window can open, never whether the
connection is remote.

A figure tab you closed came back. The snapshot lists every figure the session is holding,
and the poll read that as "show these" — so plotting into figure 3 reopened figure 1's tab, closed
a minute ago because you were done with it. A tab now stays closed until that figure is drawn
again.

The Run button typed a shell command at a live Octave prompt. On a headless Linux build
/usr/bin/octave execs octave-cli-11.3.0, and Linux shows that name cut to fifteen characters.
Neither spelling was recognised as an Octave, so Run decided no prompt was open and sent
octave --persist file.m, which at an Octave prompt is only error: 'octave' undefined.

Octave's advice about gnuplot arrived at your first plot. Nine lines recommending qt instead,
printed the first time the toolkit is used — so on a machine whose only toolkit is gnuplot they
landed in the middle of your own output rather than at startup. They are about a choice CleeCode
made on your behalf, recommending a toolkit that needs the display the machine does not have, so
that one warning is off for the session. Everything else it wants to tell you still gets through.

The presets put the prompt underneath at every width. It used to move beside the editor on a
wide window. That reads well until a figure opens: the editor splits to put the plot next to the
code that drew it, and in three columns each half is about a third of the window — a plot that
size is a thumbnail. Underneath, the editor keeps the whole width to divide and the prompt keeps
it too, which is what a matrix row wanted in the first place.

A settings.toml written before this keeps working: diagnostics_figures is still read under its
new name, plots_in_tabs.

What's new in 0.9.1

Four bugs in the numeric side, all of them reported from real use and none of them visible from
the machine it was built on.

The workspace panel worked sometimes. It followed "the newest .json in the snapshot
directory", and four kinds of JSON live there: the snapshot, the inspector's question, its answer,
and the breakpoints. So setting a breakpoint or opening the inspector made a request file the
newest thing there; the panel read it as a snapshot, found it was not one, and went blank until
the next tick wrote the real file. The same watcher is where the breakpoint file's path comes
from, so this was worse than a flicker — with the watch pointed at break-0.json, the next
breakpoint went to break-break-0.json, a file no session reads. Breakpoints that intermittently,
silently stopped being set.

Plots opened in real windows. The figure capture, and the setting that stops a window opening
at all, were installed by the octave preset's own startup command — so an Octave started any
other way got none of it: one typed at a shell tab, or the one the Run button starts when no
prompt is open. Octave's own mechanism fits better: the library directory now carries a PKG_ADD,
which Octave runs when the directory joins the load path, and OCTAVE_PATH is set on every shell.
Any Octave you start in a CleeCode terminal now reports its workspace and hands over its plots.
It prepends to your own OCTAVE_PATH rather than replacing it, and outside CleeCode it stays
inert.

A machine with no display had no way to plot, and no way to find that out. On a remote server
over ssh there is no display, qt cannot load, and with gnuplot not installed either Octave has no
graphics toolkit at all — which you discover from inside whichever line of your own script first
calls figure(), as error: no graphics toolkits are available!. A session CleeCode drives never
shows a window, so its toolkit only has to be able to print: gnuplot can do that with no display,
and is now preferred when th...

Read more