Repository navigation
Releases
v0.15.1
.gtui format 10 (reads v1..v10). Crate 0.15.1.
Fixed
-
A zero-byte
.gtuiopens as a new document instead of failing withjson: byte 0: unexpected end of input.touch empty.gtui && graph-tui empty.gtuinow behaves likenvim: empty buffer,
:wwrites it. Any empty file (not just.gtui) is treated the same way, and--dumpof one
prints the blank document rather than an error. -
Editing text in place with double-width characters no longer swallows them. The draft was written
one character per cell, so each CJK character's right half was overwritten by the next one; the
caret column was counted in characters as well, so it drifted half a cell per wide character.
Both now use display width: wide characters occupy two cells (the second is a blank continuation,
like the static text path), andUp/Downkeep the column in cells, landing on the character
boundary at or before it -- the same rule nvim uses.
v0.15.0
.gtui format 10 (reads v1..v10). Crate 0.15.0.
Added
graph-tui tutoropens a walkthrough document built into the binary: 8 short chapters (welcome,
moving, finding, view, editing, building, styling, files) joined by arrows, plus a sandbox with a
text to edit, an empty box, two boxes to link, an edge and a point node. It is an ordinary
document in a scratch buffer -- edit it, break it,rit into another layout -- nothing is
written anywhere,:qleaves, and running the command again starts from the original. The
English document is compiled in fromassets/tutor.en.gtui; the tutorial itself is English-only
for now, while--langstill controls the app's own UI text. The subcommand takes no file
argument and refuses--dump/--export.
Fixed
-
w/bare a horizontal ray across the whole picture. The ray used to pick an axis from the
picture's proportions and give up as soon as it left the viewport, so on a wide picture it
printednothing ahead of the cursorand on a tall one it walked up the column and could never
reach a box hanging off to the side. It now goes right (w) / left (b) from the cursor's row
to the edge of the picture, stops at the first box or edge it meets, and scrolls the view there;
an edge counts as a stop wherever it is drawn (edges are measured inside the window only, so
beyond it the first box is the stop). When the row is empty -- a vertical stack, a table-like
layout -- it falls back to the nearest box in the forward half-plane (right or below) or the
backward one (left or above), measured to the box border, so a vertical column stays walkable and
a detached box (a legend, a note pinned to the left) is still reachable. Keys and directions are
unchanged. -
Dragging a box in a medium document no longer stalls: the port plan was recomputed for every
layer twice per frame (once in the routing pass, once in the inking pass), and its inner
candidate loop calledcontent_center-- and through ittext_metrics, which splits and
re-collects the label -- for every pair of candidate cells.content_centeris now computed once
per box and the plan once per layer per frame. Onlocal/llvm-tablegen.gtui(36 boxes, 35 edges)
a full frame drops from about 353 ms to about 4 ms, and--dumprenders in 10 ms instead of
330 ms. No picture changes: every example and that file dump byte-identically before and after.
v0.14.0
.gtui format 10 (reads v1..v10). Crate 0.14.0.
Added
Alt-Backspacedeletes the word before the caret while editing text in place (i). The other
editors (prompt,:line,/search) already had it; the in-place draft only deleted one
character per press. It follows the same word rule asAlt-←Alt-→there: a word is a run of
non-space, soalpha beta gammalosesgammain one press.Ctrl-Wis still not bound in the
in-place editor -- it is a line-editor key and the draft is multi-line.
Changed
Eisolates instead of dimming. It now shows the cursor node, its neighbours and the edges that
touch it, and every other element is gone rather than greyed out: boxes, text, point labels, edge
notes (only the ones bound to a visible edge stay), regions and search highlights are all
skipped, so they take no space and cannot be clicked, selected or picked. Layout coordinates,
port assignment and routing are still computed for the whole document, so a visible edge keeps
its ports and its exact route across the toggle and the picture comes back byte-identically when
E(orEsc) turns it off. The status line carriesisolate <name>while it is on,f/F
fit the visible nodes only, a jump to a hidden box leaves the isolate first, andn/Nskip
hidden matches (with a note when every match is hidden). The centre follows the cursor onto a
visible neighbour and is tracked by box id, so deleting a box before it does not silently move
the isolate and deleting the centre itself exits it.
v0.13.1
.gtui format 10 (reads v1..v10). Crate 0.13.1.
Fixed
-
An edge between point nodes now leaves them in the direction its
from_sideasks for. Point nodes
have no port cell, so the plain L-shaped fallback used to ignore the declared side and picked
"east then south" whenever the next node in the chain suggested a horizontal approach. In
examples/fun/dynasties.gtuithat made南宋 -> 元run east along北宋 -> 南宋's row, so the two
edges overlapped and read as one line from北宋to元; it now drops out of南宋downward and
comes into元from the west.uk-dynasties.gtuiloses a row of overlapping strokes the same way. -
A manual waypoint (
via) that makes the edge double back on the row or column it just
travelled is now skipped instead of drawn: such a point only adds a spur that ends in
mid-air.hub -> leaf15inexamples/limits/star40.gtuiused to come down to the shared
channel and climb back up, leaving a│tail under the leaf's╯; the elbow now reads
╯and the tail is gone. Waypoints that make progress are untouched, and no other
example changes.
v0.13.0
.gtui format 10 (reads v1..v10). Crate 0.13.0.
Changed
-
In neighbour view (
E) the selected edge pulses again: the overlay that lifts the highlighted
path above boxes and text used to repaint it in the plain edge colour, which wiped the pulse. -
In neighbour view (
E) the cursor only picks edges of the highlighted node: pointing at an edge
that merely passes through the same cell (or belongs to a neighbour) no longer selects it, so when
several edges share a cell the ones from the centre win. Clicking such an edge says "E: only edges
of the centre" instead of silently selecting it. -
Neighbour view (
E) now keeps the highlighted path visible: the lit edges are drawn once more
above boxes, text and labels, so a highlighted edge is not cut by the boxes it passes. Ports on a
highlighted neighbour keep the colour of their own edge and the neighbour's other ports (arrows
andTs of edges that are not lit) are dimmed, sodbus.socket's 16 red arrowheads no longer
compete with the one blue one. -
A
Tjunction on a box border now uses the colour of the edge it belongs to, the same as the
arrowhead, instead of the box's border colour. The glyph is unchanged. -
An edge that is short relative to its boxes (its length is no larger than the biggest box side,
both ends onauto) now picks its two sides from all 16 pairs, preferring ones that can run
straight. This only touches small boxes standing next to big ones (systemd.gtuimoves one edge);
everything else keeps the ordinary rule, and explicitfrom_side/to_sideopts out. -
Ports are allocated by the border's capacity again, not by the text size: an 18-row box with a
one-line label has 16 cells down each side, so a side no longer collapses to a single anchor when
its label happens to be one line. A side with fewer than two cells that cannot hold its group
moves the whole group to a side that has room and also faces the other box; the surplus endpoints
otherwise move individually, choosing the side closest to the target instead of the next one
clockwise. This fixessystemd.gtuiat 1:1:basic.targetkeeps 16 arrowheads down its full
right edge (instead of one plus fourvon top, eight^on the bottom and a wrap around the
left) anddbus.socketthe same on its left. -
layeredonly forces a vertical side for adjacent ranks when the two boxes do not overlap
vertically; two boxes side by side that merely start at a different row keep their left/right
edges. -
The last-resort route no longer draws a line that stops halfway: when the straight fallback is
blocked the edge draws nothing instead of leaving a dangling stroke. -
When a side runs out of cells the endpoint that moves is now chosen by gain instead of by edge
order: plain lines move before arrowheads, and only the surplus count moves (a side with 16 cells
and 17 ends moves exactly one). A one-rownonenode keeps its single-cell sides and shares them
between plain lines rather than being pushed to the top or bottom.
Changed
- Port and arrow ownership is now an explicit role model instead of "whichever edge
last wrote the cell": a cell's port role is eitherArroworLine(never both),
an arrowhead keeps a set of owning edges (arrow_owners) plus, per edge, the
strokes / ports / arrows it actually owns. Selection, the E-mode overlay and the
arrow colour no longer consultedge_cells(which stays as the hit-testing union). - Consequences:
- A plain connection end no longer takes a cell that an arrow end owns, so
selecting a forward edge (dbus.service -> dbus.socket) no longer shows an
arrowhead at its source (<->). - Only arrows the selected edge owns are highlighted; foreign arrowheads along
the path (and at the picked edge's ports) keep their own colour. - An arrowhead shared by several edges is highlighted for every owner.
- Routing treats other edges' ports/arrowheads as a soft obstacle (
+100), so
lines prefer to route around them instead of crossing them.
- A plain connection end no longer takes a cell that an arrow end owns, so
- Routes on
examples/real/systemd.gtuishift (same arrow count and directions,
three more rows of detours); the dump is stable across runs.
Fixed
-
A text node (
style=none) endpoint no longer loses the corner where its edge turns: the cell just
outside the port gets the hidden direction bit pointing back at the port, so the route draws
╭ ─ ╮ ╰ ╯instead of a bare─/│. The port cell itself (the text) is still never drawn on, and
a straight run stays straight. Onexamples/fun/hongloumeng.gtuithree junctions gain their
corner (╭───╯,贾蓉╮,╰秦可卿);systemd.gtuiis unchanged. -
The cells a selected edge's ports open onto are now taken from what was actually drawn
(Canvas::edge_out_cells, recorded inedge_full/path/edge_loop), not from the port plan.
When a side is full the plan has no entry for that end and the drawing falls back topick_port,
so a foreign arrowhead sitting on that cell was not suppressed: selecting
dbus.service -> basic.targetstill showedmulti-user.target -> dbus.service's>at its
source. It is hidden now, while the selected edge's own target arrowhead stays. The set is
assigned once after every layer is drawn instead of being cleared per layer. -
A shared arrowhead now takes the colour of the edge that is currently active instead of the
colour of the first edge that claimed the cell: the picked edge wins, then the single lit edge in
neighbour view, then the first (visible) owner. In neighbour view onfwupd.service, the arrow
intodbus.serviceis drawn in thefwupdedge's colour rather than the first owner's red; when
several owners are lit at once the visible colour is kept (one cell, one colour). Applies to
arrows between text nodes, to arrows embedded in a box border, and leaves the ordinary picture
unchanged. -
A port cell's outside cell now carries the same role as the port (
Arrow/Line), so a
plain connection never starts on a cell an arrowhead is drawn in.Canvas::outside_arrowrefuses
to draw an arrow on a cell that a line end already starts from, and the E-mode overlay no longer
repaints an arrow thatblithid for the selected edge. Selecting a forward edge
(dbus.service -> dbus.socket) therefore no longer shows a foreign>at its source, in either
state; the ordinary (unselected) picture is unchanged. -
A one-line text target (
dbus.serviceinsystemd.gtui) has exactly one arrow cell on each of
its two usable sides, but four edges point into it. Once a side's arrow cell was taken, the port
planner had nothing left and returned no port, so the route reached the box without an arrowhead
(thefwupdandwpa_supplicantarrows were simply absent). Arrow ends now share the cell on
their side when nothing else is free: the glyph is drawn once, its ink and owner stay with the
first edge, and every sharing edge counts the cell as its own, so selecting any of them blinks it. -
The E-mode overlay used to repaint an edge's line glyph over an arrowhead belonging to another lit
edge of the same centre, hiding the shared arrowhead again. Arrowheads stay on top there too. -
The selected edge is now painted over every other edge, including the other edges that E mode
raises for the same centre. Before, in E mode the last drawn lit edge won the shared cells, so a
selected edge was interrupted exactly where a neighbour edge ran along the same row. -
A port whose exit cell lied inside another box is no longer used: the edge (and its arrowhead)
used to be painted under that box, so the arrow simply was not there. The planner now switches
that endpoint to another side that has a way out (and overflow never lands on a covered cell).
Onsystemd.gtuifwupd.serviceandwpa_supplicant.servicenow enterdbus.servicefrom the
right and the left with visible arrowheads (dbus.service</>dbus.service). -
An edge could disappear completely: the port chosen on its side had its first step inside
another box (two boxes overlap on screen), so the route had nowhere to go and the whole edge was
dropped.pick_portnow skips a side whose exit cell is a box cell, and when a planned route
still fails the edge is re-planned once with both ends scanning the border instead of trusting
the plan. Onsystemd.gtuithis brings back the two edges intodbus.servicethat were
invisible (all six of its neighbours now show their edge). The examples suite goes from ~44 s to
~66 s because those edges are now routed instead of being dropped. -
A cross cell keeps the colour of the shape you can see: the edge drawn first owns both the glyph
and the colour, and a later edge (even when its group is the one under the cursor) no longer
repaints that one cell in its own colour, which made a long red run look like it was cut by a
blue square. Raising an edge (selection /E) still recolours it as a whole. -
An edge whose route cannot be found is no longer half drawn: the fallback used to return a single
cell while the caller still painted the port stub, the arrowhead and a one-cell vertical, leaving
a floating^under the box (visible onbasic.targetinsystemd.gtui). A failed route now
draws nothing at all -- no stroke, no port, no arrowhead. -
The route cache keyed documents by geometry only, so two documents with the same box rectangles
but different text could reuse each other's planned strokes (and their ports). Every document now
carries a process-wide id that is part of the key.
v0.12.0
.gtui format 10 (reads v1..v10). Crate 0.12.0.
Added
examples/fun/hongloumeng.gtui: 30 characters from Dream of the Red Chamber plus a title and five colour-coded relationship legends, with 32 undirected edges (blood, marriage, love, servant, kin).
Fixed
- Self-loops were drawn twice when the planning pass went in: the loop owns border cells, so the
planning copy took a port and the drawing copy picked a different one, which collapsed the loop
into a straight line.edge_loopnow only records nothing during planning (a loop is short and
does not need to block other edges). A loop on anone-style (one-row) node also stopped hiding
behind the normal port scan when its side was already taken by other edges -- it goes to the side
centre instead, so it stays visible. - Two
none-style boxes whose texts end up on top of each other no longer erase single characters.
Text is drawn for the box under the cursor / being dragged first and a box whose text would land
on already drawn text now yields as a whole, instead of painting its wide-character continuation
cells (a space) over the other box's first character, which looked like that character had
disappeared. This only showed up with CJK names because those cells carry a half of a glyph. - An explicit
from_side/to_sidethat points away from the other box is now ignored: the edge
used to leave the left side and arrive at the right side of a box that sits to its left, wrapping
around the whole picture. A pin that only loses to a shorter way (sayupwith the target below
and to the right) is still honoured, so the port can still be chosen on purpose.
Changed
-
The planned routes are cached (last four document/view combinations, keyed by the document
revision, the box/edge geometry, the zoom, the viewport origin and the view switches that change
which edges are drawn). Cursor movement, selection, menus and switching back to an already drawn
tab no longer route anything; panning, zooming or editing re-routes. The cache lives in the
renderer and is never written anywhere. -
layered/tree/chainlayouts now record the row of every node, and an edge between two
adjacent rows always leaves from the lower side and arrives at the upper side instead of picking
the side from the two centres. A rank therefore reads as one vertical channel and the ports line
up by column; edges that span more than one row keep the old centre-based choice, which keeps long
spans from detouring around whole rows. The rows are not written to the file (they are redone by
the next layout) and are dropped when the box count changes. -
Routing gained a planning pass: every edge in every layer is routed once with the already planned
routes of the earlier edges as a soft penalty, and the drawing pass then follows those planned
polylines instead of routing again. Two edges of the same group no longer run along one stroke
(where the later one would be invisible), and a frame still costs one routing round rather than
two. Edges whose endpoints are both point nodes, and edges withviapoints, are planned with
the same pass but still recompute their (cheaper) L / straight legs while drawing. -
Ports are assigned per edge instead of "at most two anchors per side": three or more edges on one
side of a wide enough box now each take their own cell, and so do three or more arrowheads. The
cell is chosen so both ends line up (two facing boxes no longer jog by a cell) and stays near the
content centre; assignment walks the file order, so adding an edge never moves existing ports.
When a side is full, an arrowhead moves to the neighbouring side of that endpoint and a plain
line may merge into the nearest junction (drawn as aT); a box too small at the current zoom
keeps the old "scan the border for a free cell" behaviour, which also keeps arrows off the
corners. -
Routing picks paths by a weighted cost instead of lexicographic (bends, then cost): length,
10 per bend, 25 per crossing, 40 per cell that would reuse another edge's stroke (a reused stroke
makes the later edge invisible for that stretch) and 6 for another group's ink. Fixed shapes are
still tried first, but a detour that avoids a long shared stroke now wins, with a detour cap
(3x the Manhattan distance + 40). -
The A* fallback searches a 6-cell window first, then 24, then the whole document frame, so a box
taller than the old fixed window no longer makes the edge stop at its border. Edge pairs whose
endpoints are point nodes are routed with the same obstacle-aware code instead of a blind L (they
used to be drawn straight through a box, which looked like a broken line because the box covers
the stroke). -
Right drag is context-sensitive: on empty space or an edge it pans (2 cells of movement), and on
a box it links that box — drag to another box and release to create the edge, release on empty
space to cancel, release on the source for a self-loop. Under two cells it is still the menu
click. Any box style works, includingpoint/none. -
A dashed preview now follows the pointer for every kind of linking (right drag from a box, left
drag from a border,emode): with a box under the pointer the line runs to that box, otherwise
it runs to the cursor cell, so the direction is visible before you reach a target. The preview
never touches the document, never covers a box and never costs an undo step. -
Editing and clipboard are one-box-at-a-time now:
iedits only the box under the cursor (a
block selection is refused with a hint instead of writing one text into several boxes), and
<|new box|>splitting applies to the in-place commit too. -
Ssplits the cursor box between the cursor line and the next one (first part stays, the rest
moves into a new box on the right with the same style / colours / alignment / layer; edges stay
with the original; refused on the last line, apointnode and an edge note). -
Mmerges the selected boxes into the one under the cursor (else the first in reading order):
texts joined with\n, internal edges dropped, boundary edges re-pointed at the merged box and
duplicates collapsed, one undo step. -
yyanks a picture fragment without touching the document.yanddshare one rule: the
clipboard holds the boxes, their texts and the edges with both ends inside the selection
(plus explicitly selected edges), whiledstill removes every edge touching the removed boxes. -
Default keys removed:
t(the prompt path is:text),a(:notes),x(dcuts; the
"boxes only" variant is gone),V,Ctrl-q,Ctrl-i,z,=and_.toggle_labelsmoved
frombtoB.toggle_notesandconfirm_selectionstay bindable but have no default key.
v0.11.0
.gtui format 10 (reads v1..v10). Crate 0.11.0.
Changed
- SVG export draws border glyphs as vector
<path>segments and centres every character in its
cell, on a 9x18 px cell with a 15 px font. Before, the font's advance (8.42 px at 14 px) did not
match the 8 px cell, so glyphs drifted about 0.4 px per column and box corners broke up. - The jump list is global instead of per document: entries remember the tab as well as the cell, so
Ctrl-oaftergf(or any tab switch) goes back to the previous buffer and position, and
Ctrl-i(Tab) walks forward again. Closing a tab drops its entries and shifts the rest.
Added
w/b(jump_next/jump_prev) cast a ray from the cursor along the picture's main axis
(right when the drawing is wider than it is tall, down when it is taller) and move the cursor to
the first box or edge the ray reaches — a connector between two boxes counts as a stop. The
ray stops at the viewport edge, and a direction with nothing in it only prints a message.
toggle_labelsmoved frombtoBto free the vim keys.iedits the cursor box's text in the box: no prompt line, the frame grows as you type,
Enterinserts a newline,Esccommits (one undo step) andCtrl-Cdrops the draft. The caret
starts on the character the cursor is on (the end when it is outside the text) and is the only
cursor on screen — the terminal cursor is moved onto the draft, nothing is painted twice.
Left/Right/Home/End move by character and line,Alt+ arrow by word,Up/Downkeep the
column. On an empty cellicreates the box first and starts editing it (an untouched new box is
dropped again onEsc).tstill edits through the prompt (multi-selection and<|new box|>
splitting), and apointnode keeps the prompt because a one-cell name has no room for a draft.Ctrl+G(edit_text_external) edits the cursor box's text in$VISUAL/$EDITORand writes
the result straight into the box when the editor exits, as one undo step;<|new box|>splitting
applies. The prompt'sCtrl+Galso applies its result immediately now instead of leaving the
text in the prompt for a secondEnter.- Custom URI schemes are configurable: any
scheme://…link is looked up in a new[links]
section (custom = my-reader) and the raw target is appended as one argument, run blocking
with the terminal suspended. An unconfigured scheme only saysno handler for scheme …. The
core carries no vendor-specific scheme any more (there is a source audit test for it), and
--print-configshows an empty[links]example. - Link targets are parsed into a handler chain (
self-> URL -> custom scheme -> local -> unknown) and
can end in a position:[x](./src/app.rs:10,4)opens$VISUAL/$EDITORat line 10,
column 4 (nvim/vim get+call cursor(10,4), other editors get+10and a note that the column
was dropped),[x](self:10,4)jumps the current document's cursor to cell10,4and
[x](./other.gtui:10,4)switches to that tab (opening one if needed) and jumps. URLs never take
a position,.dotignores it, custom schemes have no handler by default, and
every jump is recorded soCtrl-ogoes back across tabs. The terminal suspend/resume for a
positioned edit is theCtrl+Gpath; atrue/false/:$EDITORstub now counts as
unset. - Each edge can pick its own arrowhead glyphs:
headisauto|ascii|arrows|trianglesin the
.gtuifile (format v10), settable from the right-click menu'sarrowheadsubmenu or
:head triangles, one undo step, previewed live like every other menu item.autokeeps
following the global--arrow/[prefs] arrow, a new edge inherits the last chosen one, and
files from v9 and older load withauto. - A link keeps its link inside italics (
*[label](target)*); bold already did. - Text can carry links:
[label](target)renders as the underlinedlabel, the target takes no
width, andEnterorgfon it opens the target.http(s)://andmailto:go to the opener,
file:///~//.//..///are resolved to a local path (relative ones against the
document directory), and a local.gtui/.dotopens as a tab instead of starting another
graph-tui. The opener is[prefs] opener, then$OPENER, thenopener/xdg-open; when it is
theopenerhelper it is asked with--whichand theeditanswer runs with the terminal
suspended, sonvimcan take over.Enteris nowopen_link(confirm_selectionkeeps no
default key and can be rebound).
v0.10.0
.gtui format 9 (reads v1..v9). Crate 0.10.0.
Added
- Right-drag pans like middle drag or
Ctrl+ left drag, and a right click opens the action menu
when the button is released: a right press is ambiguous, so nothing happens until it moves. Under
two cells of movement it counts as a click and the menu opens at the pressed cell; two cells or
more pans the view and no menu appears (even if you drag back to where you started). With a menu
already open a right press still only cancels it.
Changed
- The style menu (
s/ right click) remembers its stack per kind (box, edge, colour): every
level you walked through, and the item you stood on in each. Opening it again goes straight back
down the same path with the cursor on the item you picked, andhwalks back to the exact item
you came from instead of resetting to the first one. So changing a colour twice iss,l,
Enter. Theedit text…entry is gone from the box menu (usei/t);deletestays as the
last item.
v0.9.0
.gtui format 9 (reads v1..v9). Crate 0.9.0.
Added
examples/fun/uk-dynasties.gtuifollows the English/British royal houses from Wessex to
Windsor, with the Scottish Stewart line joining in 1603: 17pointnodes, one line each, and no
edge labels.examples/fun/dynasties.gtuigrows to 50 content nodes: 44pointregimes plus six
one-linenonelabels for the Warring States (齐 / 楚 / 燕 / 韩 / 赵 / 魏), which branch from 东周
and merge into 秦.- Go-to sequences:
ggputs the cursor on0,0,{x},{y}Gjumps to that cell (read asx,y,
like the status line) and{n}Gto rownwith the column unchanged; the view centres on the
landing cell and the jump goes into the jump list. A jump that has to move the view now lands
exactly on the cursor instead of covering only 85% of the distance, which used to leave a far
jump looking like nothing happened. Counts (5l) still work, and any sequence that is still
waiting (12,100,100,g) is echoed on the right end of the bottom line like nvim's
showcmd. /searches the text in the document, not just node names: node labels (substring, any
position), aliases and pinyin initials, group names and region titles. A hit on a group or a
region collects the boxes inside it, son/Nwalk real jump targets — and every matched box
is drawn in search-yellow (frame, text, point glyph and its label) so you can see what matched.
A text search no longer touches the group focus — that used to light up every other node in the
same group, so one match looked like a whole chain; a bare number still focuses that group, and
Escrestores the focus you had before searching. Ids are not searched, and the search prompt
says what is.
Fixed
- Selecting an edge no longer shows a stray arrowhead at its far end: when another edge's arrow
sat on a cell the selected edge runs through, the glyph stayed the foreign arrow (so 金 -> 蒙古
looked bidirectional because 辽 -> 金's<sat on the shared cell). A raised edge now wins the
glyph on its own cells and keeps its stroke.
Changed
- Point-node labels are no longer dropped when their four standard slots are blocked: a node that
sits on a crossing (e.g. 东周 indynasties.gtui) now gets its name on the offset cell above or
below, which is usually free while the centred slot is not. - Edge ports are fixed anchors instead of a greedy search for a free border cell: the anchor is
measured from the node's text, a side carrying one edge (or three and more) uses the middle
anchor, two edges get the two anchors nearest it, and a side never uses more than two. Adding an
edge no longer reshuffles the others,noneand framed styles keep the same anchor, and zooming
cannot send an edge to another side. The trade-off: several incoming edges on one side share a
single arrowhead. 0only puts the zoom back to1:1and leaves the picture where it is: going to the origin is
ggnow, so resetting the view no longer jumps the map around. Opening a file still centres it.
v0.8.0
.gtui format 9 (reads v1..v9). Crate 0.8.0.
Changed
- Keyboard cursor movement scrolls like nvim: the view stays put until the cursor reaches a
two-cell margin, then every further step scrolls the picture by exactly one cell immediately
while the cursor keeps its screen line (only its logical cell advances). Scrolling here is
not eased — an animation would move the cursor down one cell and then pull it back, which reads
as a jump. Jumps (search,n/N, click, jump history) stay animated, and all mouse gestures
are unchanged.
Fixed
-
A point-node edge no longer routes straight along an edge that is already drawn: two edges that
occupy the same cells collapse into one stroke, so the later edge looks invisible while the
earlier one's arrowhead sits on it, which makes the selected edge read as bidirectional. The L
now picks the other corner when the first one would duplicate an existing run of the same axis
(crossings still share a single cell, as before). -
Edges keep their shape while the view moves: routing now looks at the whole document
(every node is an obstacle, and the "this cell already has a stroke" state is kept outside the
viewport too) instead of only at the visible cells, so panning and cursor scrolling are a pure
translation of the picture and a long edge whose node has left the screen still shows the part
that is on screen. -
Ctrl-o/Tab(Ctrl-i) walk a proper jump list: besides search hits,n/Nand mouse
clicks, every edit records its position — creating a box, editing text, changing a style — while
plain cursor moves do not.Ctrl-ofrom anywhere first notes where you are and then goes back to
the last place you worked.
Added
- Six hand-placed joke graphs under
examples/fun/(owl,left-pad,git-graph,unix,
invaders,dynasties), listed indocs/examples.md. They usegeolayout, so the stored
coordinates are the picture;invadersis pure pixel art built from 46 single-cell text boxes,
anddynastiesis a 44-regime top-down Chinese-dynasty DAG ofpointnodes with one-line CJK
labels placed around them: edges branch where regimes coexisted and merge where one power
reunified, with no edge labels. The
native example test walksexamples/fun/as well. <|new box|>inside the text prompt splits one box into several: the text before the marker
stays in the box, each following segment becomes a new box to its right (top aligned, one cell
apart, nudged right/down when the space is taken) that copies the source box style, colours,
fill, text alignment and layer. Edges and notes stay on the original box; the cursor does not
move and the whole split is one undo step. Only single plain boxes split: multi-selections, edge
notes andpointnodes keep the marker as ordinary text, and so do:textand headlesstext.
\<|new box|>is a literal.