Skip to content

Releases: dantebertuzzi/Perth.jl

v0.9.2

Choose a tag to compare

@github-actions github-actions released this 17 Aug 03:30

Perth v0.9.2

Diff since v0.9.1

Perth.jl v0.9.2 β€” the version, where you can see it

A patch release: one small addition to the chrome, no API change, nothing to migrate. Every 0.9.1 project opens exactly as it did. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Added

  • The version, on the right end of the status bar. Which Perth is running was the REPL banner's answer, and whoever opened the link never saw the banner β€” yet that is exactly the question that comes up when something is not where it was expected to be ("does this one have marked months yet?"). A tag icon with the number at the end of the footer, in both the gantt and the kanban, dimmed like the rest of the bar and lighting up on hover: true all the time, urgent never.

    The answer comes from the server, through /api/apps β€” the route that already describes what is up in this process β€” and not from the static file. The app.js the browser holds may come from a service worker's cache of an earlier version, and a label that lies about the version is worse than no label. It is asked once, at boot; if the answer does not arrive the tag simply does not appear, so there is never a tag icon sitting there without a number.

    The style lives in shared/ui.css, defined once for both tools, and the tooltip is translated into all five languages β€” a key that exists in only one of them fails the i18n suite. Covered by six checks in the jsdom suite and by a testset over both routes: the kanban has its own dispatch, so forgetting the field there would not show up in the gantt.

v0.9.1

Choose a tag to compare

@github-actions github-actions released this 16 Aug 22:20

Perth v0.9.1

Diff since v0.9.0

Perth.jl v0.9.1 β€” the read-only menu, closed properly

A patch release: one visual fix, no API change, nothing to migrate. Every 0.9.0 project opens exactly as it did. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Fixed

  • The read-only menus no longer end in a gap. Opening a plan through a read-only link hides every item that writes, but the separators between them stayed behind. The Edit menu came down to a single "View selected task" with three orphan rules stacked underneath it β€” an empty strip at the bottom of the box that read as a broken menu β€” and File left one dangling where "Delete project…" used to be.

    A rule only earns its place between two visible items. applyReadOnly() now recomputes which ones survive whenever the mode changes: no rule at the top, none doubled up, none hanging off the end β€” and they all come back the moment editing is allowed again. Covered by the jsdom suite, so the next item hidden from a menu cannot bring the gap back.

Also since v0.9.0

  • The README was rewritten and translated into five languages, with a real screenshot of the chart, and the kanban documentation moved back next to the code it describes.
  • The Frontend CI job was red on a Node version, not on a defect; that and a boot race in the browser test are fixed.

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 16 Aug 18:44

Perth v0.9.0

Diff since v0.8.9

Perth.jl v0.9.0 β€” finding your place, and keeping it

A minor bump because two things you can see change on their own: the zoom no longer sends you back to today, and the interface is monospaced throughout. No API was removed and every 0.8 project is read exactly as it was β€” the two new fields default to nothing. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Added

  • What you folded survives the reload. Collapsing the phases is how you work a plan of a hundred-odd tasks, and it was thrown away on every reload, every trip to the kanban and back, every project switch. It now lives in the browser next to the zoom, the theme and the lanes β€” one key per project, because "Ana" folded here does not mean "Ana" folded in the next plan, and ids that no longer exist are dropped on the way back in.

  • Walking the plan from the keyboard. The selection existed but only the mouse moved it β€” and moving the selection is the thing you do most. ↑/↓ walk the visible rows (a folded phase counts as one row, not as the twenty it hides), ← closes a summary and, on a leaf, goes up to its parent, β†’ opens it, Home/End jump to the ends, PageUp/PageDown move a screenful. Ctrl+wheel zooms, keeping the date under the pointer where it is.

  • Marked months. A whole month painted in the ruler at the top of the chart (File β†’ Marked months…, or add_month_mark!). Where a marked day draws a line across the plan, a marked month colours the strip that already writes its name β€” said once, at the top, instead of repeated on every task inside it. The name is optional here, unlike a marked day: the cell already says which month it is, so the colour alone is allowed to be the whole message. It never touches the body of the chart β€” shading the work behind a stretch is what a Band is for.

  • Markdown in a task's notes, and a note you can actually read. The red dot said "this task has a note" and the text arrived in a native browser tooltip: no formatting, no decent line breaks, gone if the pointer wavers. The dot now opens the note, in a popover that renders **bold**, *italic*, `code`, ~~strike~~ and links β€” the same one-line subset the kanban card speaks, which moved to shared/inline.js so two screens with the same meaning stop having two parsers.

    The formatting stops at the notes, on purpose. A task's name sorts its siblings, is what the search matches, and is what goes into CSV, iCalendar, .perth.jl, the PNG and the REPL β€” markdown there would either leak as punctuation into all of them or need a stripping step remembered in eight places, and the day one is forgotten an asterisk lands in a client's spreadsheet.

  • A test that measures what the screen is doing. In a real browser (jsdom has no layout engine, and without real text widths there is no collision to find) it seeds a project built to collide and crosses the box of every text, shape and line against every other, at four zooms, both densities, with lanes and with the critical path on. Both layers were checked against a deliberately sabotaged build.

