Releases: junkerderprovinz/arrowloop
Release list
v0.7.5
The Android app ships with the release, signed with ArrowLoop's own key, and the README gets a button for it and one for the container.
✨ Added
- The Android app is attached to every release: an
arm64APK for phones and anx86_64APK for emulators, each once with the version in its name and once without. - Docker and Android download buttons, in a second row under the desktop ones. Docker opens the container image's page, Android downloads the newest APK.
🎨 Design
- One band of light crosses all three rows of buttons in turn, the donation row first. Three rows need a little more than seven seconds, so the loop on this page runs 8.2 seconds. The band keeps the speed and the pause it has on every other README.
⚡ Improved
- Released APKs are signed with ArrowLoop's own key instead of Android's public debug key, which anyone could use to sign an update that installs over the app. The release build checks every APK's certificate against the real one and stops if it does not match. An APK installed from an earlier CI build has to be uninstalled once before this one will install.
- The app reports the release's version. Its version name and version code come from the release tag.
v0.7.4
The crypto window's way out is a button in its row again, with its words and its glyph.
🎨 Design
- The close button is back in the window's button row, and it follows the labelling setting like every other button in the app: text, text with glyph, or glyph alone, whichever that one setting says. The square X in the corner that the last release introduced is gone, and so is the missing footer under it.
- Why it went back. A corner square that is glyph-only in every mode is a control opting out of the engine that governs every other control, and a window's way out is the last place that should be an exception. The tidier argument, that a bar holding a single button is a row with one thing in it, lost to that one.
- The confirmation window is unchanged again too, its corner control carrying its label the way it did before.
v0.7.3
A window's way out moves to the square in its top right corner, the download row carries the same band the give row does, and the PayPal button opens a page that asks for an amount.
🎨 Design
- The crypto window has no button row any more. Its way out is the square with an X beside the title badge, and that square is square in every label mode, which is the one thing an ordinary row action deliberately is not. A corner control is placed as much as it is drawn: a word printed there grows a lozenge into the badge beside it and reads as part of the title. The window kept a whole line at the bottom to hold one button, and a bar with one button in it is a row of buttons with one button in it.
- The confirmation window stops printing "Close" beside its heading. Its corner control had followed the labelling setting like every other button, on a sound principle, and the result was a third, wider control saying what its own X already said while the footer below carried the two answers that decide anything.
- The four download buttons carry the house band. A tilted band of light crosses each of them once every seven seconds, and it is the same band the give row runs rather than a second effect that happens to look similar. It is measured in screen pixels rather than in the canvas each button was drawn on, which is why the two rows now agree: 33px wide, 250px per second.
- One band works its way down the page, one row at a time. The download row runs first and the give row follows once that row is finished, instead of both crossing at once side by side.
- The separator between two buttons stands on its own line. A
glued to the closing tag measures 8.77px where one standing alone measures 13.16, and the step from one button to the next is derived from that number, so the two spellings are not interchangeable.
⚡ Improved
- The PayPal button opens a donation page rather than a handle. It says who is being paid, carries a sentence about what the money does, offers three amounts and a free one, takes a card without a PayPal account, and has a box for making it monthly.
- GlimStone 2.6.0, and the About card says so.
v0.7.2
The design language's copies here were newer than the number that named them, and two of its rules had never been adopted. Both are in now, so the version on the About card is a claim the files back up.
🎨 Design
- A page entrance moves and no longer scales. An ancestor that scales while its contents animate makes the compositor resample them through it, which is where the shimmer on the top level came from. The entrance travels and stops there.
- The fade is its own animation, on a curve that cannot overshoot. Opacity never rides a spring: a spring drives its value through the target and back, which is the point for a distance and meaningless for opacity, because it clamps at 1. The fade used to finish early and then sit flat, so the dial set to make an entrance longer made its fade shorter.
- One curve token became two. A page may overshoot; a toast slides in from the edge of the screen, and a spring carries it back past that edge, which reads as a glitch. The quiet level names no toast curve on purpose, and at the top level both spring, because that is the level where restraint is the thing being switched off.
⚡ Improved
- The About card matches the language's own file again. The two had drifted apart in their comments after the same fix landed in both by hand.
- The GlimStone version on the card reads 2.5.0, which is what these files now are. It had been left at 1.14.0 while the files themselves were carried forward, so the one number a reader can check was the one thing that was wrong.
v0.7.1
The About card says what it has to say in fewer sentences, asks for support in a friendlier way, and its buttons stop losing the bottom of their letters.
🎨 Design
- The crypto button wears the bare Bitcoin letterform instead of the coin disc. At 16px, beside a coffee cup and a PayPal P, a filled circle hands the eye the shape of the ground rather than the shape of the letter, so the one button that should say "crypto" said "orange dot". The full coin logos stay in the donation window, where the marks are large and telling eight of them apart is the job.
- The mail button carries an envelope. It was the one control in that row with no mark at all, which reads as a missing image rather than as a plainer button. Its label names the thing rather than the act: "Email" instead of "Send an email", in every language.
⚡ Improved
- A shorter, friendlier support text, in 42 languages. Two sentences listing what the app does not do became one, the itemised running costs are gone, and the ask is a coin tossed to the knight. The card and the README now carry the same claims in the same order, so the page somebody reads before installing and the card they find afterwards no longer sound like two different projects.
🐛 Fixed
- Button labels lost the bottom of every descender. The label box hides its overflow while the button sets a line height of 1, so at 14px every p, g, y and j was cut about 1.7px short. Both label boxes carry their own line height now, and nothing else moves.
v0.7.0
Four questions this could not answer before, and all four are answered in the database rather than in the browser. That distinction is the release: the activity log runs to tens of thousands of rows, the screen holds a few dozen, and filtering what was already fetched searches the last page instead of the log.
✨ Added
-
Search the activity log by path. "Which run touched that file" was not answerable at all: the log shows the newest few dozen entries and a file touched a thousand rows ago was simply gone. The search narrows at the server, so a name from three weeks back comes back on the first press. It escapes LIKE's own wildcards, which is less pedantic than it sounds - an unescaped
a_bmatchesurlaub, because the underscore means "any one character" and paths are full of both. The box stays on screen when a search finds nothing, so a mistyped letter still has a way back to the full list. -
Narrow the history to a stretch of days. "What happened at the weekend" had the same problem from the other end: a job watching a folder writes a run a minute, so ten thousand rows sit between Friday and Monday and the only way back through them was to keep asking for more. Two calendar days, either of which may be left blank for an open end, and both blank is the plain newest-first list it has always been. The upper bound is the LAST instant of its day rather than its midnight, so "up to the 3rd" no longer hides everything that happened on the 3rd.
-
How full a target is. The question people have BEFORE a sync rather than after: a job copying eighty gigabytes onto a cloud drive with twelve left fails eighty gigabytes in, having spent an evening on it. Shown on the same press as the connection check, so it costs nothing until somebody asks. Most cloud targets cannot answer - a bucket store has no quota to report and will take another terabyte - and those are reported as not knowing rather than as empty, because "no limit at all" and "nothing free" must never render the same.
-
Find duplicate files. The same holiday photos under three names, an archive unpacked twice, a backup of a backup. It reports and never deletes: which of three identical files is the one to keep is a decision about what your folders mean, and the copy in
urlaub/and the copy inbackup/are the same bytes and not the same thing. Per side rather than per job, because a job's two sides are supposed to hold the same files. Sets are listed biggest-waste-first, with what deleting the extra copies would actually return - counting every copy but the first, since the first one is the file rather than a duplicate of it. Files the target cannot checksum are counted and named, because that is the one thing that makes the answer incomplete. -
A saved target is asked whether it answers. Saving a target proved nothing: a typo in an address or a password with a trailing space was written to the file as happily as a working one, and the first anybody heard of it was a failed run at three in the morning. The check now runs on save and reports what the server said. It never blocks - a server that is switched off right now is not a mistake, and closing the form stays your decision.
⚡ Improved
- The About card's prose runs the width of its card. It was the only capped text on its page, so three paragraphs stopping two thirds of the way across read as hand-set line breaks rather than as a measure.
v0.6.2
Every job created through the interface failed on every single run. If you have set one up and it has never copied anything, this is why, and updating is the whole fix.
🐛 Fixed
- A job created in the interface could never run, on the clock or on a change. Every run ended with
create schema: unable to open database file (14)and nothing was copied. The interface gives each new job a state path ofstate/<name>.db, and nothing ever created thatstatefolder: SQLite creates a database file that is not there and will not create the directory it was asked to put it in, because from where SQLite stands a missing directory is indistinguishable from a typo. Its refusal isSQLITE_CANTOPEN, whose message names a file and means a folder, which is why this read like a permissions problem for as long as it did. Both databases this program keeps now make the folder they were asked to write into. A container that had been running for hours, reporting a failure every minute and syncing nothing, was what it took to find it.
v0.6.1
A folder this program creates on a network share can now be written to, which it could not before. If you run the container on Unraid or any other box where the share belongs to somebody other than root, this one matters.
🐛 Fixed
- A folder created through the browse panel came out unwritable on a share. It was
root:rootwith mode 0755 while the share around it wasnobody:userswith 0777, so the person who asked for the folder could open it and not put anything in it. The mode handed toMkdiris only a ceiling: the process umask masks it, and a container's default of 022 is exactly what turns 0777 into 0755. A new folder inherits the mode AND the owner of the folder it is created in now, which is the right rule rather than a fixed mode - it makes the new folder behave like the one it sits in, and that is correct on a private disk as well as on a world-writable share. Reported with the Windows dialog attached, and measured on the box before anything was changed. - The files a run copies had the same defect, arriving quietly. A folder you cannot write to announces itself immediately; a copied file that belongs to root does not, until somebody tries to edit it. That write belongs to rclone, so there is no moment for this program to step into and no way to fix it the way the folder was fixed.
ARROWLOOP_UMASKcovers it at the one lever that reaches every create the process makes. The container image sets000, which matches the share it writes into; a desktop install sets nothing and nothing changes there, because there the process is the user and the owner is already right. The Unraid template carries the field with000filled in, never blank - a blank template field overrides the image default rather than falling back to it.
⚡ Improved
- The engine tab saves as you change it, and its save button is gone. A page of settings is not a form somebody fills in and submits: every control on it stands alone, so a button at the foot means a switch flipped at the top does nothing until you scroll down and find it. Writing is delayed by a moment, because half of these controls are text boxes and saving per keystroke would refuse "1" on the way to "1M" and flash an error at somebody typing correctly.
- A refusal now sticks to the bottom of the window. That is the price of taking the button away: with a button the answer appears where the finger just was, and saving as you type puts it at the foot of a long page while the eye is on a field near the top. Only a refusal sticks; "saved" is not news worth pinning over the page.
🎨 Design
- The README has a row of download buttons above its contents, one per platform, drawn from a template rather than by hand. Each carries the platform's own mark and its own colour, with no outline: a filled shape in a colour somebody already associates with the platform does the work an outline was doing, and the eye finds "the blue one" before it reads the word. The marks come from Font Awesome Free, whose icons are CC BY 4.0, and each is used the one way a trademark may be used without permission - to name the thing it refers to, on a button linking to a download for exactly that platform. They link straight to the newest release: the release workflow now uploads each bundle twice, once with the version in the name and once without, because GitHub serves
releases/latest/download/<name>only on an exact name and every versioned link in a README goes stale at the next tag. - The paragraph explaining the name explains the current one. It described what a manorial reeve did, which was the working title's story and not this program's.
🧹 Housekeeping
- The language bar stops counting the translation tables as if they were the program. 1.26 MB of the repository is forty-two locale files and a generated glyph file holding raw SVG paths, all wearing a
.tsextension, which made TypeScript the "main language" of a program whose engine is Go. Marked as data in.gitattributes: Go 0.97 MB against 0.63 MB of actual interface code.
v0.6.0
Two rounds of live review. The job card told you the same thing three times, its buttons ignored the one setting that governs them, and the three explanations on the schedule panel explained the wrong things.
✨ Added
- A dropdown switches on the mouse wheel. Every picker in the app: hover it and roll, and it steps through its values without opening. It clamps at both ends rather than wrapping, because one notch too many should not land a value from the other end of the list. GlimStone has promised this for a native
<select>since 1.5.0, and this app replaced its last one, so the behaviour went with it. - The whole setup travels in one file, from where you would look for it. Export and import sit under Settings, General, next to the language, rather than among the engine's own settings: those are values the engine reads, and this is the file that holds all of them plus every job. The buttons are named for the acts and wear one box glyph with the arrow reversed. The confirmation before an import is the app's own window now, not the browser's grey box, which was the one dialog in the program that no setting reached.
- The job card leads with what the job IS. The two paths in large type with the arrow between them, and the mark the logo is drawn from standing at the far left as the status display. It was a 12px row with a 14px arrow, set in the same size as the schedule under it, so nothing on the card led.
- Its schedule, in words, at the top right. "runs every 3 hours" rather than
@every 6h, and "runs Mon, Fri at 03:00" rather than0 3 * * 1,5. The card was printing the engine's own vocabulary at somebody who never asked to learn it. Under it, the last run with a real date and time, because the relative form answers "is this thing alive" and cannot answer "was that before or after I changed the folder". - An activity log on the card, folded away. The same rows the history draws and the same detail panel under them, for this one job. It was nowhere: the card said when the job last WORKED, and everything else meant leaving the page and reading a list of every job's runs.
- Real time is a schedule now, not a switch beside them. It was a toggle under the strip, which made it read as an extra rather than as one of the answers, and that is exactly how it went missing in the first place. Picking it also writes a BACKSTOP, shown right there and editable: the engine has always refused a watching job with no schedule, because a watcher can miss an event and never know it did, and that requirement is now visible instead of implied.
- A second standardised button height, and there will not be a third. The button that creates a job and the direction switch stand one step up, at 2.5rem. There was exactly one height before, which is right for every button that stands in a row of fields and could not answer the button a page is about. Raising the one height would have put every control in the app out of line with every field beside it to solve a problem two controls have.
🎨 Design
- The steppers on a number field are inside the field again. They were being placed against the edge of the COLUMN: a labelled field is a column flex container, and the default stretch spread the wrapper across the full width while the box itself stayed at 6rem, so two arrows sat at the far right of the row with the field alone on the left. Four fields, one declaration. GlimStone 1.7.5.
- The both-ways arrow is two arrows. The transform list reads right to left, and the old one squashed the arrow along its length rather than across it, then pushed it off the top of the box before the rotation turned that overrun into an overrun off the right edge. Both halves ended up with their shafts in the same horizontal band, so it drew one broken bar with clipped stubs. It is a stacked pair pointing opposite ways now, measured rather than assumed: tips at 11.1 and 2.9 across a 14-unit box, symmetric about the middle.
- Every button is in the colour and label engines. Sixteen row actions were badges, which put them outside the one setting that decides what a button shows, and twenty-two more passed no translation key, so the glyph resolver could not pick them a symbol and they fell back to text in the two modes that exist to hide it. The resolver itself was eight keys against forty buttons and is a rule table now.
- The three buttons on the About card have their marks. That card is GlimStone's own, byte for byte; what was missing was that the app had never told the resolver what
about.repolooks like. - One control, one hover bubble. Seven elements carried both a
titleand the app's owndata-tip, so each opened two bubbles saying the same sentence, the second drawn by the operating system a moment later, at the pointer instead of at the trigger. - The direction switch is no longer blank in text mode. Its only child is its glyph, and the rule that hides a glyph in that mode had no exception for a control that has no words to fall back to.
- The unit picker beside the quiet period is a fixed narrow column. It holds one of three words and was being handed every pixel the row had left over: half an editor row in the job form, and most of a card on the Engine tab.
- A new job's quiet period starts at five seconds. An empty box is a real setting that means "act on the first event", which is the one answer nobody wants when a folder of a thousand files arrives at once.
- The job card's seven controls are one row of one control. Every one of them paints in its card's own colour instead of a flat grey the colour engine cannot reach, and every one of them answers the app-wide label setting: words where the setting shows words, a row of square tiles where it does not. Five of them used to be permanently glyph-only by a documented exemption, which from outside looks exactly like five controls ignoring the setting, and was reported as such twice. The activity fold's button stands with them now rather than pushed to the far end, and its list opens under the whole row.
- A glyph alone in a square is half its box. 20px is the right size beside 14px text, where a mark and its words have to read as one control; in a 32px tile there are no words to match and 20px fills 62% of the frame. Reported as chunky, and it was: the sibling app's own icon tiles have carried a 16px drawing in a 32px box all along.
- The unit beside "every N" and beside the backstop is a dropdown. It was a four-segment strip spelling out minutes, hours, days and weeks next to a number box, which spent most of a form column saying one word.
- Every explanation in the app is in a bubble. Three were still printed as grey paragraphs on the page. Prose under a control is read once and then costs vertical space for ever, and there is a guard for it now: a translation key whose name says it explains something may only be passed as a prop, never rendered.
⚡ Improved
- A new job arrives switched on. It arrived held purely so the validator would accept it before it had any sides, so every job anybody created announced itself as disabled until they found a switch at the bottom of the form. A job with NEITHER side is a draft now and loads: it has no schedule, so nothing reaches it. A job with ONE side and not the other is still refused when it is switched on, because that is a form half filled in rather than a draft. A duplicate still arrives held, and that is deliberate: it points at the same two folders as the job it was copied from, with a state database of its own.
- The job form has no active switch at all any more. A new job has arrived switched on for a while, so the switch spent its life showing the answer it was created with, and holding a job is not something anybody decides while filling in a form. That control lives on the card, next to the one that starts a run by hand, and it saves itself.
- The schedule, the settle time and the backstop say what they are FOR. All three explanations described the mechanism instead of the reason, and the schedule's still described a cron field that has not existed since the builder replaced it. They now name the six positions, count the fifty events a folder of fifty files produces, and say plainly that only the side on this machine can be watched, which is why the backstop exists at all.
- Four button labels that were written as tooltips are verbs again. "Edit this job" reads well spoken on hover and badly printed on a button, and four such sentences wrapped the card's action row onto two lines.
- The bandwidth limit explains itself. The old text said how to write the value and never what the setting does or what to put in it. It now answers both, in that order, and says the honest thing first: leave it empty unless a sync actually makes the rest of the network unusable.
- The card says a job is held once, not three times. A badge reading "disabled", the word "disabled" beside it, and "never worked" beside that, all on the same card. The state is the mark at the other end of the row, which was already carrying it in colour and in its own tip.
🐛 Fixed
- A bandwidth limit now takes effect when it is saved. It was written to the file and nowhere else, so the setting was correct, the page showed it, and the next transfer went at whatever speed the program booted with. A plain limit applies to the next transferred block; a timetable applies its current slot the same way and its later slots are followed by a ticker that is started here if the process booted without one.
- An invalid bandwidth limit is refused when it is saved. It saved cleanly and the program then refused to START on the next boot, which is the worst place to find out: the message goes to a console nobody is watching, and the interface that could have shown it is the thing that is no longer running.
- The unsaved job's row is the same row as a saved one. It was drawn at...
v0.5.0
Forty-six reported points, and the largest finding was that the program could already do more than it showed.
✨ Added
- An Engine tab, for the settings that were always read and never settable. The bandwidth limit, how many jobs may run at once, where the run log lives, who gets told when a run finishes, and both brakes. Every one of these was being read by the engine and could only be set by editing the configuration file, which from the outside is the same as not existing. The brakes are the point: they are the net that stops a run removing more than half of everything it knows about, and they could not be seen at all.
- Shared defaults, so the job form can stay short. What is usually the same for every job lives in one place and a job keeps only what makes it different. Empty folders and metadata became three-state settings in the process, because a plain switch cannot tell "off" from "not mentioned", and a default would have turned a deliberate "off" back on without a word.
- Named exclude lists. Written once and asked for by name, instead of the same twenty lines pasted into every job and drifting apart. A job asking for a list that does not exist is refused when the file is read: a filter that silently matches nothing does not break anything, it just quietly syncs the thing you asked it to leave alone.
- The history opens. A row said twelve copied and two conflicts; clicking it now says which files, and for a conflict what was decided. That last part is the one worth having: a scheduled run keeps both versions because it has nobody to ask, so the decision was made on your behalf while you were not watching. It can be revisited from there, which starts a fresh run for exactly those paths rather than rewriting what the first run did.
- A month of runs, drawn above the list. Days with no runs are empty columns rather than missing, because a chart that closes its gaps turns "nothing happened" into "nothing to show".
- Room is checked before an automatic run starts. A full destination is worse than a failed run: the transfer stops midway, the tree holds a partial file, and the record and the disk disagree in a way only a full re-scan resolves. A run started by hand is never refused, and only the space finding stops one: a side that does not exist yet is created by the very run being refused.
- A health check and a consistency check, on a button. The first asks whether the job can work at all. The second compares both sides against the record and reports where they disagree, marking each finding with whether any future run would notice it by itself. That is the failure a sync tool cannot see: if a row claims the two sides agree when they do not, every later run compares both against a record that matches both.
- The first run can be told which side is right. With no record yet, every file on both sides counts as new, so the default merges them and the other side fills with files you meant to leave behind. Choosing a side makes it win every disagreement while a file it never had is left exactly where it is: seeding copies, it does not mirror. It applies once.
- The bin can be looked in. Nothing was ever deleted outright, and that was only half a promise while the only way to look was a file manager. It lists, one entry goes back, and old runs clear out by age. An entry whose age cannot be read says so and is never cleared by age.
- A file that is overwritten can keep its history. The last few contents of an edited file, off by default, filed under the same reserved directory the bin uses.
- A schedule that only reports. The whole comparison runs on time, writes down what it would have done per file, notifies, and touches nothing. Pressing the button by hand still writes.
- An optional password. Off by default and unchanged for every install that does not set one. The hash comes from the environment and deliberately not from the configuration file, which is served whole as a backup and can be replaced whole by a restore.
- Start and hold on a job's card, and the jobs in the tray menu. Two verbs and never one button: a held job that could no longer be started by hand would be a deleted job with extra steps.
- Real time in the schedule, where you look for it. It is an addition to the schedule rather than one of its modes, because a watcher that missed an event has no way to know it did, and the schedule is what eventually notices.
- Not on battery and not on a metered connection. Conditions only the desktop can ask about, so the container is unaffected. An unknown power state counts as mains: a desktop PC with no battery reports unknown on some hardware.
- Duplicate a job, and keep a copy of the whole setup. The backup is the file's own bytes, so every key this build has never heard of comes back the way it went in.
🎨 Design
- The cross and the check were reaching too far. A cross puts its tips on the corners of its box and a round glyph puts its ink on the edge midpoints, so the same box makes the cross reach sqrt(2) further. Measured at 9.49 against 7.0.
- The direction arrows carried a bar behind the arrowhead, which is the "jump to the end" keyboard idea rather than a direction. One drawing now, turned.
- One height for everything that stands in a row with a field. An input took its height from its padding and the button between two of them was written as a separate number. Neither was wrong alone.
- The settings strip is tabs rather than a groove. A strip in a track reads as one control with a slot, whatever size it is.
- The quiet period is a number and a unit rather than a text box with a syntax to guess, and the number answers the mouse wheel while the field has focus.
🐛 Fixed
- A file held open by another program was only probed for copies. A rename of an open file, a conflict between two open files and a deletion that could not bin its victim all fell through to a generic failure with a raw system message attached, and renames were never probed at all. The files are now asked directly after a failure, because the transfer writes a partial file and renames it into place, and Windows refuses that rename onto a held file with plain access-denied rather than a sharing violation.
- A weak agreement said nothing about being weak. Checksum verification after a transfer was already there; what was missing is that "this backend has no checksums" and "this file could not be read" arrived as the same empty answer, and the comparison quietly dropped to a length and a clock reading.
- Three reasons the engine really gives had no wording at all, so a plain delete printed a path, a colon and nothing.
- The folder picker's buttons wrapped onto two lines in German, and the window is wider now so the row is a promise rather than a hope.
- The language picker and its flags were a size too small.
📚 Documentation
- The example configuration shows the new settings again, and two guards keep it that way: one loads it through the same function that guards a hand-written file, the other checks that what the README points people here for is actually shown.