Releases: NspxMiguel/Nuvo
Release list
0.9.6
Adding an exam now goes all the way to the file picker.
The three buttons that offer to add an exam — the empty-professor call to action, the sources header and the past-year shortcut — asked for a folder name and stopped there; the primary one even carried an upload icon. The folder was created empty, and finding where files actually go was left to the reader. Naming and filling are one flow now.
The seeded class-material folder is named Aulas: it used to sit under a section label of nearly the same name — identical in Spanish, one letter apart in Portuguese.
0.9.5
Process.terminate() only reaches the child when the window process exits cleanly, and it does not always: killed from outside, a crash, the system shutting down. The server stayed up alone, and the symptom surfaced far from the cause — you update the app, open it again, and the window finds something answering on the port and attaches to that: the old server, serving the previous version's files. New version installed, old version running.
The server now watches the process that started it, which is the only approach that covers every way the parent can go, including the one that runs no code on the other side. And the window handles SIGTERM and SIGINT itself, because AppKit does not turn them into quit — pkill and system shutdown were leaving both processes standing.
Measured on a real bundle: killed politely the server is gone in 0.4s, killed outright in 1.2s.
0.9.4
The title bar is transparent and the content runs to the top edge, which is what makes the window read as an app rather than a browser. The cost is that close, minimise and full-screen float over the top-left corner — and that corner is where the mark sits, so the three buttons landed on the logo.
The page cannot know it is inside that window, so the window tells it, and the stylesheet gives the sidebar the 22 points they occupy. With the drawer collapsed the same room goes to the top bar, which is what ends up under them instead.
Both the window and its verification mode now build the web view from the same configuration; before, the check ran without the very script it was meant to prove.
0.9.3
Both live at ~/Applications/Nuvo.app: the release bundle, which is a real app with a native window and the server beside it, and what instalar-app writes, which is a shortcut that opens the browser. Someone who installed from the site and then ran instalar-app — reasonably, since that is how the README said to get a Dock icon — traded the native window for a Chrome one with nothing explaining the change.
On macOS the command now recognises the release bundle by the server sitting next to the executable, says the app is already installed, and writes nothing. The check runs before the delete, which is the whole point.
0.9.2
Nuvo.app/Contents/MacOS/Nuvo was the Node binary, and it is the path old scripts and shortcuts know. Making it a window and nothing else broke that silently: someone who had written .../MacOS/Nuvo backup would get a window and no backup.
The window now steps aside whenever the caller is the command line — any argument, or a terminal on the other end — and hands the process over to the server binary with execv, so nothing sits in the middle. Opened from the Dock, with no arguments and no terminal, it is still the native window.
0.9.1
The Dock icon opens a window of our own now. The bundle used to launch Chrome with --app=, which gives a window with no tabs and no address bar but is still Chrome: its icon in the Dock, "Google Chrome" in Cmd+Tab, and clicking Nuvo in the taskbar opening the browser. An app that opens another app is not an app.
The bundle's executable is a WKWebView in an NSWindow, compiled at packaging time, with the Node binary beside it. It starts the server only if nothing is already answering, checks that whoever answers is a Nuvo before handing over the access token, and stops the server on quit only when it was the one that started it — so someone with nuvo already running in a terminal does not lose that session by closing the window. Links outside 127.0.0.1 open in the real browser. Without swiftc on the packaging machine the bundle falls back to the Node binary alone, which still opens the old way.
Creating a teacher is its own page, not a form appended below the list. With half a dozen teachers you had to scroll past the table to reach a field that had just appeared, and the screen showed half of each. Escape goes back, the cursor starts in the name field, and creating goes straight to the teacher.
The three organisation options had never had a style rule of their own — they inherited the generic button and came out centred like photo captions. The guard that catches classes with no CSS now covers the three families that had grown outside it.
0.9.0
Six defects, all of them found by exercising what the app promises on its own screens.
A dropped connection said "network error" — English either way, past the reach of any dictionary, printed in red in the middle of a conversation written in Portuguese. It also withheld the one fact that matters: the server is the reader's own, and it stopped.
The research blamed the search engine for everything. A request with no question ran the whole pipeline for nine seconds before saying "no search results". And DuckDuckGo answers a blocked request with HTTP 200 and an anti-bot challenge page, so "no results" was what appeared when waiting would have fixed it. The block is now recognised and named, and the reason travels to the final error.
The research report opened with the model discussing its own tooling — "simple writing task from a single source, not worth fanning out agents". That cost more than tidiness: the same prose broke the JSON parse of the search plan, so the planner fell back to searching the question verbatim and the research read one source instead of four. With the prompt fixed it plans four angles and reads four.
The empty-state sentence contradicted itself, saying "you already turned on 2 AIs" and "both are turned off" in one line — and blaming the reader for AIs the app found by itself.
Verified along the way and found sound: the whole chat screen (ask, regenerate, edit, delete, export, automatic title), first run on a machine with no AI installed at all, the anonymous chat — which really does store nothing — and the file-editing mode, with a path outside the project folder refused.
0.8.9
Two promises the app makes on its own screens, neither of them kept.
The council did not read the shared memory. "One memory, read by every AI" is the sentence the app opens with, and the screen whose whole point is asking several AIs the same question was the only one reading none of it. It surfaced in a test that asked two AIs for a fact only the memory held: one answered correctly by luck, the other answered — correctly — that it had no way to know. Both now answer from it, and the screen is told which facts were used, the same as a chat. Web research still runs without memory on purpose: it turns the request into search-engine queries, and personal facts do not leave the machine.
The search never looked inside documents. The text of every attachment has always been indexed — it is how the model finds the passage it quotes — and the sidebar search never consulted it. Someone who attached five past papers and searched for a word inside them got "nothing found". Hits are one row per file and clicking one goes to wherever the file lives.
Verified along the way and found sound: all six studio formats generated for real and drawn in both themes; the previous-year paper flow; backup and restore into an empty install with the raw upload files; and 440 kB of attachment, 61 teachers and 400 conversations without the screen stalling.
0.8.8
Twelve defects, found by probing the API surface and clicking through every panel.
The portrait was still a black box. The model returns, per topic, which papers it appeared in and a literal quote; none of it reached the screen, so the legend read "osmose 13%" — a number with no origin. And corrections were worse than absent: the field existed in the stored portrait, survived every regeneration, the PATCH route accepted it, a comment said "the machine proposes, the person decides" — and nothing wrote one, nothing read one. There is now a Not quite button, and the correction goes into the next synthesis as a rule that outranks the model's own reading. Verified against the real model: the portrait listed mitosis, a correction said mitosis had been dropped from the term, and the rebuilt portrait no longer listed it.
The built-in profiles shipped in Portuguese, and the Assistant's instruction told the model to answer in Brazilian Portuguese — so someone using the app in English got an assistant that answered in Portuguese. They are now seeded in the machine's language, and NUVO_LANG forces the language of both server and screen.
The generated mock exam summed to 9.0 instead of 10 — twice in a row. The prompt never stated a total and nothing checked the arithmetic.
Also: a JSON body of null returned a 500 on six routes; an empty profile or project name silently created "Novo perfil"/"Novo projeto"; a double click on a create button created two rows; an empty upload failed with no explanation; a finding label sat at 2.1:1 from two multiplied opacities; three controls cleared their focus ring and put nothing back; and four on-screen strings written as t(x ? 'a' : 'b') had never been translated, because the coverage test could not see them.
Backup and restore verified end to end into an empty install, raw upload files included.
0.8.7
You can open a source file now. A file could be added to a professor and then never read again — the column knew its name, its role and how many chunks it became, but the content existed only inside generated answers. That made "it cites the file it came from" a claim with nothing to check it against. Clicking a source opens the document in the middle column, the same way a folder or a generated output already opens there.
It is also where a bad extraction announces itself: a PDF that came through as garbled characters shows up on the file itself, rather than three screens later in a mock exam that reads strangely for no apparent reason.
Builds no longer pile up. build/dist had kept every package since 0.1.0 — 7.3 GB of tarballs for releases already published here and rebuildable from any tag.