Changed

  • Changing the zoom no longer teleports you to today. It ended in scrollToToday() every time: you would scroll out to November, zoom in to read the detail, and land back on today. Now the date you were looking at β€” the middle of the screen, or the pointer when the zoom comes from the wheel β€” stays where it is, the way any map behaves. Going back to today is still one key away: that is what T and the Today button are for.

  • The interface is monospaced end to end. The menubar, the table headers and the numbers already were; task names and bar labels were not, which made a name look like it came from a different application than the header right above it.

Fixed

  • Nothing on the chart is written on top of anything else any more. The audit found 11 to 17 collisions per view β€” worst at month zoom, present in every density β€” nearly all of one family: the sideways names of calendar bands and marked days written across task names, and the full-height lines (today, marked day, band edge) drawn straight through them. Lines now open a gap wherever they cross a label, and sideways names look for a free stretch instead of always starting at the top, which is where almost every plan has its first bars. Measured after: zero, in every configuration.

  • A programmatic scroll no longer leaves the ruler behind. Mirroring the day ruler waited for the scroll event, which arrives later; in between, header and chart disagreed, and a click on the ruler in that window marked a day other than the one under the finger.

  • The link dot no longer eats the first letter of the task's name, and an error Perth raised for a human to read no longer arrives as "internal error" β€” a project on a business-day calendar opened without using BusinessDays failed every calendar-aware route with a generic 500, hiding the one sentence that solved it.

  • The Share dialog no longer breaks on a payload without view_urls, and the four languages are level again: every string the interface can show is translated in all of them.

