Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.
Bot has no folders it can read right now. Folder access ends when you stop OpenBot. To give it a
folder, use Choose folder in Settings → Computer." It said "No host folders are granted to this
Bot."
- **Install's bar is full only when it is done.** Once every byte has arrived and the last items
are still unpacking, the bar moves without filling and the line says "1.1 GB downloaded ·
Unpacking the last item". It showed a full bar beside "1.1 GB of 1.1 GB · 1 of 5 items
remaining".

### Teach a Bot

Expand Down Expand Up @@ -173,6 +177,8 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.
- LangChain is called **LangChain**, with LangChain's own mark, where it said "LangChain
(LangGraph)": in New Bot, a Bot's "Powered by" subtitle and its Runs on row, and OpenBot Desktop's
note under a ChatGPT plan.
- Onboarding's Welcome says "Give work to Bots in plain words.", where it said "Hand work to
Bots".

## 0.3.4

Expand Down
2 changes: 1 addition & 1 deletion app/src/routes/_authed/onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ function welcomeStep(computer: boolean): OnboardingStep {
</section>
}
>
<p>Hand work to Bots in plain words. They tell you when it's done.</p>
<p>Give work to Bots in plain words. They tell you when it's done.</p>
</StepIntro>
),
demo: () => <IntroReel computer={computer} />,
Expand Down
12 changes: 12 additions & 0 deletions app/tests/onboarding-route.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,18 @@ test("arriving without a step writes the first one into the address, in place",
expect(view.history.length).toBe(1);
});

// "Hand work to Bots" was read with "Hand" as a noun. "Give" can only be the verb.
test("Welcome says to give work to Bots in plain words", async () => {
const view = open("/onboarding?step=welcome");
await heading(view, "Welcome to OpenBot");
expect(
view.getByText(
"Give work to Bots in plain words. They tell you when it's done.",
),
).toBeTruthy();
expect(view.queryByText(/Hand work to Bots/)).toBeNull();
});

test("Continue adds a step to the address and to history, and saves it", async () => {
const view = open("/onboarding?step=welcome");
await heading(view, "Welcome to OpenBot");
Expand Down
47 changes: 47 additions & 0 deletions desktop/src/SetupProgress.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,53 @@ test("install progress is the bytes downloaded out of the whole download, and di
expect(row.queryByRole("progressbar")).toBeNull();
});

// Reported on 1.0.0-rc.1: a full bar read "1.1 GB of 1.1 GB · 1 of 5 items remaining" while the
// last image was still being unpacked, so a person took the step for done.
test("with every byte in and an item still unpacking, the bar is not full and says it is unpacking", () => {
const size = 1_100_000_000;
const view = render(
<SetupProgress
steps={installing({ done: 4, total: 5, bytes: size, size })}
showElapsed={false}
/>,
);
const row = within(
view.getByRole("listitem", { name: "Installing OpenBot" }),
);
const said = "1.1 GB downloaded · Unpacking the last item";
let bar = row.getByRole("progressbar", { name: "Download progress" });
// No value: the indeterminate bar, not a full one.
expect(bar.hasAttribute("value")).toBe(false);
expect(bar.getAttribute("aria-valuetext")).toBe(said);
expect(row.getByText(said)).toBeTruthy();
expect(row.queryByText(/remaining/)).toBeNull();

view.rerender(
<SetupProgress
steps={installing({ done: 2, total: 5, bytes: size, size })}
showElapsed={false}
/>,
);
bar = row.getByRole("progressbar", { name: "Download progress" });
expect(bar.hasAttribute("value")).toBe(false);
expect(
row.getByText("1.1 GB downloaded · Unpacking the last 3 items"),
).toBeTruthy();

// Bytes still arriving: the bar shows its fraction, as before.
view.rerender(
<SetupProgress
steps={installing({ done: 4, total: 5, bytes: 1_000_000_000, size })}
showElapsed={false}
/>,
);
bar = row.getByRole("progressbar", { name: "Download progress" });
expect(bar.getAttribute("value")).toBe("1000000000");
expect(
row.getByText("1.0 GB of 1.1 GB · 1 of 5 items remaining"),
).toBeTruthy();
});

test("a download whose size is not known yet says so, and its bar does not guess", () => {
const view = render(
<SetupProgress
Expand Down
40 changes: 32 additions & 8 deletions desktop/src/SetupProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,15 @@ function showsDownload({ bytes, size, total }: Downloads): boolean {
/**
* The bytes downloaded out of the whole download, with a bar driven by the bytes. Until the size
* is known the line says so and the bar is indeterminate: it claims no fraction it cannot know.
*
* Once every byte is in and items are still being unpacked, the bar is indeterminate again. A full
* bar beside "1 of 5 items remaining" read as nothing left to do while the last image was still
* being unpacked and tagged.
*/
function DownloadProgress({ downloads }: { downloads: Downloads }) {
const summary = downloadSummary(downloads);
const known = downloads.size != null && downloads.size > 0;
const known =
downloads.size != null && downloads.size > 0 && !unpacking(downloads);
return (
<div className="downloads">
{known ? (
Expand All @@ -281,13 +286,32 @@ function DownloadProgress({ downloads }: { downloads: Downloads }) {
);
}

/** "1.2 GB of 3.4 GB · 1 of 5 items remaining", or what is known of it. */
export function downloadSummary({
bytes,
size,
done,
total,
}: Downloads): string {
/**
* Every byte is in, and some items are still being unpacked, imported or tagged. That brings no
* more bytes, so the bytes alone would say the step is done.
*/
function unpacking({ bytes, size, done, total }: Downloads): boolean {
return (
size != null &&
size > 0 &&
bytes >= size &&
total != null &&
(done ?? 0) < total
);
}

/**
* "1.2 GB of 3.4 GB · 1 of 5 items remaining", or what is known of it. Once every byte is in:
* "3.4 GB downloaded · Unpacking the last item".
*/
export function downloadSummary(downloads: Downloads): string {
const { bytes, size, done, total } = downloads;
if (unpacking(downloads)) {
const remaining = (total ?? 0) - (done ?? 0);
return `${formatDownloadBytes(bytes)} downloaded · Unpacking ${
remaining === 1 ? "the last item" : `the last ${remaining} items`
}`;
}
const parts: string[] = [];
if (size != null) {
if (size > 0)
Expand Down
Loading