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
4 changes: 2 additions & 2 deletions apps/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ serves the built `dist/` on its own origin.
- `src/pages/`: sign-in, the project library, settings and providers;
`pages/workspace/` is the open project (conversation, documents, design
concepts, live app).
- `src/client.ts` and `src/api/`: the `api` object the screens call, built on
the typed client and the hub's routes.
- `src/api/`: the calls the screens make, built on the typed client and the
hub's routes.
- `src/queries/`: TanStack Query keys and the shared query client.
- `public/mockup-frame.html`: the sandboxed page design mockups render in.
- [`DESIGN.md`][design]: the workspace layout and styling contract.
Expand Down
10 changes: 8 additions & 2 deletions apps/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import {
useState,
useSyncExternalStore,
} from "react";
import { api, ApiFailure, type ProjectSummary } from "./client.js";
import { listProjects } from "./api/projects.ts";
import { ApiFailure, type ProjectSummary } from "./api/types.ts";
import { singleFlight } from "./single-flight.ts";
import type { ModelProvider } from "@corbits-builder/schemas/models";
import {
ArrowLeft,
Expand Down Expand Up @@ -433,7 +435,7 @@ export function App() {
});
const projectsRead = useQuery({
queryKey: keys.projects,
queryFn: () => api.projects(),
queryFn: readProjects,
refetchInterval: FOLD_POLL_MS,
});
const projects = projectsRead.data?.projects ?? NO_PROJECTS;
Expand Down Expand Up @@ -777,5 +779,9 @@ export function App() {
const NO_PROJECTS: ProjectSummary[] = [];
const NO_PROVIDERS: ModelProvider[] = [];

const readProjects = singleFlight(async () => ({
projects: await listProjects("all"),
}));

/** What the home fold rests on; `refresh` re-reads all of it at once. */
const FOLD_READS = [keys.modelCatalog, keys.projects, keys.workspaces] as const;
147 changes: 0 additions & 147 deletions apps/web/src/client.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/web/src/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* sanitiser to keep up to date. What is not handled below renders as text.
*/
import type { JSX, ReactNode } from "react";
import { DEL, END, INS } from "./revisions.js";
import { DEL, END, INS } from "./revisions.ts";

/**
* Inline spans: code, bold, italic, links, with tracked changes threaded through them.
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/pages/project-library.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { Ellipsis } from "lucide-react";
import { useEffect, useRef, useState, type CSSProperties } from "react";
import type { ProjectStage } from "@corbits-builder/schemas/project";
import type { ProjectSummary } from "../client.js";
import type { ProjectSummary } from "../api/types.ts";
import type { NoticeAction } from "../components.jsx";
import { ProjectMenu } from "./project-menu.jsx";
import "./project-library.css";
Expand Down
62 changes: 32 additions & 30 deletions apps/web/src/pages/project-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ import {
MenuTrigger,
} from "@corbits/react-ui/ui/menu";
import { useEffect, useState, type ReactElement } from "react";
import { api } from "../client.js";
import {
archiveProject,
exportProject as exportProjectArchive,
exportSections,
renameProject,
restoreProject,
shipProject,
} from "../api/projects.ts";
import { Banner, Button, type NoticeAction } from "../components.jsx";
import { Dictated } from "../dictation.jsx";

Expand All @@ -48,7 +55,7 @@ async function exportProject(
project: MenuProject,
sections: readonly ExportSection[],
) {
const { filename, archive, note } = await api.exportProject(
const { filename, archive, note } = await exportProjectArchive(
project.id,
sections,
);
Expand Down Expand Up @@ -123,11 +130,7 @@ export function ProjectMenu({
{project.archivedAt ? null : (
<MenuItem
onSelect={() =>
void act(() =>
api.updateProject(project.id, {
shipped: !project.shippedAt,
}),
)
void act(() => shipProject(project.id, !project.shippedAt))
}
>
{project.shippedAt
Expand All @@ -139,9 +142,9 @@ export function ProjectMenu({
<MenuItem
onSelect={() =>
void act(() =>
api.updateProject(project.id, {
archived: !project.archivedAt,
}),
project.archivedAt
? restoreProject(project.id)
: archiveProject(project.id),
)
}
>
Expand All @@ -155,7 +158,7 @@ export function ProjectMenu({
className="menu-danger"
onSelect={() =>
void act(async () => {
await api.deleteProject(project.id);
await archiveProject(project.id);
onDeleted?.();
})
}
Expand Down Expand Up @@ -233,13 +236,13 @@ function ExportDialog({
const [chosen, setChosen] = useState<Set<ExportSection>>(() => new Set());
useEffect(() => {
let current = true;
api.exportSections(projectId).then(
(sections) => {
async function loadSections() {
try {
const sections = await exportSections(projectId);
if (!current) return;
setAvailable(sections);
setChosen(new Set(sections));
},
(cause: unknown) => {
} catch (cause) {
if (!current) return;
const planning = new Set<ExportSection>(["planning"]);
setFailure(
Expand All @@ -249,8 +252,9 @@ function ExportDialog({
);
setAvailable(planning);
setChosen(new Set(planning));
},
);
}
}
void loadSections();
return () => {
current = false;
};
Expand Down Expand Up @@ -340,10 +344,18 @@ export function ProjectInfoDialog({
const save = async () => {
if (!dirty || saving) return;
setSaving(true);
const ok = await act(() => renameProject(project.id, nextTitle));
setSaving(false);
if (ok) onClose();
};
const toggleArchived = async () => {
setArchiving(true);
const ok = await act(() =>
api.updateProject(project.id, { title: nextTitle }),
project.archivedAt
? restoreProject(project.id)
: archiveProject(project.id),
);
setSaving(false);
setArchiving(false);
if (ok) onClose();
};

Expand Down Expand Up @@ -394,17 +406,7 @@ export function ProjectInfoDialog({
<Button
loading={archiving}
disabled={saving}
onClick={() => {
setArchiving(true);
void act(() =>
api.updateProject(project.id, {
archived: !project.archivedAt,
}),
).then((ok) => {
setArchiving(false);
if (ok) onClose();
});
}}
onClick={() => void toggleArchived()}
>
{project.archivedAt
? wording.action.unarchive
Expand Down
15 changes: 10 additions & 5 deletions apps/web/src/pages/projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@ import {
ShimmerText,
} from "@corbits/react-ui";
import { useRef, useState } from "react";
import { api, ApiFailure } from "../client.js";
import { ApiFailure } from "../api/types.ts";
import {
createProject,
importProject,
readImportFile,
} from "../api/projects.ts";
import { Banner, Button } from "../components.jsx";
import { ConnectProvider } from "./providers.jsx";
import { NewProjectProfile } from "./profile-select.jsx";
import { Dictated } from "../dictation.jsx";
import { useShellReply } from "../flourish.tsx";
import { COMPOSER_BOX_CLASS } from "./workspace/pane-classes.ts";
import { HOME_COMPOSER_PLACEHOLDER, canStartProject } from "./home-view.js";
import { HOME_COMPOSER_PLACEHOLDER, canStartProject } from "./home-view.ts";
import "./home-layout.css";
import type { ProjectImport } from "@corbits-builder/schemas/project-import";

Expand Down Expand Up @@ -53,7 +58,7 @@ export function Projects({
setBusy("start");
setError(null);
try {
const created = await api.createProject({
const created = await createProject({
problemStatement: problem.trim(),
...(profile === undefined ? {} : { profile }),
});
Expand Down Expand Up @@ -86,7 +91,7 @@ export function Projects({
);
const chooseImport = async (file: File) => {
try {
const body = await api.readImportFile(file);
const body = await readImportFile(file);
if (body.file.documents.some((document) => document.approved))
setPendingImport({ name: file.name, body });
else await importFile(file.name, body, true);
Expand All @@ -105,7 +110,7 @@ export function Projects({
const importing = toast.loading(`Importing ${name}…`);
setBusy("import");
try {
const created = await api.importProject(
const created = await importProject(
withApprovals
? file
: {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/pages/workspace.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import "./workspace-layout.css";
import "./workspace/stage-chrome.css";
export * from "./workspace/index.js";
export * from "./workspace/index.tsx";
Loading