Merged pull requests:

  • feat: navegar pelo plano β€” dobras que ficam, teclado, e zoom que nΓ£o … (#8) (@dantebertuzzi)
  • feat: markdown na nota da tarefa, e uma nota que dΓ‘ para ler (#9) (@dantebertuzzi)
  • feat: meses marcados e markdown nas notas (0.9.0) (#10) (@dantebertuzzi)

v0.8.9

Choose a tag to compare

@github-actions github-actions released this 16 Aug 15:54
1e95387

Perth v0.8.9

Diff since v0.8.8

Perth.jl v0.8.9 β€” nothing is written on top of anything else

Nothing breaks: no API change, no new field, and a project written by any 0.8 is read exactly as it was. This release is about what the chart draws where. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Fixed

  • Nothing on the chart is written on top of anything else any more. Instead of looking for collisions by eye, this release measures them: an audit takes the box of every text, shape and line and crosses all of them against each other. It found 11 to 17 per view β€” worst at month zoom, present in every density β€” nearly all of one family: the sideways names of calendar bands and marked days written across task names, and the full-height lines (today, marked day, band edge) drawn straight through them. Only the dependency arrows knew how to get out of the way.

    The machine the arrows use is now shared. Lines open a gap wherever they cross a label, and sideways names look for a free stretch instead of always starting at the top, which is where almost every plan has its first bars. label_at, set by hand on the slider, still wins over the automatic choice. Order matters and is now written down in the code: first every sideways name finds its place, then every line is drawn β€” doing it annotation by annotation leaves the first line cut only by what existed before it, and the second name lands on top of it.

    Measured after: zero collisions at day, week, month and fit zoom, in both densities, with lanes on and with the critical path on.

  • Two of the bugs were found by measuring rather than by looking, and neither was visible from the source: the box of a sideways name was six pixels off (rotated, the glyphs sit from x-3 to x+11, not centred on the anchor), which is why a marked day's line still clipped its own name; and a label's box was estimated from its baseline instead of measured, missing the four pixels the tallest letters reach β€” exactly the sliver a vertical line was still eating. A first attempt also made things worse in a way only the audit caught: told to look for a free height, a name that only avoided text walked straight onto a bar.

  • The Share dialog no longer breaks on a payload without view_urls. The read-only link added the field in 0.8.8 and the dialog iterated it without a guard, so a server from an earlier version β€” or any response that omits it β€” threw and took the whole dialog down.

Added

  • A test that measures what the screen is doing. The audit now runs in CI: in a real browser (jsdom has no layout engine, and without real text widths there is no collision to find) it seeds a project built to collide and sweeps four zooms, both densities, lanes and critical path. The geometry underneath β€” cutting a line around a box, finding a free height for a sideways name β€” is unit-tested without a browser, so the logic stays covered where Chrome is not installed. Both layers were checked against a deliberately sabotaged build: they report 10 to 33 collisions per scenario when the fix is removed.

  • The four languages are level again: every string the interface can show is translated in all of them. The glossary shipped in 0.8.8 in Portuguese only, which the i18n suite had been calling out β€” 54 keys Γ— 3 languages.

Merged pull requests:

  • fix: nada Γ© escrito por cima de nada no grΓ‘fico (0.8.9) (#7) (@dantebertuzzi)

v0.8.8

Choose a tag to compare

@github-actions github-actions released this 16 Aug 14:58
a0ceb4c

Perth v0.8.8

Diff since v0.8.7

Perth.jl v0.8.8 β€” a link that only shows, and an order you choose

Nothing breaks: two fields are new β€” order on a task, label_at on a marked day β€” and both default to 0, which is exactly the old behaviour. Projects saved before this release load unchanged, and a plan nobody reordered still comes out by date. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Added

  • A read-only share link (view_key). A second key, independent of the access key: one link edits, the other only shows β€” Perth.run(view_key = "…"), Perth.view_key!, or the Share / QR dialog. Sharing was all-or-nothing, and that is what stopped a plan from being sent to a client, a director, the whole site.

    The refusal is the server's, decided by the method rather than by a list of routes, so a route added tomorrow is refused by default. That includes the door the interface does not use β€” the presence socket: the chat persists to disk and reaches everyone, so it is writing, and refusing the PUT while leaving the socket open would be changing the lock and leaving the window open. The payload of /api/share is written for whoever asks: the editing key cannot appear to someone who came in through the read-only link, which would undo the link on the first screen.

    The interface stops offering what the link cannot do β€” the editing menus, the drag gestures and the chat composer are gone rather than left there to fail β€” and viewers show up among the connected machines as a hollow ring: present, not writing. Changing either key disconnects only the machines it invalidated.

  • Order the plan by hand: drag a row up or down (move_task!, order). Row order was derived β€” children under their parent, siblings by (start, name) β€” and stayed a good order right up to the day three tasks start together and the sequence on site is not alphabetical.

    Dropped in the gap between two rows, the task takes that position; dropped on top of a task, it becomes a subtask of it. One gesture, two destinations, decided by where you let go β€” the convention of every file tree, and one gesture fewer than making the WBS a separate operation. order is 0 until someone drags, so a plan nobody reordered still comes out by date; a drop renumbers the whole sibling group 1, 2, 3, …, so a group is never half by hand and half by date, and the statement is about one group only.

  • A # column at the left of the task table: the row order written down, so it can be read and checked after a drag instead of counted with a finger. Its tooltip carries the task id β€” the thing you type in the REPL, which until now had to be fished out of an export.

  • A new warning: "starts before its dependencies allow." A dependency never moves anything on its own in Perth β€” that is schedule!'s job β€” so a plan can hold a task that starts before its predecessors let it, and until now the only sign of it was an arrow drawn backwards on the chart. The warning gives the earliest date the task could start and how many days early it is; when the start is pinned the row says so, because that is precisely the case auto-schedule will not fix.

  • A slider for where a marked day's name sits (label_at, 0–100 percent of the chart height; a slider per row in File β†’ Marked days…, or the keyword on add_marker!). The name lies along the line it names, so it always lands on something, and which bar it lands on depends on the plan. A percentage rather than pixels: the chart grows with the plan, and "a third of the way down" should stay a third of the way down.

  • Help β†’ What the words mean: a glossary of the vocabulary the interface speaks β€” task, summary, WBS, dependency, slack, critical path, baseline, S-curve, PERT, P80, overallocation, band, marked day. ⚠ 4 overallocations Β· ⚠ 1 past deadline is only a warning to someone who already knows the words, and the only place they were explained was the package documentation, which whoever opens the browser does not read.

  • Telling whose baseline a ghost bar is. The ghost lives on its task's own row, but a task that moved a long way leaves it far from its bar, and "same row" becomes guesswork on a busy chart. Hovering it names the task and gives the promised dates; selecting the task lights its ghost up and joins the two with a dotted line β€” which is the slippage drawn at actual size. Neither adds anything to the chart at rest.

Fixed

  • An error Perth raised for a human to read no longer arrives as "internal error." A project on a business-day calendar opened without using BusinessDays failed every calendar-aware route with a generic 500, hiding the one sentence that solved it. Messages the package raises itself now reach the screen; anything else stays a 500 with no detail, because that is a bug of ours and not the browser's business.

  • Lines and labels no longer cross each other. A dependency arrow leaves the end of the bar, which is exactly where the label starts, and struck the word through; a marked day's dotted line ran down through its own name. Pushing the text aside would detach it from what it names, so the line opens a gap instead β€” around every label it passes, on any row. The click target stays whole: what disappears is the stroke, not the sensitive area, so double-clicking an arrow to remove it still works anywhere along it.

  • The link dot no longer eats the first letter of the task's name. The dot you drag a dependency from is born at the end of the bar, where the label starts. The label now cedes it room, and only while the dot is there β€” only the selected bar has one, and moving every label away from every bar because of it would be paying for it across the whole chart.

Merged pull requests:

v0.8.7

Choose a tag to compare

@github-actions github-actions released this 16 Aug 03:11

Perth v0.8.7

Diff since v0.8.6

Perth.jl v0.8.7 β€” draw the dependency, fit the plan

Nothing breaks: no API change, and a project written by any 0.8 is read exactly as it was. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Added

  • Link two tasks by dragging. A dependency was the only relation drawn on the chart that could still only be declared in a form. Select a bar and it grows a dot at each end: drag from the right one to the task that follows, or from the left one to the task that comes before β€” both create the same finish-to-start link, what changes is which end of the chain you are building from. Lag, start-to-start and finish-to-finish stay in the modal: they are the exception, and an exception does not need a gesture.

    The dots appear only on the selected bar. A dot that appears on hover would fight the bar drag and the resize grip, which live in the same pixels; selecting is already the gesture that says this one.

    Four refusals, each saying why on screen instead of quietly doing nothing: the pair is already linked; the drop would close a loop β€” refused before saving, because the engine reports a cycle only after the plan is stored, and then it stops scheduling; the target is a summary, whose subtasks are what actually get scheduled, so the link would promise what it cannot keep; or the two are in the same block, where a summary already waits for its children by definition. A summary may still be the predecessor: its finish is the finish of the block.

    Double-click an arrow to remove it, with a fat invisible hit area over the 1px stroke β€” creating a link with the mouse and having to open a modal to undo it would be giving the outward trip without the return. And the drop target comes from the whole stack of shapes under the pointer, not just the top one: at the edge of a bar the topmost shape is the critical-path or selection outline, which has fill: none and carries no id.

  • Zoom: fit (4, or the Fit button). The step is computed so the whole project lands on screen, instead of being one of three sizes chosen by hand β€” none of which suits a two-year plan. It is clamped at both ends (never larger than day zoom, never so small the chart becomes a smear), the ruler switches between days and weeks by the space available rather than by the name of the zoom, and fitting deliberately does not scroll to today: that would undo what the button just did. The chosen zoom is now remembered per browser.

v0.8.6

Choose a tag to compare

@github-actions github-actions released this 16 Aug 01:34

Perth v0.8.6

Diff since v0.8.5

Perth.jl v0.8.6 β€” days you can mark, blocks you can fold

Nothing breaks: no API change, and a project written by any 0.8 is read exactly as it was. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Added

  • Marked days β€” Marker, markers, markers!, add_marker!, remove_marker!, File β†’ Marked days…, and a double-click on the day ruler. A named day drawn as a vertical line across the whole chart, the way the today line is drawn and for the same reason: some dates matter to every task at once. A delivery, an audit, the day the scaffolding comes down.

    The gesture is the short one β€” the day is already under the cursor, so double-clicking its column opens the panel with the date filled in and the caret in the name, the only field the computer cannot guess. Typing the date into a form would be repeating to the computer something it just saw. Like a calendar band, a marker is annotation: it never moves a task and never enters the CPM engine; when a date must actually bind a task, that is the task's deadline, which does change its slack. A nameless marker is dropped β€” a line that does not say what it marks is a stroke on the screen.

    Each column of the day ruler now carries its own date (data-date), so the ruler describes itself instead of being read back through pixel arithmetic.

  • Collapsible WBS summaries. The β–Ύ on a summary row was decoration: the chart could group tasks into lanes and fold those, but a category β€” the thing the WBS exists to create β€” could not be closed. Clicking the arrow now folds the whole subtree, not just the direct children, and the summary bracket stays on the chart, so a folded block still says when it happens. The arrow does not select the task: a click on the arrow is about the tree, a click on the row is about the task. Searching a task inside a folded summary opens it, the same way it opens a folded lane.

  • Drag the divider between the task table and the timeline. The width was already a preference with a slider in settings, but nobody opens a preferences panel to read a task name that is cut off. The handle takes its bounds and its step from that same slider β€” two places with the same limit written by hand is one place that falls behind β€” and moves it live, so the two never disagree. Double-click resets to the default; the arrow keys move it one step when the handle has focus.

Changed

  • The kanban no longer pops a "Transmission on" toast when sharing is switched on: whoever turned it on just clicked the button, and the button already changes colour and label. The gantt never announced it, and the difference between the two was only inheritance.

v0.8.5

Choose a tag to compare

@github-actions github-actions released this 16 Aug 00:42

Perth v0.8.5

Diff since v0.8.4

Perth.jl v0.8.5 β€” who does the work, and when the calendar is different

Nothing breaks: no API change, and a project written by any 0.8 is read exactly as it was. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Added

  • A collaborator registry β€” people, person, people!, add_person!, remove_person!, and File β†’ Collaborators…. assignee was, and stays, free text, and free text fragments in silence: "Ana", "Ana " and "ana" become three different people to the workload, to the overallocation check and to the highlight, with nothing on screen explaining why. Every save now trims the name β€” ends and middle β€” and unifies spellings that differ only in case, adopting the first one the project already knows. Accents are deliberately not unified: "Ana" and "Γ‚na" may be two real people, and the computer cannot know they are not.

    Because the registry is consulted before the tasks, registering a name is how you fix a spelling everywhere: add_person!(p, "Ana Paula") re-spells every "ana paula" on the tasks. Each collaborator is a Person with name, role, team, email and notes β€” a name alone in a plan needs a side channel to be understood, and the role and team ride along in the assignee autocomplete, where they pay for themselves at the moment of choosing. The autocomplete offers the registry plus every name already used by a task; offering only the registry would hide names that already exist and invite retyping them, and retyping is what fragments. Removing someone from the list keeps their name on their tasks: they leave the list, not the work.

  • Swimlanes β€” group the chart by assignee or by team (Lanes: in the toolbar). This is why the names had to be clean first: with assignee fragmenting silently, the same "Ana" would have shown up as three separate lanes. The lane header is a row like any other, same height and same grid, so the table and the timeline stay one drawing β€” a header one pixel taller would slide every bar below it, and there is a real-browser test pinning exactly that. Collapsing a lane hides the tasks, not the person: what is left is a single bar from the first day of their work to the last, because whoever collapses wants less detail, not to lose the fact that they are busy from March to May.

    WBS summaries stay out of lanes β€” a summary brackets children who may belong to different people, and hanging it in someone's lane would claim they own the whole block. Grouping never repaints (a bar's automatic colour comes from its position in the project, not on screen), dependency arrows are drawn only between visible rows, and finding a task inside a collapsed lane opens the lane.

  • Statistics by person and by team β€” people_stats, team_stats, and View β†’ Statistics…. The engine already knew all of it and never added it up: how much each person carries, how much of that is done, how many days they are double-booked, how many of their tasks are past their deadline. The weight is the same one the S-curve uses, cost when set and person-days otherwise; two screens telling different stories about the same work would be worse than one screen fewer. Overloaded days are a person fact β€” two people from the same team working the same day is normal, and only an individual can be double-booked β€” so a team row sums its members' overloaded days rather than recomputing them. Work with no assignee gets its own row instead of being dropped: unowned work is a fact about the plan, not a gap to hide.

  • Calendar bands β€” Band, bands, bands!, add_band!, remove_band!, and File β†’ Calendar bands…. A named stretch of calendar shaded behind the chart, with the name written along its left edge: a sprint, a shutdown, the rainy season, the fortnight the crane is on site. The colour is yours to pick, and bands may overlap, because a crunch week inside a sprint is a real thing to say. A band is annotation, not scheduling: it never moves a task, constrains a date or enters the CPM engine β€” it answers "why is this stretch different?", which until now had to live in someone's head. Inverted ranges are swapped on save, the way a negative duration is clamped, and a nameless band is dropped: shading that does not say why is noise.

Changed

  • The WBS summary bracket is no longer a solid block in the text colour β€” a black bar among pastel ones pulled the eye to the container instead of the work. It is now a thin neutral rail whose filled part is the progress that already rolls up from the children, a number the summary had and showed nowhere on the chart.

Fixed

  • The collaborator panel could lose an edit: it reloaded the whole project after saving, and a second edit made during that reload was overwritten when the older response landed. Both panels now adopt the answer to their own PUT, which is already the normalized state, and never reload underneath themselves.

  • Four translation keys were defined twice with different wording in the same dictionary, where the last definition silently wins; 27 duplicate definitions in total were removed. The suite now fails on any repeated key, and on any key missing from one of the four languages.

v0.8.4

Choose a tag to compare

@github-actions github-actions released this 15 Aug 20:51

Perth v0.8.4

Diff since v0.8.3

Perth.jl v0.8.4 β€” Perth and your editor

Nothing breaks: no API change, and a project written by any 0.8 is read exactly as it was. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Added

  • The mirrored .perth.jl file now flows back. Mirroring already went one way: every save rewrote the file at file_path. Edit that file in your editor and the browser follows on its own β€” no REPL round trip, and no more losing your edit to the next save from the UI. Keep the file open in VS Code, keep the chart open in the browser, and let the two agree. Perth.run(watch = false) turns it off.

    The reload is deliberately conservative: our own writes are recognised by content rather than by timestamp, so mirroring cannot loop; a file caught half-written (editors save in stages) fails to parse and is ignored until the next event; the file carries content, never identity, so a pasted file cannot hijack another project's id, creation time or mirror path; and the project object is mutated in place rather than replaced, so a p = project("obra") you are holding in the REPL stays valid instead of silently detaching.

  • Find a task by name in the gantt β€” the box next to Highlight, or /, the same key the kanban uses. Non-matching tasks dim, in the table and in the chart. Accents and case do not matter: typing integracao finds IntegraΓ§Γ£o por partes, which is the point in a project with a hundred tasks β€” needing the right accent means needing to already know what you are looking for.

    Enter walks the matches, one at a time, wrapping at the end; Shift+Enter walks back. The counter reads position over total, like a find box in an editor (4/13), and each step selects the task and brings its bar into view β€” a task lives in time, not in the list, so scrolling the rows alone would light up a name whose bar sits months off screen. Search and Highlight compose, so you can filter to one person and search within that.

  • using Perth offers the doors, and you pick one with the arrows. The package exports the data API, but Perth.run() and Perth.kanban() are not exported (run would clash with Base.run), so nothing on screen pointed at them. Loading the package in an interactive terminal draws them as a list: ↑↓ to move, Enter to open, any other key to dismiss. Perth.menu() brings it back later.

    The list gives up on its own after six seconds, and it only takes the keyboard when the keyboard is idle. If anything is already feeding the session β€” VS Code's Execute File in REPL, julia -i behind a pipe, a batch include β€” those bytes are the caller's code, not keystrokes, and reading the first one corrupts it. Non-interactive sessions print nothing and wait for nothing; PERTH_SPLASH=0 silences it entirely.

Fixed

  • The test suite fails on a file with mixed line endings. The repository is CRLF, and any tool that reads and rewrites a file in text mode converts it silently β€” the diff becomes the whole file, or half of it turns LF and nobody notices. file is no guard: it reports "CRLF line terminators" with dozens of LF lines in the middle. Counting bytes is.

v0.8.3

Choose a tag to compare

@github-actions github-actions released this 15 Aug 19:19

Perth v0.8.3

Diff since v0.8.2

Perth.jl v0.8.3 β€” three ways in, closed

A security release. Every user of 0.8.2 or earlier should update, and anyone who runs Perth.share!() on a network they do not fully trust should update before running it again. No API change, no data change: a project written by any 0.8 is read exactly as it was. Full changelog: https://github.com/dantebertuzzi/Perth.jl/blob/main/CHANGELOG.md

Security

  • A project file could overwrite an unrelated file on disk. file_path is the mirror path of this machine, and the writer never puts it in a .perth.jl β€” the format's own comment says so. The reader did not enforce it: a hand-written or hostile file that declared the field had it accepted, and the first save wrote the project over whatever it pointed at β€” ~/.ssh/authorized_keys, a source file, a document β€” silently. It was reachable through Perth.load, which is exactly the documented way to open a file somebody sent you. The parser refuses such a file outright now, which covers every reader at once.

  • A few kilobytes of [[[[[ killed the server process. Julia's own parser is recursive and dies on deeply nested brackets β€” not a catchable exception, a core dump β€” so Meta.parseall never returned. Project source arrives over HTTP through /api/import, so any client that could reach the API could take the server down. Source is now checked before parsing: 4 MB and 32 levels of nesting, against the four levels a real file uses. Brackets inside strings and comments do not count, so a task named Coleta [campo] (fase 1) is still just a name.

  • What reaches the host machine is now host-only. With sharing on, any guest on the network could set a project's mirror path, browse the host's directory tree and start the kanban process. The mirror was the worst of the three: the path resolver accepts any path ending in .jl verbatim, so a guest could point it at ~/.julia/config/startup.jl and have the host overwrite that file on its next save β€” the .jl requirement, which looks like a guard, is exactly what puts the most sensitive target in reach. These three are not project edits, they are access to the machine, and they answer 403 to anyone but the machine running Perth now, like the sharing toggle and the access key already did.

Editing projects over a shared session is deliberately untouched β€” that is a different question, and a read-only switch is the right shape for it.

Added

  • The UI background accepts AVIF, still or animated. It is the same risk class as the WebP already accepted β€” a raster codec the browser decodes β€” unlike SVG, which stays out. The check is still by content: an ftyp box with any other brand, MP4 included, is refused.

Notes

  • Animated backgrounds, as measured in Chrome: GIF, animated WebP and animated AVIF all play. APNG is accepted β€” it carries the PNG signature β€” and it animates when the file is opened on its own, but it stayed on the first frame as a CSS background in every sample we took, while a GIF with identical timing advanced normally. Nothing in Perth touches the frames, so if animation matters, prefer GIF, WebP or AVIF.