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
8 changes: 4 additions & 4 deletions examples/chat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fact about the thread, not something left hanging.

```tsx
const getThread = liveQuery(
live(async (id: string) => {
async (id: string) => {
"use server";
return (props: { composer: ComposerSlot; copyCode: CopySlot }) => {
const view = createMemo(() => db.watchThread(id, gone.signal));
Expand All @@ -95,13 +95,13 @@ const getThread = liveQuery(
</article>
);
};
}),
},
"thread"
);
```

`live` holds the response open and reconnects it if it dies. `liveQuery`
gives it a key and one connection per thread.
`liveQuery` declares the function `live`, which holds the response open and
reconnects it if it dies, and gives it a key and one connection per thread.

Each `{…}` that reads `view()` is a piece of the page the server can send
again by itself, and it sends one only when its HTML has changed. The thread
Expand Down
2 changes: 1 addition & 1 deletion examples/chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"dependencies": {
"@anthropic-ai/sdk": "^0.133.0",
"@solidjs/router": "2.0.0-next.41",
"@solidjs/router": "2.0.0-next.42",
"@solidjs/web": "workspace:*",
"highlight.js": "^11.11.1",
"marked": "^12.0.2",
Expand Down
54 changes: 25 additions & 29 deletions examples/chat/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// it.
import { createMemo, onCleanup } from "solid-js";
import { getRequestEvent } from "@solidjs/web";
import { live } from "@solidjs/web/server-functions";
import { createRouter, defineRoute, liveQuery } from "@solidjs/router";
import Home from "~/routes/home";
import Thread from "~/routes/thread";
Expand All @@ -16,34 +15,31 @@ import * as db from "~/server/db";
// changes without this tab doing anything — another tab sends a message, a
// reply finishes, a thread is renamed or deleted. The router marks the link
// to the page you are on.
export const getThreads = liveQuery(
live(async () => {
"use server";
return () => {
const gone = new AbortController();
onCleanup(() => gone.abort());
getRequestEvent()?.request.signal.addEventListener("abort", () => gone.abort(), {
once: true
});
const threads = createMemo(() => db.watchThreads(gone.signal));
return (
<ul class="threads">
{threads().map(thread => {
const writing = thread.generating ? "writing" : undefined;
return (
<li>
<a href={`/t/${thread.id}`} class={writing}>
{thread.title}
</a>
</li>
);
})}
</ul>
);
};
}),
"threads"
);
export const getThreads = liveQuery(async () => {
"use server";
return () => {
const gone = new AbortController();
onCleanup(() => gone.abort());
getRequestEvent()?.request.signal.addEventListener("abort", () => gone.abort(), {
once: true
});
const threads = createMemo(() => db.watchThreads(gone.signal));
return (
<ul class="threads">
{threads().map(thread => {
const writing = thread.generating ? "writing" : undefined;
return (
<li>
<a href={`/t/${thread.id}`} class={writing}>
{thread.title}
</a>
</li>
);
})}
</ul>
);
};
}, "threads");

export const Router = createRouter({
routes: [
Expand Down
83 changes: 40 additions & 43 deletions examples/chat/src/routes/thread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
// code block's Copy button, a client component in a slot in that block.
import { createMemo, For, Loading, onCleanup } from "solid-js";
import { dynamicComponent, getRequestEvent, redirect } from "@solidjs/web";
import { live } from "@solidjs/web/server-functions";
import type { Slot } from "@solidjs/web/frames";
import { formAction, liveQuery, type RouteSectionProps } from "@solidjs/router";
import Composer from "~/components/composer";
Expand Down Expand Up @@ -65,53 +64,51 @@ const deleteThread = formAction(async (id: string) => {
return redirect("/");
}, "deleteThread");

// `live` holds the response open and reconnects it if it dies; `liveQuery`
// gives it a key and one connection per thread, and tells the router that
// this address is kept fresh by its own stream.
const getThread = liveQuery(
live(async (id: string) => {
"use server";
return (props: { composer: ComposerSlot; copyCode: CopySlot }) => {
// This render lives as long as its response. When the tab goes away
// the watcher below is told at once.
const gone = new AbortController();
onCleanup(() => gone.abort());
getRequestEvent()?.request.signal.addEventListener("abort", () => gone.abort(), {
once: true
});
// `liveQuery` declares it `live`, which holds the response open and
// reconnects it if it dies; it also gives it a key and one connection per
// thread, and tells the router that this address is kept fresh by its own
// stream.
const getThread = liveQuery(async (id: string) => {
"use server";
return (props: { composer: ComposerSlot; copyCode: CopySlot }) => {
// This render lives as long as its response. When the tab goes away
// the watcher below is told at once.
const gone = new AbortController();
onCleanup(() => gone.abort());
getRequestEvent()?.request.signal.addEventListener("abort", () => gone.abort(), {
once: true
});

// The thread as it is stored, again after every write to it.
const view = createMemo(() => db.watchThread(id, gone.signal));
// The thread as it is stored, again after every write to it.
const view = createMemo(() => db.watchThread(id, gone.signal));

return (
<article class="thread">
<header class="thread-head">
{headMarkup(view().thread)}
<p class="muted">Answered by {model.modelName()}</p>
</header>
<ol class="messages">
{/* A row the server has already rendered is kept while its
return (
<article class="thread">
<header class="thread-head">
{headMarkup(view().thread)}
<p class="muted">Answered by {model.modelName()}</p>
</header>
<ol class="messages">
{/* A row the server has already rendered is kept while its
message is unchanged, so a streaming reply doesn't render
the whole history again on every write (#4056). */}
<For each={view().messages.filter(m => m.status !== "generating")}>
{message => messageMarkup(message, props.copyCode)}
</For>
{messagesMarkup(view().messages, true, props.copyCode)}
</ol>
{/* Keyed by thread: going to another conversation is a new
<For each={view().messages.filter(m => m.status !== "generating")}>
{message => messageMarkup(message, props.copyCode)}
</For>
{messagesMarkup(view().messages, true, props.copyCode)}
</ol>
{/* Keyed by thread: going to another conversation is a new
composer, so a half-typed message stays with its thread. */}
<props.composer
$key={id}
threadId={id}
seen={view().messages.map(m => m.id)}
busy={view().messages.some(m => m.status === "generating")}
/>
</article>
);
};
}),
"thread"
);
<props.composer
$key={id}
threadId={id}
seen={view().messages.map(m => m.id)}
busy={view().messages.some(m => m.status === "generating")}
/>
</article>
);
};
}, "thread");

// The title is its own editor: a form around a field, so renaming is typing
// and pressing Enter, with or without JavaScript.
Expand Down
6 changes: 4 additions & 2 deletions examples/chat/src/server/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,16 @@ This thread is one. It renders on the server, from the messages the server has s

\`\`\`jsx
const getThread = liveQuery(
live(async (id) => {
async (id) => {
"use server";
return props => <Thread id={id} composer={props.composer} />;
}),
},
"thread"
);
\`\`\`

\`liveQuery\` declares it \`live\`, so the response stays open and the server sends the parts of the thread that change.

The markdown parser and the syntax highlighter that produced this reply never reach the browser. Only what they produce does.`
}
],
Expand Down
2 changes: 1 addition & 1 deletion examples/hackernews-spa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@solidjs/router": "2.0.0-next.41",
"@solidjs/router": "2.0.0-next.42",
"@solidjs/web": "workspace:*",
"solid-js": "workspace:*"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/hackernews/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@solidjs/router": "2.0.0-next.41",
"@solidjs/router": "2.0.0-next.42",
"@solidjs/web": "workspace:*",
"solid-js": "workspace:*"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/notes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@solidjs/router": "2.0.0-next.41",
"@solidjs/router": "2.0.0-next.42",
"@solidjs/web": "workspace:*",
"date-fns": "^3.6.0",
"marked": "^12.0.2",
Expand Down
2 changes: 1 addition & 1 deletion examples/server-dbmon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@solidjs/router": "2.0.0-next.41",
"@solidjs/router": "2.0.0-next.42",
"@solidjs/web": "workspace:*",
"solid-js": "workspace:*"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/todos-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@solidjs/router": "2.0.0-next.41",
"@solidjs/router": "2.0.0-next.42",
"@solidjs/web": "workspace:*",
"solid-js": "workspace:*",
"unstorage": "^1.17.5"
Expand Down
30 changes: 15 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading