Skip to content
Closed
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
282 changes: 38 additions & 244 deletions bun.lock

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,30 @@
},
"license": "CC-BY-4.0",
"dependencies": {
"@pathscale/ui": "^1.2.11",
"@solidjs/router": "^0.15.3",
"@pathscale/ui": "2.6.2",
"@solidjs/router": "2.0.0-next.16",
"@standard-schema/spec": "^1.0.0",
"clsx": "^2.1.1",
"glob": "^11.0.3",
"popmotion": "^11.0.5",
"promptsyntax": "^0.1.0",
"solid-js": "^1.9.5",
"solid-js": "2.0.0-rc.0",
"solid-layouts": "0.2.0",
"tailwind-merge": "^3.6.0"
},
"devDependencies": {
"@biomejs/biome": "^2.4.15",
"@iconify/tailwind4": "^1.0.6",
"@rsbuild/core": "^1.3.20",
"@rsbuild/plugin-babel": "^1.0.5",
"@rsbuild/plugin-solid": "^1.0.5",
"@tailwindcss/postcss": "^4.1.7",
"@types/node": "^22.15.17",
"babel-preset-solid": "2.0.0-rc.0",
"class-variance-authority": "^0.7.1",
"compression-webpack-plugin": "^11.1.0",
"cssnano": "^7.0.7",
"fork-ts-checker-webpack-plugin": "^9.1.0",
"rsbuild-plugin-solid-layouts": "0.2.1",
"tailwindcss": "^4.0.7",
"typescript": "^5.7.2"
}
Expand Down
49 changes: 47 additions & 2 deletions rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,42 @@
import { defineConfig } from "@rsbuild/core";
import { pluginBabel } from "@rsbuild/plugin-babel";
import { pluginSolid } from "@rsbuild/plugin-solid";
import CompressionPlugin from "compression-webpack-plugin";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import { pluginSolid2LayoutsApplication } from "rsbuild-plugin-solid-layouts";

const isProd = process.env.NODE_ENV === "production";

