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
2 changes: 1 addition & 1 deletion help/guide/custom-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ For example:
https://andeplane.github.io/atomify/?examplesUrl=https%3A%2F%2Fyou.github.io%2Fphysics101%2Fexamples.json
```

The **Example library**, the sidebar's **Examples** list, the examples on Home and **Quick-run an example** then use that library, and the library's `title` becomes the heading. The parameter stays in the address as you move around the app, so the link can be bookmarked and shared.
The **Example library**, the examples on Home and **Quick-run an example** then use that library, and the library's `title` becomes the heading. The parameter stays in the address as you move around the app, so the link can be bookmarked and shared.

## Requirements

Expand Down
3 changes: 1 addition & 2 deletions help/guide/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ Atomify comes with more than 50 ready-to-run examples, from simple Lennard-Jones
## Find examples

- **Example library** in the sidebar shows every example as a card with a picture and description.
- The sidebar's **Examples** list shows every example by name.
- Home shows four of them under **Start from an example**.
- Home shows up to 16 of them (a 4×4 grid) under **Start from an example**.
- **New project → From example** has a search box that filters by title, description and keyword.

![The Example library](/screenshots/example-library.webp)
Expand Down
2 changes: 1 addition & 1 deletion help/guide/projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The folder name is the project name in lowercase, with accents removed and anyth

### Other ways to start a project

- **Use as project** on any example (Home, Example library, or click an example in the sidebar's **Examples** list).
- **Use as project** on any example (Home or the Example library).
- **Save as project** on a [quick run](/guide/quick-runs).

## Open a project
Expand Down
1 change: 0 additions & 1 deletion help/guide/quick-runs.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Any of these:

- **Quick-run an example** on Home (runs the first example in the library).
- The ▶ button on an example card on Home or in the **Example library**.
- The ▶ button next to an example in the sidebar's **Examples** list.

The buttons are disabled until the engine has loaded.

Expand Down
2 changes: 1 addition & 1 deletion help/reference/bundled-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { data as examples } from "./examples.data";

# Bundled examples

Atomify ships with {{ examples.length }} examples. Open any of them from the **Example library**, the sidebar's **Examples** list, or **New project → From example**. This table is generated from the app's own example list, so it is always current.
Atomify ships with {{ examples.length }} examples. Open any of them from the **Example library**, Home, or **New project → From example**. This table is generated from the app's own example list, so it is always current.

- **Multithreaded**: the script uses `suffix kk` and runs on 4 threads ([Multithreading](/guide/multithreading)).
- **Notebook**: the example ships its own analysis notebook.
Expand Down
2 changes: 1 addition & 1 deletion help/tutorials/example-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Add `?examplesUrl=` and the address of your `examples.json` to the Atomify link.
https://andeplane.github.io/atomify/?examplesUrl=https%3A%2F%2Fyou.github.io%2Fphysics101%2Fexamples.json
```

Open it. The **Example library** now shows your examples under your title, the sidebar's **Examples** list shows them too, and **Quick-run an example** on Home runs your first one.
Open it. The **Example library** now shows your examples under your title, Home shows them under **Start from an example**, and **Quick-run an example** on Home runs your first one.

## 5. Share the link

Expand Down
6 changes: 5 additions & 1 deletion src/shell/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { PlayIcon, PlusIcon } from "./icons";

export const LAST_PROJECT_KEY = "atomify_last_project";

/** Home shows a 4×4 grid of examples; the library has the rest. */
const HOME_EXAMPLE_COUNT = 16;

