Skip to content

Commit 5bfa92b

Browse files
aster-voidclaude
andcommitted
treewide: fix article preview and page freeze bugs
- Fix Markdown component using $derived() instead of $derived.by() - Add @tailwindcss/typography for prose styling - Remove all $effect usage (caused infinite loops) - Move view count increment to server-side 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent d12149e commit 5bfa92b

File tree

9 files changed

+37
-47
lines changed

9 files changed

+37
-47
lines changed

bun.lock

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"@sveltejs/adapter-auto": "^7.0.0",
2121
"@sveltejs/kit": "^2.49.1",
2222
"@sveltejs/vite-plugin-svelte": "^6.2.1",
23+
"@tailwindcss/typography": "^0.5.19",
2324
"@tailwindcss/vite": "^4.1.17",
2425
"@types/marked": "^6.0.0",
2526
"@types/node": "^22",
@@ -288,6 +289,8 @@
288289

289290
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="],
290291

292+
"@tailwindcss/typography": ["@tailwindcss/[email protected]", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="],
293+
291294
"@tailwindcss/vite": ["@tailwindcss/[email protected]", "", { "dependencies": { "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA=="],
292295

293296
"@types/cookie": ["@types/[email protected]", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
@@ -692,7 +695,7 @@
692695

693696
"postcss-scss": ["[email protected]", "", { "peerDependencies": { "postcss": "^8.4.29" } }, "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A=="],
694697

695-
"postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
698+
"postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="],
696699

697700
"prelude-ls": ["[email protected]", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
698701

@@ -882,6 +885,8 @@
882885

883886
"postcss-load-config/yaml": ["[email protected]", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
884887

888+
"svelte-eslint-parser/postcss-selector-parser": ["[email protected]", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
889+
885890
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
886891

887892
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"@sveltejs/adapter-auto": "^7.0.0",
3131
"@sveltejs/kit": "^2.49.1",
3232
"@sveltejs/vite-plugin-svelte": "^6.2.1",
33+
"@tailwindcss/typography": "^0.5.19",
3334
"@tailwindcss/vite": "^4.1.17",
3435
"@types/marked": "^6.0.0",
3536
"@types/node": "^22",

src/app.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import "tailwindcss";
2+
@plugin "@tailwindcss/typography";
23
@plugin "daisyui" {
34
logs: false;
45
}

src/lib/components/Markdown.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
99
const { content }: Props = $props();
1010
11-
const html = $derived(() => {
11+
const html = $derived.by(() => {
1212
const rawHtml = marked.parse(content, { async: false });
1313
return DOMPurify.sanitize(rawHtml);
1414
});
1515
</script>
1616

17-
<div class="prose-zinc prose max-w-none">
17+
<div class="prose max-w-none prose-zinc">
1818
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
1919
{@html html}
2020
</div>

src/lib/components/admin-search-modal.svelte

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,17 @@
2525
import type { AdminSearchResult } from "$lib/shared/logic/search";
2626
2727
let query = $state("");
28-
let inputRef: HTMLInputElement | null = $state(null);
2928
let selectedIndex = $state(0);
30-
let prevQuery = $state("");
3129
3230
const results = $derived(query.trim() ? await searchAdmin(query) : []);
3331
34-
$effect(() => {
35-
if (modalState.open && inputRef) {
36-
inputRef.focus();
37-
}
38-
});
39-
40-
$effect(() => {
41-
if (query !== prevQuery) {
42-
prevQuery = query;
32+
function handleQueryInput(e: Event & { currentTarget: HTMLInputElement }) {
33+
const newQuery = e.currentTarget.value;
34+
if (newQuery !== query) {
35+
query = newQuery;
4336
selectedIndex = 0;
4437
}
45-
});
38+
}
4639
4740
function handleGlobalKeydown(e: KeyboardEvent) {
4841
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
@@ -160,8 +153,8 @@
160153
<div class="flex items-center gap-3 border-b border-base-300 px-4">
161154
<Search class="h-5 w-5 shrink-0 text-base-content/40" />
162155
<input
163-
bind:this={inputRef}
164-
bind:value={query}
156+
value={query}
157+
oninput={handleQueryInput}
165158
type="text"
166159
placeholder="Search articles, projects, members..."
167160
class="h-14 flex-1 bg-transparent text-base outline-none placeholder:text-base-content/40"

src/lib/components/site-search-modal.svelte

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,17 @@
2525
import type { SearchResult } from "$lib/shared/logic/search";
2626
2727
let query = $state("");
28-
let inputRef: HTMLInputElement | null = $state(null);
2928
let selectedIndex = $state(0);
30-
let prevQuery = $state("");
3129
3230
const results = $derived(query.trim() ? await searchPublic(query) : []);
3331
34-
$effect(() => {
35-
if (modalState.open && inputRef) {
36-
inputRef.focus();
37-
}
38-
});
39-
40-
$effect(() => {
41-
if (query !== prevQuery) {
42-
prevQuery = query;
32+
function handleQueryInput(e: Event & { currentTarget: HTMLInputElement }) {
33+
const newQuery = e.currentTarget.value;
34+
if (newQuery !== query) {
35+
query = newQuery;
4336
selectedIndex = 0;
4437
}
45-
});
38+
}
4639
4740
function handleGlobalKeydown(e: KeyboardEvent) {
4841
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
@@ -163,8 +156,8 @@
163156
<div class="flex items-center gap-3 border-b border-zinc-200 px-4">
164157
<Search class="h-5 w-5 shrink-0 text-zinc-400" />
165158
<input
166-
bind:this={inputRef}
167-
bind:value={query}
159+
value={query}
160+
oninput={handleQueryInput}
168161
type="text"
169162
placeholder="記事やプロジェクトを検索..."
170163
class="h-14 flex-1 bg-transparent text-base outline-none placeholder:text-zinc-400"

src/lib/data/public/index.remote.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import * as v from "valibot";
2-
import { query, command } from "$app/server";
2+
import { query } from "$app/server";
33
import {
44
listPublishedArticles,
55
getPublishedArticle,
6-
incrementViewCount,
76
getRelatedArticles,
87
searchPublishedArticles,
98
} from "$lib/server/database/articles.server";
@@ -17,7 +16,6 @@ import type { SearchResult } from "$lib/shared/logic/search";
1716

1817
export const getPublicArticles = query(listPublishedArticles);
1918
export const getPublicArticle = query(v.string(), getPublishedArticle);
20-
export const incrementArticleViewCount = command(v.string(), incrementViewCount);
2119
export const getPublicRelatedArticles = query(
2220
v.object({
2321
articleId: v.string(),

src/lib/server/database/articles.server.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,20 @@ export async function getArticleBySlug(slug: string) {
2222
}
2323

2424
export async function getPublishedArticle(slug: string) {
25-
return db.query.article.findFirst({
25+
const result = await db.query.article.findFirst({
2626
where: and(eq(article.slug, slug), eq(article.published, true)),
2727
with: { author: true },
2828
});
29+
30+
// Increment view count (fire-and-forget)
31+
if (result) {
32+
db.update(article)
33+
.set({ viewCount: sql`${article.viewCount} + 1` })
34+
.where(eq(article.slug, slug))
35+
.run();
36+
}
37+
38+
return result;
2939
}
3040

3141
export async function listAllArticles() {

src/routes/(site)/articles/[slug]/+page.svelte

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
import { page } from "$app/state";
33
import Markdown from "$lib/components/Markdown.svelte";
44
import { Eye } from "lucide-svelte";
5-
import {
6-
getPublicArticle,
7-
getPublicRelatedArticles,
8-
incrementArticleViewCount,
9-
} from "$lib/data/public/index.remote";
5+
import { getPublicArticle, getPublicRelatedArticles } from "$lib/data/public/index.remote";
106
117
const slug = $derived(page.params.slug ?? "");
128
const article = $derived(await getPublicArticle(slug));
@@ -19,13 +15,6 @@
1915
})
2016
: [],
2117
);
22-
23-
// Increment view count when article is loaded
24-
$effect(() => {
25-
if (article) {
26-
incrementArticleViewCount(slug);
27-
}
28-
});
2918
</script>
3019

3120
<svelte:head>

0 commit comments

Comments
 (0)