export default defineConfig({
plugins: [
// `@rsbuild/plugin-solid` is deliberately not used: it pins
// `babel-preset-solid@^1.9.12`, the Solid 1 compiler, which emits helpers
// (`use`) and a runtime specifier (`solid-js/web`) that Solid 2 dropped.
// Driving the Solid 2 preset through Babel directly compiles JSX against
// the runtime that actually exists.
pluginBabel({
include: /\.(?:jsx|tsx|ts)$/,
babelLoaderOptions: (config) => {
config.presets ??= [];
config.presets.push(["babel-preset-solid", {}]);
},
}),
// Compiles `@pathscale/ui`'s layout recipes and resolves `solid-layouts`
// to its Solid 2 backend, which lives behind a `/solid-2` subpath while
// the default entry stays on Solid 1.
pluginSolid2LayoutsApplication({
layouts: ["@pathscale/ui"],
}),
pluginSolid(),
],
resolve: {
alias: {
"~": "./src",
// Belt and braces alongside the plugin above: `solid-layouts`'s default
// entry is the Solid 1 build, whose renderer imports `solid-js/web`.
// Pin every specifier to the Solid 2 backend so nothing reaches it.
"solid-layouts/recipe": "solid-layouts/solid-2/recipe",
"solid-layouts/cx": "solid-layouts/solid-2/cx",
"solid-layouts/application-boundary": "solid-layouts/solid-2/application-boundary",
"solid-layouts$": "solid-layouts/solid-2",
},
},
source: {
Expand Down Expand Up @@ -49,8 +70,32 @@ export default defineConfig({
port: 3000,
},
tools: {
// rsbuild's own SWC pass also transforms JSX, and it defaults to Solid 1's
// `solid-js/web`. Babel has already produced the Solid 2 output by then, so
// point SWC at the same runtime rather than letting it re-emit the old one.
swc: {
jsc: {
transform: {
react: {
runtime: "automatic",
importSource: "@solidjs/web",
},
},
},
},
rspack: {
// `solid-layouts` supports both Solid majors from one file by feature
// detecting on a namespace import (`"omit" in solid ? solid.omit :
// solid.splitProps`). Only one branch can resolve on a given major, and
// the dead one is never evaluated, but the bundler still checks both and
// hard-errors on the missing export. Downgrade that to a warning.
ignoreWarnings: [/export '(splitProps|omit)' .* was not found in 'solid-js'/],
module: {
parser: {
javascript: {
exportsPresence: "warn",
},
},
rules: [
{
resourceQuery: /raw/,
Expand Down
29 changes: 17 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Route, Router } from "@solidjs/router";
import { createRouter } from "@solidjs/router";
import type { ParentComponent } from "solid-js";

import Footer from "~/components/Footer";
Expand All @@ -17,16 +17,21 @@ const Shell: ParentComponent = (props) => (
</div>
);

const App = () => {
return (
<Router root={Shell}>
<Route path={ROUTES.HOME} component={HomePage} />
<Route path={ROUTES.SPEC} component={SpecPage} />
<Route path={ROUTES.SYNTAX} component={SyntaxPage} />
<Route path={ROUTES.VIGNETTE} component={VignettePage} />
<Route path="*" component={HomePage} />
</Router>
);
};
const Router = createRouter({
routes: [
{
component: Shell,
children: [
{ path: ROUTES.HOME, component: HomePage },
{ path: ROUTES.SPEC, component: SpecPage },
{ path: ROUTES.SYNTAX, component: SyntaxPage },
{ path: ROUTES.VIGNETTE, component: VignettePage },
{ path: "*", component: HomePage },
],
},
],
});

const App = () => <Router />;

export default App;
6 changes: 3 additions & 3 deletions src/ThemeToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ export const ThemeToggle: Component = () => {
type="button"
variant="ghost"
size="sm"
isIconOnly
width="square"
onClick={toggleTheme}
aria-label={label()}
title={label()}
>
{theme() === "light" ? (
<Icon name="icon-[lucide--moon]" width={16} height={16} />
<Icon src="icon-[lucide--moon]" width={16} height={16} />
) : (
<Icon name="icon-[lucide--sun]" width={16} height={16} />
<Icon src="icon-[lucide--sun]" width={16} height={16} />
)}
</Button>
);
Expand Down
9 changes: 4 additions & 5 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Footer as LibFooter, Link } from "@pathscale/ui";
import { A } from "@solidjs/router";
import type { Component } from "solid-js";
import { CONTACT_EMAIL, GITHUB_URL, ROUTES } from "~/config/routes";

Expand All @@ -10,12 +9,12 @@ export const Footer: Component = () => (
PromptSyntax · a vendor-neutral specification proposal · Draft v0.2.1 · CC BY 4.0 (proposed)
</div>
<div class="flex flex-wrap items-center gap-x-5 gap-y-2">
<A class="text-base-content/55 text-xs hover:text-base-content" href={ROUTES.SPEC}>
<a class="text-base-content/55 text-xs hover:text-base-content" href={ROUTES.SPEC}>
Spec
</A>
<A class="text-base-content/55 text-xs hover:text-base-content" href={ROUTES.SYNTAX}>
</a>
<a class="text-base-content/55 text-xs hover:text-base-content" href={ROUTES.SYNTAX}>
Syntax reference
</A>
</a>
<Link
class="text-base-content/55 text-xs"
href={GITHUB_URL}
Expand Down
14 changes: 7 additions & 7 deletions src/components/SiteNavbar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Navbar } from "@pathscale/ui";
import { A, useLocation } from "@solidjs/router";
import { useLocation } from "@solidjs/router";
import clsx from "clsx";
import type { Component } from "solid-js";
import Logo from "~/components/Logo";
Expand All @@ -23,16 +23,16 @@ const SiteNavbar: Component = () => {
<Navbar.Row bordered class="site-nav" padded={false}>
<div class="page-container flex min-h-14 items-center justify-between gap-4">
<Navbar.Start>
<A href={ROUTES.HOME} class="mr-4 no-underline">
<a href={ROUTES.HOME} class="mr-4 no-underline">
<Logo class="text-base" />
</A>
</a>
<nav class="hidden items-center gap-1 sm:flex">
<A href={ROUTES.SPEC} class={navLinkClass(ROUTES.SPEC)}>
<a href={ROUTES.SPEC} class={navLinkClass(ROUTES.SPEC)}>
Specification
</A>
<A href={ROUTES.SYNTAX} class={navLinkClass(ROUTES.SYNTAX)}>
</a>
<a href={ROUTES.SYNTAX} class={navLinkClass(ROUTES.SYNTAX)}>
Syntax reference
</A>
</a>
</nav>
</Navbar.Start>
<Navbar.End>
Expand Down
3 changes: 1 addition & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* @refresh reload */
import "./index.css";
import { render } from "solid-js/web";
import { render } from "@solidjs/web";

import App from "./App";

Expand Down
13 changes: 8 additions & 5 deletions src/lib/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ if (typeof window !== "undefined") {
document.documentElement.setAttribute("data-theme", initialTheme);
}

createEffect(() => {
const current = theme();
if (typeof window !== "undefined") {
// Solid 2 splits `createEffect` in two: the first function tracks and returns a
// value, the second receives it and does the side effect.
createEffect(
() => theme(),
(current) => {
if (typeof window === "undefined") return;
document.documentElement.setAttribute("data-theme", current);
localStorage.setItem("theme", current);
}
});
},
);

export { setTheme, theme };
43 changes: 24 additions & 19 deletions src/pages/DocPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Component, createSignal, For, onMount } from "solid-js";
import { type Component, createEffect, createSignal, For } from "solid-js";

type TocItem = { id: string; text: string; level: number };

Expand All @@ -12,25 +12,30 @@ const DocPage: Component<DocPageProps> = (props) => {
let contentRef: HTMLDivElement | undefined;
const [toc, setToc] = createSignal<TocItem[]>([]);

onMount(() => {
if (!contentRef) return;
const items: TocItem[] = [];
for (const heading of contentRef.querySelectorAll("h2[id], h3[id]")) {
items.push({
id: heading.id,
text: heading.textContent?.replace(/\s+/g, " ").trim() ?? "",
level: heading.tagName === "H2" ? 2 : 3,
});
}
setToc(items);
// Runs once the content div is attached. Solid 2 replaces `onMount` with the
// two-argument `createEffect`: the first function tracks, the second acts.
createEffect(
() => props.html,
() => {
if (!contentRef) return;
const items: TocItem[] = [];
for (const heading of contentRef.querySelectorAll("h2[id], h3[id]")) {
items.push({
id: heading.id,
text: heading.textContent?.replace(/\s+/g, " ").trim() ?? "",
level: heading.tagName === "H2" ? 2 : 3,
});
}
setToc(items);

if (window.location.hash) {
const id = window.location.hash.slice(1);
scrollToHeading(id, "instant");
// Re-anchor once the full document has laid out (large tables shift heights).
setTimeout(() => scrollToHeading(id, "instant"), 300);
}
});
if (window.location.hash) {
const id = window.location.hash.slice(1);
scrollToHeading(id, "instant");
// Re-anchor once the full document has laid out (large tables shift heights).
setTimeout(() => scrollToHeading(id, "instant"), 300);
}
},
);

// Leave `behavior` to CSS (html { scroll-behavior: smooth }) unless overridden.
const scrollToHeading = (id: string, behavior?: ScrollBehavior) => {
Expand Down
13 changes: 6 additions & 7 deletions src/pages/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Button } from "@pathscale/ui";
import { A } from "@solidjs/router";
import type { Component } from "solid-js";
import LensTabs from "~/components/LensTabs";
import { GITHUB_URL, ROUTES } from "~/config/routes";
Expand All @@ -20,11 +19,11 @@ const HomePage: Component = () => (
PromptSyntax is a vendor-neutral proposal to fix that, at the layer where you type.
</p>
<div class="flex flex-wrap gap-3">
<A href={ROUTES.SPEC}>
<Button variant="primary" type="button">
<a href={ROUTES.SPEC}>
<Button variant="solid" flavor="primary" type="button">
Read the specification
</Button>
</A>
</a>
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer">
<Button variant="outline" type="button">
View on GitHub
Expand Down Expand Up @@ -174,11 +173,11 @@ const HomePage: Component = () => (
</li>
</ul>
<p class="mt-7">
<A href={ROUTES.SPEC}>
<Button variant="primary" type="button">
<a href={ROUTES.SPEC}>
<Button variant="solid" flavor="primary" type="button">
Read the full specification →
</Button>
</A>
</a>
</p>
</div>
</section>
Expand Down
7 changes: 4 additions & 3 deletions src/pages/VignettePage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Button } from "@pathscale/ui";
import { createMemo, createSignal, For, type JSX, Show } from "solid-js";
import type { JSX } from "@solidjs/web";
import { createMemo, createSignal, For, Show } from "solid-js";
import { compileVignette, MINI_MODEL, PRECISE_MODEL, VIGNETTE_PARSER } from "~/lib/vignette";

const INITIAL_PROMPT = "Summarize the attached Q3 report. Keep it concise.";
Expand Down Expand Up @@ -210,7 +211,7 @@ function VignettePage(): JSX.Element {
</section>

<div class="vignette-actions">
<Button type="button" variant="primary" onClick={check}>
<Button type="button" variant="solid" flavor="primary" onClick={check}>
Check my answer
</Button>
<Show when={attempts() >= 3 && !compiled().passed && result() === null}>
Expand Down Expand Up @@ -238,7 +239,7 @@ function VignettePage(): JSX.Element {
<div>
<input
id="completion-code"
readOnly
readonly
value={completionCode(payload())}
onFocus={(event) => event.currentTarget.select()}
/>
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsxImportSource": "@solidjs/web",
"types": [],
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
Expand Down
Loading