const HomeScreen = () => {
const projects = useStoreState((state) => state.projects.projects);
const activeRun = useStoreState((state) => state.projects.activeRun);
Expand Down Expand Up @@ -339,13 +342,14 @@ const HomeScreen = () => {
</a>
</div>
<div
data-testid="home-examples"
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
gap: 16,
}}
>
{ui.examples.examples.slice(0, 4).map((example) => (
{ui.examples.examples.slice(0, HOME_EXAMPLE_COUNT).map((example) => (
<ExampleCard key={example.id} example={example} />
))}
</div>
Expand Down
29 changes: 0 additions & 29 deletions src/shell/Shell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,35 +199,6 @@ describe("Shell", () => {
);
});

it("lists examples in the sidebar below the projects", async () => {
renderShell();

const row = await screen.findByTestId("sidebar-example-diffusion");
expect(screen.getByTestId("sidebar-examples-list")).toContainElement(row);
expect(row).toHaveTextContent("Diffusion");
// Projects come first, examples below.
expect(
screen
.getByTestId("sidebar-projects-section")
.compareDocumentPosition(screen.getByTestId("sidebar-examples")) &
Node.DOCUMENT_POSITION_FOLLOWING,
).toBeTruthy();
// Quick run waits for the engine, which never loads in these tests.
expect(
screen.getByTestId("sidebar-example-quick-diffusion"),
).toBeDisabled();

await userEvent.click(screen.getByTestId("sidebar-example-use-diffusion"));
expect(screen.getByTestId("new-project-modal")).toBeInTheDocument();
});

it("hides the sidebar examples list when collapsed", async () => {
renderShell();
await screen.findByTestId("sidebar-example-diffusion");
await userEvent.click(screen.getByTestId("sidebar-collapse"));
expect(screen.queryByTestId("sidebar-examples")).not.toBeInTheDocument();
});

it("creates a blank project from the modal and opens its workspace", async () => {
renderShell();

Expand Down
152 changes: 2 additions & 150 deletions src/shell/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/**
* Global sidebar (ADR-003 §2): Home, Example library, the PROJECTS list with
* color dots + running pulse, New project, a scrollable EXAMPLES list (click
* to use as a project, play to quick-run), and a footer with the theme
* color dots + running pulse, New project, and a footer with the theme
* toggle, Settings, and the engine-loading chip (loading never blocks
* browsing — ADR-003 §6). Collapsible to a narrow icon rail (persisted) so
* the simulation gets the horizontal room.
Expand All @@ -17,7 +16,6 @@ import {
AtomLogo,
ChevronRightIcon,
CompassIcon,
PlayIcon,
GridIcon,
HelpIcon,
HomeIcon,
Expand All @@ -30,7 +28,6 @@ import {
import { HELP_URL } from "./helpUrl";
import AboutModal, { AUTHOR_NAME, AUTHOR_URL } from "./modals/AboutModal";
import { ColorDot, PulseDot } from "./ui";
import type { Example } from "../hooks/useExamples";

/** Persisted collapse choice — the rail should survive reloads. */
const SIDEBAR_COLLAPSED_KEY = "atomify_sidebar_collapsed";
Expand Down Expand Up @@ -91,102 +88,6 @@ const sectionLabelStyle: CSSProperties = {
color: "var(--text-3)",
};

/**
* One row of the sidebar examples list: thumbnail + title opens the
* "use as project" flow; the play button quick-runs it.
*/
const SidebarExample = ({ example }: { example: Example }) => {
const ui = useShellUI();
return (
<div
className="shell-hoverable"
data-testid={`sidebar-example-${example.id}`}
style={{
display: "flex",
alignItems: "center",
gap: 4,
borderRadius: 9,
paddingRight: 4,
}}
>
<button
onClick={() => {
track("Sidebar.Example.UseAsProject", { exampleId: example.id });
ui.useExampleAsProject(example);
}}
title={`Use “${example.title}” as a project`}
data-testid={`sidebar-example-use-${example.id}`}
style={{
display: "flex",
alignItems: "center",
gap: 10,
flex: 1,
minWidth: 0,
padding: "5px 0 5px 8px",
border: "none",
background: "transparent",
color: "var(--text-2)",
fontWeight: 500,
fontSize: 13,
fontFamily: "inherit",
cursor: "pointer",
textAlign: "left",
}}
>
<span
aria-hidden="true"
style={{
width: 32,
height: 24,
flexShrink: 0,
borderRadius: 5,
background: "var(--viewport)",
backgroundImage: example.imageUrl
? `url("${example.imageUrl}")`
: undefined,
backgroundSize: "cover",
backgroundPosition: "center",
border: "1px solid var(--border)",
}}
/>
<span
style={{
flex: 1,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{example.title}
</span>
</button>
<button
onClick={() => ui.quickRunExample(example)}
disabled={!ui.engineReady}
title={ui.engineReady ? "Quick run" : "Engine loading…"}
aria-label={`Quick run ${example.title}`}
data-testid={`sidebar-example-quick-${example.id}`}
className="shell-hoverable-text"
style={{
width: 24,
height: 24,
flexShrink: 0,
borderRadius: 6,
border: "none",
background: "transparent",
color: "var(--text-3)",
cursor: ui.engineReady ? "pointer" : "not-allowed",
opacity: ui.engineReady ? 1 : 0.5,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<PlayIcon size={10} />
</button>
</div>
);
};
/**
* A NavButton look-alike that is a real link, so it opens in a new tab and
* middle-click / "copy link" work as users expect.
Expand Down Expand Up @@ -239,7 +140,6 @@ const Sidebar = () => {
);
const ui = useShellUI();
const [aboutOpen, setAboutOpen] = useState(false);
const examples = ui.examples.examples;

const [collapsed, setCollapsed] = useState(() => {
try {
Expand All @@ -248,8 +148,6 @@ const Sidebar = () => {
return false;
}
});
// The icon rail has no room for a list; "Example library" still links there.
const showExamples = !collapsed;
const toggleCollapsed = () =>
setCollapsed((previous) => {
const next = !previous;
Expand Down Expand Up @@ -361,11 +259,10 @@ const Sidebar = () => {
flexDirection: "column",
}}
>
{/* Nav + projects: natural height, scrolls only when space is tight. */}
<div
data-testid="sidebar-projects-section"
style={{
flex: showExamples ? "0 1 auto" : "1 1 auto",
flex: "1 1 auto",
minHeight: 0,
overflowY: "auto",
overflowX: "hidden",
Expand Down Expand Up @@ -504,51 +401,6 @@ const Sidebar = () => {
</button>
)}
</div>

{showExamples && (
<div
data-testid="sidebar-examples"
style={{
flex: "1 1 0",
minHeight: 140,
display: "flex",
flexDirection: "column",
borderTop: "1px solid var(--border)",
}}
>
<div style={{ padding: "14px 24px 7px" }}>
<span style={sectionLabelStyle}>Examples</span>
</div>
<div
data-testid="sidebar-examples-list"
style={{
flex: 1,
minHeight: 0,
overflowY: "auto",
overflowX: "hidden",
padding: "0 12px 12px",
display: "flex",
flexDirection: "column",
gap: 1,
}}
>
{examples.map((example) => (
<SidebarExample key={example.id} example={example} />
))}
{examples.length === 0 && ui.examples.loading && (
<span
style={{
padding: "6px 12px",
fontSize: 12.5,
color: "var(--text-3)",
}}
>
Loading examples…
</span>
)}
</div>
</div>
)}
</nav>

{!ui.engineReady && (
Expand Down
Loading