<!--
The workshop's projects: every project of `$PRJ` with its worktrees, the
checkouts outside it, and the projects the workshop's notes and links name
that are not cloned on this machine — each of those one click from being
cloned.
-->
<script lang="ts">
import { onMount } from "svelte";
import { levels, actionKey } from "../../stores/levels.svelte.js";
import { peers } from "../../stores/peers.svelte.js";
import { router } from "../../stores/router.svelte.js";
import { workbench } from "../../stores/workbench.svelte.js";
import { opensInWorkbench } from "../../utils/open-link.js";
import WorktreeRow from "./WorktreeRow.svelte";
import NewWorktreeForm from "./NewWorktreeForm.svelte";
onMount(() => {
void levels.ensureLoaded();
void peers.load();
});
</script>
<div class="projects-page">
<header>
<h1>Projects</h1>
<p class="subtle">
{#if levels.prjRoot}
In <code>{levels.prjRoot}</code>
{:else if levels.loaded}
No <code>$PRJ</code> on this machine: only worktrees outside it.
{:else}
Reading this machine…
{/if}
</p>
</header>
{#if levels.error}
<p class="page-error" role="alert">{levels.error}</p>
{/if}
{#if levels.slotsError}
<p class="page-error" role="alert">prj could not list its session slots: {levels.slotsError}</p>
{/if}
{#each levels.problems as problem (problem)}
<p class="page-error" role="alert">{problem}</p>
{/each}
<section aria-label="Projects here">
{#if levels.loaded && levels.projects.length === 0}
<p class="empty">No project in <code>$PRJ</code> yet.</p>
{/if}
<div class="cards">
{#each levels.projects as project (project.name)}
<article class="card" data-project={project.name}>
<header class="card-head">
<a
class="card-title"
href={router.href({ level: "project", project: project.name })}
onclick={opensInWorkbench((beside) => workbench.openProject(project.name, { beside }))}
>
{project.name}
</a>
<span class="subtle">
{project.github ?? "no GitHub origin"} ·
{project.worktrees.length} worktree{project.worktrees.length === 1 ? "" : "s"}
</span>
</header>
{#each project.worktrees as worktree (worktree.id)}
<WorktreeRow {worktree} />
{/each}
<div class="card-foot">
<NewWorktreeForm project={project.name} />
</div>
</article>
{/each}
</div>
</section>
{#if levels.referenced.length > 0}
<section aria-label="Not cloned here">
<div class="section-title">Named by links, not cloned here</div>
<ul class="referenced">
{#each levels.referenced as named (named.project)}
{@const outcome = levels.outcomes[actionKey.clone(named.project)]}
<li>
<a href="https://github.com/{named.project}" target="_blank" rel="noreferrer">{named.project}</a>
<span class="subtle">named by {named.by.join(", ")}</span>
<button
class="secondary tiny"
disabled={outcome?.running}
onclick={() => void levels.clone(named.project)}
>
{outcome?.running ? "Cloning…" : "Clone"}
</button>
{#if outcome?.error}
<span class="page-error" role="alert">{outcome.error}</span>
{/if}
</li>
{/each}
</ul>
</section>
{/if}
{#if levels.standalone.length > 0}
<section aria-label="Outside projects">
<div class="section-title">Outside projects</div>
<article class="card">
{#each levels.standalone as worktree (worktree.id)}
<WorktreeRow {worktree} />
{/each}
</article>
</section>
{/if}
</div>
<style>
.projects-page {
max-width: 1200px;
margin: 0 auto;
padding: var(--wb-spacing-24) var(--wb-spacing-20);
}
h1 {
font-size: var(--wb-font-size-18);
font-weight: 600;
margin: 0;
color: var(--vscode-editor-foreground);
}
code {
font-family: var(--vscode-editor-font-family, monospace);
}
section {
margin-top: var(--wb-spacing-24);
}
.page-error {
color: var(--wb-error);
font-size: var(--wb-font-size-12);
}
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
gap: var(--wb-spacing-16);
}
.card {
border: 1px solid var(--vscode-panel-border);
border-radius: var(--wsb-radius-lg);
background: var(--vscode-sideBar-background);
overflow: hidden;
}
.card-head {
display: flex;
align-items: baseline;
justify-content: space-between;
padding: var(--wb-spacing-10);
}
.card-title {
font-size: var(--wb-font-size-14);
font-weight: 600;
color: var(--vscode-editor-foreground);
text-decoration: none;
}
.card-title:hover {
text-decoration: underline;
}
.card-foot {
padding: var(--wb-spacing-8) var(--wb-spacing-10);
border-top: 1px solid var(--vscode-panel-border);
}
.referenced {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: var(--wb-spacing-6);
}
.referenced li {
display: flex;
align-items: baseline;
gap: var(--wb-spacing-8);
}
</style>