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
File renamed without changes.
2 changes: 1 addition & 1 deletion scripts/build-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async function fixture() {
"tsconfig.json",
"tsconfig.bun.json",
"config.schema.json",
"image-resize-worker.js",
"image-resize-worker.ts",
"src/cli.ts",
"packaging/remote/tested-dependencies.json",
"src/shared/version.js",
Expand Down
2 changes: 1 addition & 1 deletion scripts/build-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const INPUTS = [
"tsconfig.json",
"tsconfig.bun.json",
"config.schema.json",
"image-resize-worker.js",
"image-resize-worker.ts",
"node_modules/@earendil-works/pi-coding-agent/dist/utils",
];
const EXCLUDED = new Set([BUILD_IDENTITY_PATH, "src/shared/version.js"]);
Expand Down
13 changes: 8 additions & 5 deletions scripts/check-bun-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { installPackageHelperFixtures } from "./package-helper-fixtures.ts";
import { verifyBuildArtifact } from "./build-metadata.ts";
import { captureScriptProcess } from "./script-process.ts";
import { removeExecutableFixtureDirectory } from "../src/testing/runtime-executable-fixture.ts";
import { SIDEBAR_MOTION_SOURCE, SIDEBAR_MOTION_URL, transpileSidebarMotionScript } from "../src/ui/workspace/server/public-workspace-assets.ts";

const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));

Expand Down Expand Up @@ -208,11 +209,13 @@ export async function checkBunPackage(
const response = await fetch(assetUrl);
equal(response.status, 200, assetUrl.pathname);
match(response.headers.get("content-type") || "", /(?:text|application)\/javascript/i);
deepEqual(
new Uint8Array(await response.arrayBuffer()),
new Uint8Array(await readFile(join(ROOT, "src/ui", assetUrl.pathname))),
assetUrl.pathname,
);
const sidebarMotion = assetUrl.pathname === SIDEBAR_MOTION_URL;
const sourcePath = sidebarMotion ? SIDEBAR_MOTION_SOURCE : `src/ui${assetUrl.pathname}`;
const source = await readFile(join(ROOT, sourcePath));
const expected = sidebarMotion
? new TextEncoder().encode(transpileSidebarMotionScript(source.toString("utf8")))
: new Uint8Array(source);
deepEqual(new Uint8Array(await response.arrayBuffer()), expected, assetUrl.pathname);
}
equal(page.headers.get("x-frame-options"), "DENY");
const assets = [...html.matchAll(/(?:src|href)="(\/_astro\/[^"?]+\.(?:js|css))"/g)].map((item) => item[1]);
Expand Down
5 changes: 4 additions & 1 deletion scripts/compile-bun.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
import { assertEquals, assertThrows } from "../src/testing/assertions.ts";
import { bunArtifactTarget, PASSIVE_PATHS } from "./compile-bun.ts";
import bunAdapter from "../src/ui/workspace/integrations/bun-adapter.ts";
import { SIDEBAR_MOTION_SOURCE, SIDEBAR_MOTION_URL } from "../src/ui/workspace/server/public-workspace-assets.ts";

