chromosomix 0.6.0
What changed in chromosomix 0.6.0, released on 5 August 2026.
-
Tabs, and a real session behind each one. Several files open at once, each with its own reader worker, its own annotation worker, its own stores and its own DOM. Switching a tab in re-parents that DOM rather than rebuilding it, so a map comes back with its pixels, its zoom and its scroll exactly as they were and without a single byte re-read — which is what a tab has to be for it to be worth having over a second browser window.
The address bar names the active tab and nothing else, which is what was asked for and also the only coherent answer: a hash is one view state, and writing several would make every link a description of somebody else’s workspace and make the back button walk a history that interleaves three files. The rest of the strip lives in
sessionStoragebeside the annotations (§8.6), and a restored tab is named rather than opened — the label and the view state come back, the worker does not, and clicking is what reads the file. Eight remembered tabs therefore cost eight labels. One that named a local file comes back to the picker with the explanation a pasted local-file link has always given, because no browser re-reads a dropped file without a gesture.Two tabs on one file now share nothing: the per-session memory is keyed on the session as well as the URI, so the second view starts with no tracks, no filters and no marks rather than adopting the first one’s.
-
A second cooler in the lower-left triangle. A contact matrix is symmetric, so half of every square on screen is a mirror of the other half and carries no information at all. That is the room this uses: the second file fills it, the picture stops being symmetric, and a broken symmetry is read far faster than two maps side by side.
The split is the matrix diagonal, not the viewport’s, which is what makes panning off the diagonal need no rule of its own — the far corner of a trans block is entirely above the line and shows the first file alone, and its transpose shows the second. Zoom, selection and every other gesture are untouched, because the merge happens once per read and hands the renderer an ordinary block.
Dropped on a card in the Annotations column, where everything else that is laid over a map already lives. Refused by name: a metacool (§8.5’s argument, applied to a whole matrix), a different bin size (one grid cannot be two resolutions), or a chromosome list differing in count, order, spelling or length (every coordinate is an index into one such list). An
.mcooldropped at its root opens its finest resolution, as it does on the picker.GENERAL becomes File 1 and File 2, each with the mark of its own triangle and a close button: closing the second returns to one map, closing the first promotes the second, so either is reversible and what remains is an ordinary one-file map.
uri2is in the hash, so a comparison is a link.One scale by default, because the same colour meaning the same count is the stronger claim. Match depth scales the second file so its 99th percentile meets the first’s — the same order statistic §7.1 picks
vmaxwith — for the case that motivates it: two libraries of different depth, where the shallower is otherwise a pale triangle telling you only that it was sequenced less. Two colourbars then read the same colours in each file’s own counts, and the tooltip and the status line carry both numbers at every cell. -
A genome browser that needs no contact map. Until now a bigWig could only be looked at beside a cooler of the same assembly, and that was never a property of the tracks — it was a property of the coordinate model, which started from a matrix and had no way to describe a genome that was not one.
So there is a second starting point. A
chrom.sizes, a bigWig, a bigBed or a feature file declares the assembly — outright in the first three cases, and as a lower bound in the fourth, which is marked as inferred wherever the genome is described — and the axis becomes the whole genome laid end to end at one unit per base pair. Everything drawn on it is drawn by the code that draws the lanes beside a heatmap, through the sameLocusAxis§8.4 already made the only one: a track cannot be at two positions because only one thing knows where a base pair is.The gestures are the map’s, deliberately. A drag pans, Option-drag selects a range to zoom into, the wheel zooms about the pointer, a double click fits, a lane’s border resizes it and its body reorders the stack — every one of them the gesture that already meant that. The one that is the browser’s own is ⌘-scroll over a lane, which moves that track’s ceiling from whatever it is currently resolved to; it is the adjustment a genome browser is asked for constantly and a map has no analogue for it.
With it: a locus box that accepts
chrI:1,000-20,000, a bare sequence name or*, and shows back exactly what it would accept; a feature search over every loaded gene model and peak set; click a gene to zoom to it and a sequence name to go there; a ruler that draws sequence bands zoomed out and base pairs zoomed in, because at those two extremes they are different questions; arcs for a BEDPE, which is what a loop call looks like in one dimension; range and group editors on each lane, revealed by pointing at it; autoscale groups that stack or share a single lane; and PDF/PNG export redrawn at the export scale.The two shapes of browser are one session. Drop a
.coolanywhere on the tracks and the tab becomes its map, tracks and all; close the map from the File card and it falls back to a browser on the cooler’s own/chroms, with the same tracks still loaded. A file whose name says nothing is opened as a map and, if that fails, identified by its bytes and re-routed rather than reported as broken. -
bigBed is read. Whole, and parsed as the BED it holds, so a bigBed12 draws its exons exactly as the text file would — one parser, one set of rules, one place where BED’s quirks live. Not windowed, because a
FeatureSetlives on the main thread precisely so that a pan redraws with no round trip (§8.4). A file past a million intervals is cut off and says so. -
The 1D browser exports a vector PDF, and it is the drawing rather than a photograph of it. Nothing in that view is a raster — every mark is a rectangle, a polyline or a word — so a 300 dpi image of it is a figure nobody can restyle, recolour or scale, in a file fifty times the size of the drawing it contains. The page is produced by handing the same renderers a context that emits PDF operators instead of pixels, so there is no second drawing of the view to keep in step:
render/features.ts,render/tracks.ts,render/lane.tsandrender/browser.tsdo not know which one they are talking to. Text is Helvetica, one of the fourteen fonts every reader has, so nothing is embedded; the halo behind each track name survives as PDF’s own stroked text mode; and the content stream is deflated, which on a whole-genome gene model is the difference between a twelve-megabyte figure and a seven-hundred-kilobyte one. A vector page has no resolution, so the DPI control is disabled while it is chosen rather than quietly ignored. The heatmap’s PDF is unchanged and stays a raster, because a contact map is one. -
The tracks answer to a hand, and the panel answers to a list. Rows reorder by their grip or by the arrow keys, and a lane reorders by dragging its body; a tick box on each row opens a batch bar that puts several tracks in one autoscale group, gives them one colour, one height, or removes them together — six tracks from one experiment were six trips through six rows. A colour swatch sits beside each name and is the colour input, so changing a track’s colour is one click rather than a menu. Every track’s name is larger and its controls smaller: colour, limits, a ceiling slider and its group, laid out under the name where a reader is already looking, revealed by pointing at the lane and drawn over the top of it rather than across its data.
-
A drag on the ruler zooms, with no modifier. The coordinate strip is where a reader points to say “this much”, and it is the one gesture whose meaning its position already settles — so spending a keystroke on it bought nothing. Option-drag still selects on the tracks themselves, where a drag means pan.
-
Fit every track into the space there is, in the proportions they are already in rather than equally: a reader who gave the gene model three times the height of a coverage track meant it, and “fit” is a request about the stack running off the bottom of the window. Equal heights is the other thing, and the Groups card still offers it per group.
-
A file dropped anywhere in the middle of the window is imported. The drop zones in the Annotations column say what each kind of file becomes and are still there, but a reader with a bigWig in hand and a browser on screen means one thing by dropping it, and asking them to find the right box first is a step that answers a question nobody had.
-
Gene names sit under the features, in a band of their own, so a name never covers the exon structure it belongs to; below the height where that band would leave no room for a glyph they go back inside, which is what a lane dragged very short should do.
-
Aesthetics can turn every word off. One toggle hides track names, value ranges and feature names together, leaving the ink — which is what a figure destined for a caption usually wants, and what a screenshot of six tracks usually does not. Shared with the heatmap’s own panel and the command palette, so all three agree.
-
Chromosome boundaries are drawn, as a full-height rule between sequences, and the ruler bands alternate by the sequence’s position in the assembly rather than by its position on screen — so a boundary does not change shade when a pan brings a new sequence into view.
-
The tabs are shaped like tabs, with tilted sides, the active one raised and lit along its top edge, and the pointer is the same black-filled arrowhead over the tracks that it is over the map. The × appears on the tab under the pointer and on no other — including the active one, which used to be an exception — and it lost the bordered box that was most of what the corner of a tab showed. One session is a title rather than a tab: the strip stays, because the way to a second session is the button on it, but a lone trapezoid with an accent bar is machinery announcing a choice nobody has been offered.
-
Every sequence in the genome, with its length, under the Genome card. The first question a genome browser is asked, and until now the only answer was a count. Each row is a click that goes there, which is also the fastest way to navigate — a chromosome is a locus, and the locus box is for the case where you already know the coordinate. The bar behind each row is its length against the longest, which costs a gradient and turns a column of numbers into the shape of an assembly: sixteen bars in a staircase is a yeast, one bar with a thousand slivers under it is a draft. A filter appears above a dozen sequences, and at most three hundred rows are built at once, because forty thousand scaffolds is a second of layout for a list nobody scrolled to the end of.
-
A track’s range is under its name, and you can type in it. Two changes that are one change. A number at the far end of a lane from the name it belongs to has to be paired up by eye every time, and with four tracks stacked that is four crossings of the window — so it moved under the name. And a number that is under its name is a number you can put a caret in: hovering either end gives a text cursor, and clicking opens a box exactly where the number was drawn, so nothing moves when it becomes editable. Typing a bound turns autoscale off, because that is what typing a bound means. The two boxes that used to live in the lane’s hover row are gone with it — the range is edited where it is read now, and what is left there is
auto, the ceiling slider, the colour and the group. -
A toggle for the shading under a track’s line. The pale envelope that says what a zoomed-out mean hid, and the fill an area track carries: one switch, because they are one thing to the eye. Both are context for the line, and context is the first thing to go when three tracks share a lane or a figure is going into a caption. Shared with the heatmap’s own panel and the command palette, like the track-text toggle beside it.
-
The Plot card is marks rather than sentences. Fit across, fit down, words on or off, shading on or off — four things a reader does over and over while reading, in a row of four icons instead of a column of full-width buttons spelling each of them out. The same two fit marks are on the toolbar above the tracks, because they are the same two actions. And the export controls are one line again: a vector page has no resolution, so the resolution is absent exactly when the long format name needs the width.
-
Memory and CPU on the status bar, after what has been read and whether the file checks out. Per browser tab rather than per session, because the memory a gene model holds and the core a scan is burning are shared by every tab in the window.
Both are weaker than what a task manager shows, and the tooltips say so outright. A page cannot see its own process — a precise figure would read across origins — so memory is the JavaScript heap and its ceiling where that is all Chromium offers, and the whole-agent measurement where a build is cross-origin isolated. CPU has no API at all, so it is measured from the inside: every thread in the tab times its own event loop, and the share of the last three seconds they could not answer in, summed against the machine’s cores, is the number. Work too brief to delay anything never appears, so the reading is a floor rather than an estimate. The workers report their own, which is the point — a whole-genome scan pins a worker for minutes while the main thread sits idle.
-
Drop a file on the map itself to make it a track, on the edge you aim at: a strip along its bottom for the horizontal stack, one down its side for the vertical one. Two rectangles appear while a drag carrying files is over it and are inert the rest of the time, because the map’s own gestures — pan, the Option-drag selection, the click that opens a cell — live in the same pixels. What the file is is still decided by its bytes (§8.3): a
.coolaimed at the bottom of the map is a map, not a track drawn along the bottom. -
/puts the caret in the box, in every view. It was the genome browser’s alone, so the same key that found a gene did nothing the moment a.coolwas dropped in the same tab. Each view now answers it with its own box: the region box on a region, the filter above a table, and a new one in the heading of the whole-genome, genome, pair and scan views — which is also the only way to leave those pictures by typing rather than by aiming, and a genome of one metagenome’s four thousand is a quarter of a pixel to aim at. The key is printed in the box and steps out of the way when the caret arrives. -
Every shortcut is on the button that performs it. Tooltips name their key — the panel folds, the theme, the tab strip, both Plot cards, every overlay toggle, the breadcrumb’s landing views — and the two
whole genome →buttons and the breadcrumb menu print theirs as a badge. The bindings come from two tables (ui/commands.tsandui/plotOptions.ts) that the key handler, the palette, the shortcuts dialog and every one of those tooltips now share, and a test asserts no two actions claim the same key. -
A mark before every card title in the three panels, so a column of five cards is recognised by shape before it is read.
-
A height for every track, and the button that gives it to them, in the browser’s Plot card beside the stretch mark. Stretching answers “use the space I have”; this answers “I know what a track should look like” — ninety pixels is where a yeast ATAC track starts showing the shape of a promoter — and it is also the way out of a stretch, which on a tall screen with two tracks leaves two four-hundred-pixel lanes and nothing to bring them down with. The box is a number and nothing happens as it is typed; Enter and the button are the instruction, so it keeps what you last applied and the next file is one press away from it.
-
Drag a track onto the other edge of the map to move it there. The two rectangles that take a dropped file take a loaded track as well: pick a row up by its grip in the Tracks card, and both rectangles appear the moment it is over the map — drop on one and the track is drawn there instead.
The gesture is on the row rather than on the lane’s body, and that is the second attempt. Dragging the body is what a reader reaches for and there is no room for it there: a lane already answers three drags — its border resizes, along the axis pans, across reorders — so a fourth had to be read out of one of them, and the trip from the bottom stack to the right-hand rectangle is mostly sideways, which is a bottom lane’s pan. It worked from one stack and fought the other. The row carries no such freight, and it is where the track’s other settings are. (The lane got the gesture back in the end, on a handle of its own — see below.)
A row now has a grip beside a map even when it is the only one, since there it has something to do besides reorder. The drag carries its own MIME type, so the rectangles can tell it from a dropped file and from a dragged word while the drag is still in the air, and the whole content area no longer lights up for it. It is the same state as the
locationsetting on the row, because it is the same field. -
Ssaves the plot. Whichever view is mounted exports itself — the map or the browser — in the format and at the resolution its own Plot card is set to, because a shortcut that saved something other than the button beside it would be a second export to keep in step. It is one letter from⌘S, which saves the session, and that is the pairing: two things a reader means by “save this”, told apart by the modifier. Stretching the tracks moves toH, for height. -
location, in the row’s settings, rather thanB/Rtick boxes on its head. Two buttons with the words on them —bottomandright, and not a choice between each other, since a track can be on both — drawn as one segmented control belowheight. The initials were standing furniture in six-point type between a track’s name and its eye, for a decision taken once per track and now also takeable by dragging the lane itself. The last edge a track is on cannot be switched off: a lane on neither edge is drawn nowhere while still calling itself visible, and the eye beside the name is what hides a track. -
The drop zone is what lights up, not the card around it. A card that asks for a file still takes one anywhere on it — a reader whose pointer is over the eighth row when they let go meant to load it — but what reacts is the dashed box, which is the element already drawn as the target. The whole card going blue around an inert rectangle said the rectangle was not the one. Tracks, Compare and the browser’s Contact map card all behave this way now, and a row dragged within the Tracks card lights nothing, because a drag carrying no files is a reorder.
-
Plot before Groups in the browser’s Aesthetics column. Plot acts on this picture and is there whatever the file is; Groups is empty until a reader has made one, and a column that opens with an empty card reads as a column with nothing in it. The browser’s toolbar also gives up its autoscale button, which was the sixth control in a row that then wrapped, orphaning one button on a line of its own — it is in the Plot card, on
Y, where the height box is. -
A track lands under the map, not beside it. The bottom edge is where a track is read — under a ruler, left to right — and a coverage profile turned on its side has to be read with your head tilted. The right-hand stack is still there and is now something you choose: the
B/Rboxes on the row, or the edge you drop the file on. -
Sstretches the tracks to fill the height, all of them to one height, which is the thing a reader reaches for several times a session. Autoscale, which hadS, is nowY— the axis it acts on — and works beside a map as well as under a browser, since they are the same tracks in the same store. -
The status bar is one line, always. It is the bottom edge every view is measured against, and it grew a second line exactly when the pointer was over a stack of tracks — when the reader is least interested in it. So
readis bytes and keeps the request count in the panel it already opens on hover,memoryis bytes and keeps the share of the ceiling there too, and the coordinate readout is the item that gives way, being the one thing here that is re-said a moment later. -
The standing
read in place · nothing is sentline is gone from the bar. The claim is not: the picker states it where a file is about to be opened, and About states it in full. It was a sentence you finish reading once, permanently occupying a corner of a bar whose other three items are numbers that change. -
The shortcuts dialog has no Help group, so the two gesture tables — what a drag, a wheel and a double click do on a map, against what they do on the tracks — sit side by side, which is how they are read.
⌘/was the only bound command in Help and is now beside the palette in App, where it belongs. Both tables also gained the gestures they had been missing: the lane resize, the edge drops, and dropping a file on the browser’s tracks. -
The Compare card and the browser’s Contact map card wear the Tracks card’s load zone — a thin unshaded box with a bold line and a quiet one, then the URL behind a fold. All three ask the reader for a file, and three shaded variations of one gesture in one column read as three applications. The Contact map card can now take a URL, which it could not before.
-
One box in the browser’s toolbar, not two.
chrIV:1,000-2,000,chrIVandGAL4are three spellings of “where do I want to be”, and asking which of two boxes each spelling belonged in was a taxonomy the reader had to learn in order to type a word. Coordinates win when the text is coordinates; anything else falls to the list of names under the box. Clicking it selects what is there, because what is there is where you already are, and/still lands in it. -
Loading a track is at the top of the Tracks card, rather than in a card of its own below it. The two questions are asked of the same thing, and with eight tracks loaded the way to load a ninth was to scroll past the eight.
-
The app was one object holding a worker, a store, a file and a view, and it bound itself to the address bar in its constructor. It is now
app/Session.ts— a file and the views of it — andapp/App.ts— the window around them — withmain.tsas three lines of bootstrap.ViewStorestops owning the hash, because there is one address bar and there may be six stores. -
The sample cache that keeps pan and zoom free of I/O moved out of
AnnotationLanesintoannotations/samples.ts. Two widgets need that rule now and its arithmetic — the padded window, the power-of-two bucket, keeping the old data on screen until the new data lands — is fiddly enough that two copies would drift. -
A
chrom.sizesdropped on a browser whose lengths were inferred replaces them, and replaces only them: the URI, the session memory and every loaded track stay exactly as they were, because the genome did not change — only what is known about it. Asamtools faidx.faicounts as one, decided by its name, which is the tiebreaksniffFormatalready uses for the two cases its columns cannot settle. -
The colour swatch beside a track’s name in the Annotations panel appears when the row is pointed at or has the focus, like the drag handle it sits next to. A column of coloured squares down the panel is a second legend for something the picture already says where it matters — the lane is drawn in that colour, and where two share a lane so is each name. It keeps its space while it is invisible, so reaching for it is one movement rather than a hunt.
-
Every lane has a grip, in both views. Six dots beside the track’s own name — the same glyph the Tracks card puts at the head of every row, because it is the same gesture on the same thing. Dragging it moves the track through the stack; beside a map, dragging it out onto either rectangle draws the track on that edge instead. Reordering a track was something you could only do to the picture from the list beside it, or by learning that a drag across a lane means one thing and along it means another.
It is a fourth surface rather than a fourth reading of one, which is what makes it possible at all: a press that starts on the handle is not ambiguous in any direction, so it can mean “move this track” everywhere the pointer goes, and the three drags the lane’s body already answers keep their pixels untouched. Drawn on the canvas rather than laid over it, for the reason every affordance here is: the pointer is an arrowhead pointing at a base pair, and a handle that lived in the cursor would not exist until you were on it. It takes the open hand as its cursor — the one thing the arrowhead cannot say — and is never on a saved figure, because what a reader points at is not part of the picture.
It is the only way to move a track. The lane’s body used to reorder as well, by reading the direction of the first movement — along the axis a pan, across it a reorder. Two meanings for one surface is a rule you have to know before your first drag does what you meant, and it was paid for on every pan that wandered a few pixels off the horizontal: the track jumped a slot, because a gesture that has to be performed carefully had been asked for. With a surface of its own for moving, the body goes back to meaning one thing.
It hangs outside its lane, in the margin just before the data starts, and that is what lets every lane show one at all times. Inside, it had to be paid for twice: a slot cut out of the front of each label, which moved every track name along, and a glyph that could only appear on hover — because a permanent one would have been sitting on the signal. Outside, it costs the picture nothing, so the names went back against the start of their own track and the handles stopped being something to discover. Each one is on its name’s line and keeps its distance from it, wherever the layout put that name: at the top over the data, centred in a column, down at the floor on a flipped track. The faint one is a handle; the solid one is the lane the pointer is on.
The 1D browser keeps a thirteen-pixel strip at its left for them, since outside its first lane is off the edge of the canvas otherwise. Beside a map no strip is needed — the plot’s own margin is where they go. Which end of a lane that is depends on which way the lane reads: the left of a bottom lane, and the bottom of a right-hand one, which turns a quarter turn and reads upward from there. A handle at the far end of a track from its own name is a handle belonging to nothing in particular.
A grip drag raises the other edge’s rectangle and only that one. A track taken out of the bottom stack cannot be moved to the bottom — it is already there — so offering it that target is offering a gesture with no outcome, and a rectangle that lights under the pointer and then does nothing when you let go reads as the drag having failed. The rectangle that is not offered is not a drop target either, decided in one place, so what lights up and what lands cannot disagree. A file dragged in from the desktop still gets both, since it is on neither edge yet.
The right-hand stack makes its own room. Its handles hang past the end of the data, where the space available is whatever the heatmap left under its plot — a couple of pixels in a view with no column labels — so rather than back the handle into the signal, the stack adds the few pixels it is short to the end of its own canvas. After the data, so no coordinate moves and the map is not touched, and only when the margin is not already enough.
-
Loop anchors moved off the last track. The triangle that says “this coordinate” was drawn over the last seven pixels of the last lane in the stack — on a coverage track that is the busiest part of it, and on a full one the mark is invisible. It now has a band of its own past the stack, on both edges, which is the only place a glyph about the axis can go without being ink on somebody’s data. The band is nine pixels and exists only while a loop is marked, so a session that never places one keeps those pixels for the map.
-
The map’s two drop rectangles moved inside the map, and became strips. They covered the bottom quarter and the right fifth of the whole plot area — which is where the lane stacks are — so a track dragged towards the end of its own stack met a drop target instead of the gap it was aiming for, and the last slot in the order could not be reached at all. Inside the map they overlap nothing that can be dragged, and they still name the edge they stand for: the bottom of the picture means below it, the right of the picture means beside it.
Narrow, too, and centred on the edge they name: thirty pixels thick, four fifths of the map long, each in the middle of its own side. A quarter of the map’s height and a fifth of its width is a lot of a picture to cover in order to say “this edge”, and at that size the two rectangles hid the corner of the matrix a reader is usually aiming past on the way to them. Centring at four fifths is also what keeps them apart — each stops a tenth of the map short of the corner they share — so a drop in the corner lands on neither, which is the honest answer to a gesture aimed between two targets. Down the side the label turns the same quarter turn as the track names in the stack it is naming. All three gestures that aim at them — a file from the desktop, a row from the Tracks card, a lane by its handle — now arm them, and while a lane is being carried the tooltip stays down rather than following the pointer across the map with a reading from where the drag started.
-
The service worker would have answered a pinned URL with the newest build. It is registered at the site root, so every archived build’s address is inside its scope, and its navigation handler answered everything in scope from the precached shell — the right URL, the wrong code, and no symptom to notice it by. Navigations under
<scope>v/now go to the network untouched.A worker only updates when a page it controls is loaded, so the fix cannot reach a reader who is already carrying an older one — and that reader is exactly who follows a pinned link.
recoverPinnedNavigation()catches the case from the page itself: a document built for the site root, at an address under the archive prefix, is a pair of facts that cannot both be true of a correctly served page. It unregisters and reloads, once per tab, because the second attempt at that would be a reload loop. -
A cooler rendered nothing.
MatrixPanel’s new edge-drop watcher readthis.elfrom the constructor, where it does not exist yet, so the panel threw on the way up — for every file that has annotations, which is every cooler, while a.metacool(which has none, §8.5) was unaffected. Clicking a contig or opening the whole-genome view produced no map at all. The listeners go on the grid instead, which exists by then and is the honest scope anyway: it holds the map, its lanes and the two zones, whilethis.elreaches the colourbar and the header, where a drag means nothing.It survived a type check, 697 unit tests and a code read, because none of them run the constructor. What catches this class of defect is opening the app, so the working file now gets opened: a genuine
.coolbuilt by the public cooler API, served over range requests, driven through the contigs table, the whole-genome view, a region, and a browser session with a track in it. -
The labels overlay could not be toggled in a genome browser session.
Lwas bound twice — the overlay, and “go to a locus” — and the command list is searched in order, so in a browser the locus box always won. The browser has one box that is both a locus bar and a search field, and it now has one key for it (/), which is the key every other view answers too. -
The icon generator dropped every SVG attribute whose name contained a digit, so every
<line>was emitted with no coordinates and drew nothing. It had been latent because no icon used one; the magnifiers added here came out as bare circles. -
A lane’s hover controls took presses while they were invisible.
opacity: 0hides a control but does not stop it being clicked, so every track carried a band across it — three hundred pixels wide and thirty tall — where a drag started on a<select>and the view silently refused to pan. They are now transparent to the pointer until the lane they belong to is the one being pointed at, and the pointer moving onto them no longer counts as leaving the view: without that the row goes untouchable, the pointer lands back on the canvas, the row is revealed again, and the control under the hand is never there long enough to be clicked. -
“Fit tracks to the space” measured the element it was about to resize, which is sized by its own contents — so it compared the tracks against themselves, found them already fitted, and did nothing at all. It now measures the column the view was mounted in, walking past the
display: contentswrapper that gives a session’s panels to the shell’s grid and reports every rectangle as zero. -
Every control that appears on hover — a tab’s ×, a track’s colour swatch — is now inert while it is invisible, and shown outright where there is no pointer to hover with (
@media (hover: none)). Both halves matter:opacity: 0hides a button and does not stop it taking a click, and a control revealed by pointing is a control that appears never on a touch screen. Each appears with no transition and fades only on the way out, so there is no moment in which one is clickable and barely drawn. -
Moving the caret from one lane’s limit straight to another’s kept the first number. The press that lands on a second limit is handled on
pointerdownand callspreventDefault, so it never produces the focus change that would have committed the first — and the editor was being cancelled on the way out. The natural gesture for setting the same bound on two tracks was therefore the one gesture that silently threw the first value away, while clicking anywhere else on the canvas committed it. -
With the words switched off, the lane’s two limit boxes were seeded once and never refreshed, and committing either one wrote both — so typing a floor republished whatever ceiling the row happened to be built with, silently undoing a zoom or a drag of the ceiling slider. Each box now commits its own bound and takes the other from the axis on screen, exactly as the drawn range does, and the untouched box follows the lane it is beside.
-
Choosing PNG collapsed the export row: the resolution select inherits
width: 100%from the controls card, and aflex-basis: autoresolves to it, so a control that must not shrink claimed the whole line — crushing the format select to nothing and pushing Save off the card. -
A click in the Sequences card now moves the view directly rather than through the view state. A patch that serialises to what is already stored is dropped without notifying, so clicking the sequence the hash already named could do nothing at all.
-
fmt.intgroups digits in the viewer’s locale, and half of Europe groups them with a space WinAnsi has no byte for — so a figure exported in French read7?071 in view, every grouped number corrupted by the app’s own formatter, in the one place nobody would look for a locale bug. Those spaces now fold onto the non-breaking space WinAnsi does have. -
Ticking a track did not open the batch bar until the focus left the list. The rebuild is deferred while a control in the list is being edited — a colour picker closes and a half-typed name is lost otherwise — and a checkbox was being counted as one, which deferred the very rebuild the tick was asking for. It commits on the click that focuses it, so there is nothing to protect; the focus is put back on the same box afterwards, so a second tick needs no second aim.
Documentation of chromosomix v0.20.0 · built from 40ec1ec · source