test("compiled Workspace includes the unbundled shell's browser imports", async () => {
const root = fileURLToPath(new URL("../", import.meta.url));
Expand All @@ -14,7 +15,9 @@ test("compiled Workspace includes the unbundled shell's browser imports", async
assert(imports.length > 0);
for (const [, specifier] of imports) {
const browserPath = new URL(specifier, "https://workspace.test/workspace-shell.js").pathname;
const path = resolve(root, `src/ui${browserPath}`);
const sourcePath =
browserPath === SIDEBAR_MOTION_URL ? SIDEBAR_MOTION_SOURCE : `src/ui${browserPath}`;
const path = resolve(root, sourcePath);
assert((await stat(path)).isFile());
const packaged = relative(root, path).replaceAll("\\", "/");
assert(
Expand Down
4 changes: 3 additions & 1 deletion scripts/compile-bun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { requirePinnedBun } from "./check-bun-runtime.ts";
import { BUILD_METADATA_SCHEMA, computeBuildIdentity, REMOTE_PROTOCOL_VERSION, sha256File } from "./build-metadata.ts";
import { resolveBuildVersion, writeBuildIdentityFile, writeVersionFile } from "./write-version.ts";

import { SIDEBAR_MOTION_SOURCE } from "../src/ui/workspace/server/public-workspace-assets.ts";

interface LoadedSource {
contents: string;
loader: "js";
Expand Down Expand Up @@ -54,7 +56,7 @@ export const PASSIVE_PATHS = [
"src/ui/workspace/static",
"src/ui/design-system/tokens.css",
"src/ui/design-system/components.css",
"src/ui/design-system/sidebar-motion.js",
SIDEBAR_MOTION_SOURCE,
"src/ui/design-system/panel-icon.ts",
"brand/logo.svg",
"src/agent-definitions",
Expand Down
3 changes: 1 addition & 2 deletions scripts/language-policy-baseline.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
[
"src/shared/session/session.js",
"src/ui/design-system/sidebar-motion.js"
"src/shared/session/session.js"
]
2 changes: 1 addition & 1 deletion src/ui/design-system/components/react/sidebar-motion.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { flushSync } from "react-dom";
import { animateSidebarChange } from "../../sidebar-motion.js";
import { animateSidebarChange } from "../../sidebar-motion.ts";

export function animateSidebarUpdate(update: () => void) {
animateSidebarChange(() => flushSync(update));
Expand Down
10 changes: 2 additions & 8 deletions src/ui/design-system/sidebar-motion.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { assertEquals } from "../../testing/assertions.ts";
import { test } from "node:test";
import { animateSidebarChange } from "./sidebar-motion.js";

type TestTransition = {
ready: Promise<void>;
finished: Promise<void>;
skipTransition: () => void;
};
import { animateSidebarChange, type SidebarTransition } from "./sidebar-motion.ts";

type TestDocument = {
documentElement: {
Expand All @@ -16,7 +10,7 @@ type TestDocument = {
contains: (name: string) => boolean;
};
};
startViewTransition?: (update: () => void) => TestTransition;
startViewTransition?: (update: () => void) => SidebarTransition;
};

function installBrowser(reducedMotion = false) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
/** @typedef {{ finished: Promise<void>, ready: Promise<void>, skipTransition: () => void }} SidebarTransition */
export type SidebarTransition = Pick<ViewTransition, "finished" | "ready" | "skipTransition">;

/** @type {SidebarTransition | null} */
let activeTransition = null;
/** @type {Array<() => void> | null} */
let pendingUpdates = null;
let activeTransition: SidebarTransition | null = null;
type SidebarUpdate = () => void;
let pendingUpdates: SidebarUpdate[] | null = null;

/**
* Animate a sidebar layout change without retaining hidden interactive content.
* Call only for user actions; initial state and responsive layout changes stay instant.
* @param {() => void} update
*/
export function animateSidebarChange(update) {
export function animateSidebarChange(update: SidebarUpdate) {
if (
typeof document === "undefined" || !document.startViewTransition ||
typeof document === "undefined" ||
!document.startViewTransition ||
globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches
) {
update();
Expand Down
8 changes: 4 additions & 4 deletions src/ui/workspace/pages/design-system/sidebar-motion.js.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { readFile } from "node:fs/promises";
import source from "../../../design-system/sidebar-motion.ts?raw";
import { transpileSidebarMotionScript } from "../../server/public-workspace-assets.ts";

export const prerender = false;
const MOTION_URL = new URL("../../../design-system/sidebar-motion.js", import.meta.url);

export const GET = async () =>
new Response(await readFile(MOTION_URL, "utf8"), {
export const GET = () =>
new Response(transpileSidebarMotionScript(source), {
headers: { "content-type": "text/javascript; charset=utf-8" },
});
25 changes: 17 additions & 8 deletions src/ui/workspace/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,13 @@ import { ownerProjectPlanSessionsApi } from "./server/owner-plan-sessions.ts";
import { createOwnerConnectionRegistry } from "./server/owner-connections.ts";
import { setAstroOwnerWorkspaceSessionContinuation, setAstroOwnerWorkspaceStore } from "./server/astro-owner-data.ts";
import { workspacePwaResponse } from "./server/workspace-pwa.ts";
import { isPublicWorkspaceAsset, WORKSPACE_PWA_PATHS } from "./server/public-workspace-assets.ts";
import {
isPublicWorkspaceAsset,
SIDEBAR_MOTION_SOURCE,
SIDEBAR_MOTION_URL,
transpileSidebarMotionScript,
WORKSPACE_PWA_PATHS,
} from "./server/public-workspace-assets.ts";
import { reviewHost } from "./server/review-host.ts";
import { createWorkspaceSearchService } from "./server/workspace-search.ts";

Expand All @@ -141,6 +147,7 @@ const STYLES_PATH = join(WORKSPACE_DIR, "static", "styles.css");
const TOKENS_CSS_PATH = join(DESIGN_SYSTEM_DIR, "tokens.css");
const COMPONENTS_CSS_PATH = join(DESIGN_SYSTEM_DIR, "components.css");
const WORKSPACE_CSS_PATH = join(WORKSPACE_DIR, "static", "workspace.css");
const SIDEBAR_MOTION_PATH = join(ROOT_DIR, SIDEBAR_MOTION_SOURCE);
const WORKSPACE_SHELL_JS_PATH = join(WORKSPACE_DIR, "static", "workspace-shell.ts");
const LOGO_PATH = join(ROOT_DIR, "brand", "logo.svg");
const ASTRO_SOURCE_DIST_DIR = join(ROOT_DIR, "dist", versions.bun ? "workspace-bun" : "workspace");
Expand Down Expand Up @@ -1371,10 +1378,7 @@ function registerStaticRoutes<State>(app: ReturnType<typeof createWorkspaceRoute
app.get("/workspace.css", async () => await handleStaticRoute("/workspace.css"));
app.get("/workspace-shell.js", async () => await handleStaticRoute("/workspace-shell.js"));
app.get("/design-system/panel-icon.ts", async () => await handleStaticRoute("/design-system/panel-icon.ts"));
app.get(
"/design-system/sidebar-motion.js",
async () => await handleStaticRoute("/design-system/sidebar-motion.js"),
);
app.get(SIDEBAR_MOTION_URL, async () => await handleStaticRoute(SIDEBAR_MOTION_URL));
app.get("/theme.css", async () => await handleStaticRoute("/theme.css"));
app.get("/brand/logo.svg", async () => await handleStaticRoute("/brand/logo.svg"));
app.get("/_astro/:asset", async (ctx) => await handleStaticRoute(ctx.url.pathname));
Expand All @@ -1384,8 +1388,8 @@ async function handleStaticRoute(pathname: string) {
if (pathname === "/design-system/panel-icon.ts") {
return await textFileResponse(join(DESIGN_SYSTEM_DIR, "panel-icon.ts"), "text/javascript; charset=utf-8");
}
if (pathname === "/design-system/sidebar-motion.js") {
return await textFileResponse(join(DESIGN_SYSTEM_DIR, "sidebar-motion.js"), "text/javascript; charset=utf-8");
if (pathname === SIDEBAR_MOTION_URL) {
return await textFileResponse(SIDEBAR_MOTION_PATH, "text/javascript; charset=utf-8");
}
if (pathname === "/styles.css") return await textFileResponse(STYLES_PATH, "text/css; charset=utf-8");
if (pathname === "/tokens.css") return await textFileResponse(TOKENS_CSS_PATH, "text/css; charset=utf-8");
Expand Down Expand Up @@ -1461,7 +1465,12 @@ function contentTypeForAsset(path: string) {

async function textFileResponse(path: string, contentType: string) {
try {
const body = path === WORKSPACE_CSS_PATH ? await readWorkspaceStyles(path) : await readFile(path, "utf8");
const body =
path === SIDEBAR_MOTION_PATH
? transpileSidebarMotionScript(await readFile(path, "utf8"))
: path === WORKSPACE_CSS_PATH
? await readWorkspaceStyles(path)
: await readFile(path, "utf8");
return new Response(body, { headers: { "content-type": contentType } });
} catch {
return new Response("Not found", { status: 404 });
Expand Down
10 changes: 9 additions & 1 deletion src/ui/workspace/server/public-workspace-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export const WORKSPACE_PWA_PATHS = [
"/pwa/apple-touch-icon.png",
];

export const SIDEBAR_MOTION_URL = "/design-system/sidebar-motion.js";
export const SIDEBAR_MOTION_SOURCE = "src/ui/design-system/sidebar-motion.ts";

export function isPublicWorkspaceAsset(pathname: string): boolean {
return (
WORKSPACE_PWA_PATHS.includes(pathname) ||
Expand All @@ -18,10 +21,15 @@ export function isPublicWorkspaceAsset(pathname: string): boolean {
pathname === "/components.css" ||
pathname === "/workspace.css" ||
pathname === "/workspace-shell.js" ||
pathname === "/design-system/sidebar-motion.js" ||
pathname === SIDEBAR_MOTION_URL ||
pathname === "/design-system/panel-icon.ts" ||
pathname === "/theme.css" ||
pathname === "/brand/logo.svg" ||
pathname.startsWith("/_astro/")
);
}

/** Serve the typed sidebar module at the browser's JavaScript import URL. */
export function transpileSidebarMotionScript(source: string): string {
return new Bun.Transpiler({ loader: "ts", target: "browser" }).transformSync(source);
}
1 change: 1 addition & 0 deletions src/ui/workspace/static/workspace-shell.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-nocheck -- Served as plain browser JavaScript from the owner Workspace static route.
import { PANEL_ICON_PATHS } from "../../design-system/panel-icon.ts";
// This specifier resolves to the JavaScript asset served to browsers.
import { animateSidebarChange } from "../../design-system/sidebar-motion.js";
export const LAST_SESSION_KEY = "runwield:owner:last-session";
export const LAST_PROJECT_KEY = "runwield:owner:last-project";
Expand Down
3 changes: 2 additions & 1 deletion src/ui/workspace/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
},
"types": [
"astro/client",
"vite/client"
"vite/client",
"bun"
]
},
"include": [
Expand Down
4 changes: 3 additions & 1 deletion src/ui/workspace/workspace-local-server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ test("Workspace wrapper protects page routes and serves public assets without to
const sidebarMotion = await app(new Request("http://localhost/design-system/sidebar-motion.js"));
assertEquals(sidebarMotion.status, 200);
assertEquals(sidebarMotion.headers.get("content-type"), "text/javascript; charset=utf-8");
assertStringIncludes(await sidebarMotion.text(), "export function animateSidebarChange");
const sidebarScript = await sidebarMotion.text();
assertStringIncludes(sidebarScript, "export function animateSidebarChange");
assertEquals(new Bun.Transpiler({ loader: "js" }).scan(sidebarScript).exports, ["animateSidebarChange"]);
const panelIcon = await app(new Request("http://localhost/design-system/panel-icon.ts"));
assertEquals(panelIcon.status, 200);
assertEquals(panelIcon.headers.get("content-type"), "text/javascript; charset=utf-8");
Expand Down
File renamed without changes.
Loading