Conversation
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||||
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
kotetsu | 0ee77bc | Jan 06 2026, 01:27 PM |
📝 WalkthroughWalkthroughThis PR splits CI into path-filtered jobs, moves bibliography content under Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
🧰 Additional context used🧬 Code graph analysis (1)web/src/lib/parsers/yaml.ts (1)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
🤖 Fix all issues with AI Agents
In @README.md:
- Line 11: Update the README text that currently reads "Start local sever" to
the correct phrase "Start local server" (locate and replace the string "Start
local sever" in the README.md content).
In @web/package.json:
- Line 11: The package.json "prepare" script currently appends "|| echo ''"
which silently swallows failures from "svelte-kit sync"; remove the fallback so
failures surface during install/CI (i.e., change the "prepare" script to run
"svelte-kit sync" only) or, if you need to tolerate specific known errors,
replace the silent fallback with explicit handling and a clear comment
explaining why (refer to the "prepare" script entry in package.json).
In @web/scripts/generate-data.ts:
- Around line 16-44: The interfaces BibEntry, CustomInfo, CustomInfoYaml and
BibliographyItem are duplicated here; remove these local definitions and import
the same types from the central types module instead. Replace the local
interface declarations with a single import that brings in BibEntry, CustomInfo,
CustomInfoYaml and BibliographyItem, then update any references to use those
imported types (e.g., BibliographyItem in functions or variables) so the file
relies on the single source-of-truth types.
- Around line 92-110: The parseCustomInfo function in generate-data.ts
duplicates logic that exists in the YAML parser module; remove this local
implementation and import and use the shared parser instead. Replace the local
parseCustomInfo with an import of the canonical parser function from the YAML
parser module and call that function (preserving the same signature and return
type), ensuring any used helpers like parseYaml are referenced from the imported
module or removed if redundant. Update references in this file to use the
imported parseCustomInfo symbol so there is a single source of truth for YAML
parsing.
- Around line 112-121: Remove the local mergeBibliography implementation and
import the shared merge function from web/src/lib/merge.ts instead; replace the
function declaration for mergeBibliography with an import (e.g., import {
mergeBibliography } from "web/src/lib/merge") and ensure any types (BibEntry,
CustomInfo, BibliographyItem) referenced by callers match the exported types
from the shared module, updating the import list at the top of the file and
deleting the duplicated function body.
- Around line 46-90: The file duplicates the BibTeX parsing logic (functions
parseBibTeX and parseFields); remove these local function definitions and
instead import the shared parser (exporting parseBibTeX and parseFields and any
needed types like BibEntry) from the central parser module, update usages to use
the imported parseBibTeX, and ensure the import is added at the top and types
are referenced correctly so there is no duplicate implementation to maintain.
In @web/src/lib/parsers/yaml.ts:
- Around line 12-32: parseCustomInfo currently calls parse(...) with a type
assertion to CustomInfoYaml and no try/catch or runtime checks; wrap the call to
parse in a try/catch to handle malformed YAML (return an empty Map and log the
error), then perform runtime validation of the parsed value before using it:
ensure parsed is a plain object, iterate Object.entries(parsed) only when each
value is an object, check that sites[siteId] exists and that its tags and review
fields are the expected types (e.g., tags is an array or undefined, review is a
string or undefined) before calling result.set; keep function signature and
return an empty Map on invalid inputs to preserve callers.
In @web/src/routes/+page.svelte:
- Around line 12-22: The $derived usage for allTags is wrapped in a function
which is invalid in Svelte 5; replace the function wrapper with a direct
expression or use $derived.by for a body-based derivation. Update the allTags
declaration to compute from data.items and item.customInfo?.tags directly via
$derived (or $derived.by if you need the current multi-line loop semantics) so
the returned value is an array of sorted unique tags.
- Around line 37-64: The computed store filteredItems is using $derived(...) but
for complex derivations you must call $derived.by(...); replace the $derived(()
=> { ... }) invocation that defines filteredItems with $derived.by(() => { ...
}) while keeping the same callback body and any dependencies/imports intact so
the tag-mode and normal search branches (references: filteredItems, isTagMode,
tagQuery, searchQuery, data.items) continue to work.
- Around line 31-35: The $derived call for tagSuggestions uses Svelte 4 syntax
and must be replaced with Svelte 5 derived usage: create tagSuggestions with the
derived helper (not $derived) and list the dependent stores (isTagMode,
tagQuery, allTags) as inputs, then use the callback that receives their values
(e.g., ([$isTagMode, $tagQuery, $allTags]) => { if (!$isTagMode) return [];
return $allTags.filter(tag => tag.toLowerCase().includes($tagQuery)); }); also
update any calls to allTags(), isTagMode(), and tagQuery() to use the store
values passed into the derived callback (or their $-prefixed store values if
used elsewhere) so the dependency and reactive behavior are correct.
In @web/src/routes/+page.ts:
- Around line 5-12: The load function currently calls
fetch('/data/bibliography.json') without handling network or HTTP errors; wrap
the fetch+json logic in a try/catch inside export async function load({ fetch })
to detect fetch failures and non-ok responses (check response.ok and throw with
response.status), log the error (console.error or a logger) and return a safe
fallback like { items: [] } so prerendering/runtime won’t crash; reference the
load function, the fetch('/data/bibliography.json') call and the
BibliographyItem typing when adding the error handling and fallback.
🧹 Nitpick comments (9)
web/static/robots.txt (1)
1-3: robots.txt is correctly configured for a public bibliography site.The permissive policy is appropriate for a documentation/bibliography site where search engine discoverability is valuable. The file syntax is valid and will allow crawlers to index all content without restriction.
Optional enhancement: Consider adding a
Sitemapdirective to help search engines discover and prioritize your content more efficiently. If you add a sitemap in the future, update this file with:Sitemap: https://yourdomain.com/sitemap.xmlweb/README.md (1)
1-38: Consider documenting bibliography-specific workflows.The README is standard SvelteKit boilerplate. For better onboarding, consider adding sections that document:
- Data file generation (the
web/scripts/generate-data.tsprebuild step mentioned in the PR)- Local development workflow with bibliography content sourced from
contents/directory- Any custom npm scripts relevant to bibliography management
This helps future maintainers understand the data flow beyond generic SvelteKit commands.
web/src/lib/index.ts (1)
1-1: Consider re-exporting commonly used library utilities.The
$libindex serves as a convenient entry point. Per the PR, multiple utility modules exist (types, parsers, merge logic, author formatting). Consider adding re-exports for frequently imported utilities to improve discoverability and reduce import paths:// Example: Re-export common utilities for easier access export * from './types.js'; export { mergeCustomInfo } from './merge.js'; export { formatAuthor } from './formatters/author.js';This is optional but improves developer ergonomics and makes the public API clearer.
contents/references.bib (1)
3-3: Standardize author field formatting for consistent parsing.The author fields use inconsistent spacing:
- Line 3:
{甲野,善紀}(no space after comma)- Line 12:
{諏訪, 正樹}(space after comma)Standardize to one format to ensure predictable parsing behavior, especially given the romaji-enabled author search feature mentioned in the PR objectives.
🔎 Recommended fix (standardize with space after comma)
- author = {甲野,善紀}, + author = {甲野, 善紀},Also applies to: 12-12
web/src/lib/parsers/bib.ts (2)
37-58: Nested braces support is limited to one level.The regex pattern on line 45 only handles a single level of nested braces. For example:
- ✓ Works:
{text with {nested} braces}- ✗ Fails:
{text with {deeply {nested}} braces}The comment on line 39 says "including nested" but doesn't specify depth. Most BibTeX files use single-level nesting, so this is likely sufficient for typical use cases.
If deeper nesting is needed, consider using a proper parser library like
biblatex-csl-converteror implementing a recursive brace matcher. However, for the current scope, this limitation is acceptable.
21-31: Consider validating required fields.The parser defaults missing
titleandauthorto empty strings (lines 24-25) andyearto0(line 26). While this prevents runtime errors, bibliography entries with missing required fields might not be useful.Consider adding validation or warnings for entries with missing required metadata:
🔎 Optional validation approach
const fields = parseFields(fieldsContent); + + // Optionally validate required fields + if (!fields.title || !fields.author) { + console.warn(`Entry ${id} is missing required fields:`, { + hasTitle: !!fields.title, + hasAuthor: !!fields.author + }); + } entries.push({ id, type, title: fields.title || '', author: fields.author || '', year: parseInt(fields.year || '0', 10), publisher: fields.publisher, series: fields.series, isbn: fields.isbn, url: fields.url });web/src/lib/formatters/author.ts (1)
4-7: CJK character range is incomplete.The regex on line 6 uses
\u4E00-\u9FAFfor CJK Unified Ideographs, but the complete range is\u4E00-\u9FFF. Characters in the range U+9FB0–U+9FFF (160 characters) will not be detected as Japanese.🔎 Recommended fix to complete the CJK range
function isJapanese(name: string): boolean { // Match Hiragana, Katakana, and CJK Unified Ideographs - return /[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]/.test(name); + return /[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF]/.test(name); }For more comprehensive coverage, consider including CJK extensions:
return /[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3400-\u4DBF]/.test(name);web/src/routes/+page.svelte (2)
25-28: Simplify derived expressions—use direct expressions instead of function wrappers.These simple derivations should use direct expressions rather than function wrappers.
🔎 Proposed refactor
- const isTagMode = $derived(() => searchQuery.startsWith('#')); + const isTagMode = $derived(searchQuery.startsWith('#')); - const tagQuery = $derived(() => (isTagMode() ? searchQuery.slice(1).toLowerCase() : '')); + const tagQuery = $derived(isTagMode ? searchQuery.slice(1).toLowerCase() : '');After this change, you'll need to update usages from
isTagMode()toisTagModeandtagQuery()totagQuerythroughout the file.
104-177: Header search UI is well-implemented with proper keyboard and mouse interaction.The search input includes:
- Visual search icon and clear button
- Tag mode detection and suggestions
- Keyboard navigation (arrows, Enter, Escape)
- Proper event timing (mousedown vs click) to avoid blur conflicts
Tailwind v4 syntax is correctly used throughout.
Optional accessibility enhancement: Consider adding
aria-labelattributes to the search input and clear button, plusrole="listbox"androle="option"for the tag suggestions dropdown to improve screen reader support.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlweb/src/lib/assets/favicon.svgis excluded by!**/*.svg
📒 Files selected for processing (28)
.github/workflows/ci.ymlMakefileREADME.mdcontents/custom_info.yamlcontents/references.bibdocs/adr/0004-web-ui-technology-stack.mdreferences.bibweb/.gitignoreweb/.npmrcweb/README.mdweb/package.jsonweb/scripts/generate-data.tsweb/src/app.d.tsweb/src/app.htmlweb/src/lib/formatters/author.tsweb/src/lib/index.tsweb/src/lib/merge.tsweb/src/lib/parsers/bib.tsweb/src/lib/parsers/yaml.tsweb/src/lib/types.tsweb/src/routes/+layout.svelteweb/src/routes/+page.svelteweb/src/routes/+page.tsweb/src/routes/layout.cssweb/static/robots.txtweb/svelte.config.jsweb/tsconfig.jsonweb/vite.config.ts
💤 Files with no reviewable changes (1)
- references.bib
🧰 Additional context used
🧬 Code graph analysis (5)
web/src/lib/parsers/bib.ts (1)
web/src/lib/types.ts (1)
BibEntry(4-14)
web/src/lib/merge.ts (1)
web/src/lib/types.ts (3)
BibEntry(4-14)CustomInfo(19-22)BibliographyItem(27-29)
web/src/routes/+page.ts (1)
web/src/lib/types.ts (1)
BibliographyItem(27-29)
web/src/lib/parsers/yaml.ts (1)
web/src/lib/types.ts (2)
CustomInfo(19-22)CustomInfoYaml(34-41)
web/scripts/generate-data.ts (4)
web/src/lib/types.ts (4)
BibEntry(4-14)CustomInfo(19-22)CustomInfoYaml(34-41)BibliographyItem(27-29)web/src/lib/parsers/bib.ts (1)
parseBibTeX(7-35)web/src/lib/parsers/yaml.ts (1)
parseCustomInfo(12-32)web/src/lib/merge.ts (1)
mergeBibliography(6-14)
🪛 checkmake (0.2.2)
Makefile
[warning] 9-9: Target body for "import" exceeds allowed length of 5 (7).
(maxbodylength)
[warning] 7-7: Missing required phony target "all"
(minphony)
[warning] 7-7: Missing required phony target "test"
(minphony)
🪛 LanguageTool
README.md
[grammar] ~11-~11: Ensure spelling is correct
Context: ...ation key. ## Development Start local sever bash cd web pnpm run dev
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Workers Builds: kotetsu
🔇 Additional comments (20)
web/.npmrc (1)
1-1: Enforce Node.js/npm version compatibility.Enabling
engine-strict=trueensures that developers and CI pipelines enforce compatible Node.js and npm versions, improving build reproducibility and preventing version-mismatch surprises. Good practice.web/src/app.html (1)
1-11: LGTM!Standard SvelteKit app template with proper viewport meta, preload data strategy, and placeholder hooks.
web/src/routes/layout.css (1)
1-2: LGTM!Correct Tailwind CSS v4 syntax using
@import 'tailwindcss'and@plugindirective for the typography plugin.web/svelte.config.js (1)
1-18: LGTM!Clean SvelteKit configuration with Cloudflare adapter and Vite preprocessing, aligning with the ADR decision to deploy on Cloudflare Pages.
web/tsconfig.json (1)
1-21: LGTM!Well-configured TypeScript setup extending SvelteKit's generated config with strict mode enabled and appropriate compiler options for a bundler-based project.
web/vite.config.ts (1)
1-5: LGTM!Correct Vite configuration with Tailwind CSS v4 plugin and SvelteKit integration. Plugin order is appropriate.
web/src/app.d.ts (1)
1-13: LGTM!Standard SvelteKit type declaration file with placeholder interfaces ready for customization as needed.
.github/workflows/ci.yml (2)
10-27: Well-structured path-based filtering.The use of
dorny/paths-filterto conditionally run jobs based on changed paths is a good approach for CI efficiency. The separation ofcontentsandwebfilters allows targeted validation.
41-70: No lint script is configured in the web project.The web project does not have a lint script, nor does it have any linting tools (ESLint, Prettier, etc.) in its dependencies. The existing scripts only include type checking via
svelte-checkand build steps. The reference tocheck-contentsis not relevant as it handles markdown/content files via a Makefile, not JavaScript/Svelte linting.Likely an incorrect or invalid review comment.
web/src/routes/+layout.svelte (1)
1-9: LGTM!The layout component correctly uses Svelte 5 syntax with
$props()and{@render children()}, properly imports global styles and assets, and injects the favicon into the document head.web/.gitignore (1)
1-26: LGTM!The ignore patterns are comprehensive and follow standard practices for a SvelteKit project. The inclusion of
/static/data/correctly ignores the generated bibliography data directory.web/src/lib/merge.ts (1)
6-14: LGTM! Clean merge implementation.The function correctly combines BibTeX entries with custom metadata. The use of
Map.get()safely handles missing entries by returningundefined, which aligns with the optionalcustomInfofield inBibliographyItem.docs/adr/0004-web-ui-technology-stack.md (1)
1-97: ADR looks well-structured and aligns with the PR implementation.The architecture decision record clearly documents the rationale for selecting SvelteKit, Cloudflare Pages, Tailwind + DaisyUI, and Auth.js. The decision tables, consequences, and implementation notes are comprehensive and match the actual technology choices seen in the PR (svelte.config.js using Cloudflare adapter, Tailwind configuration, etc.).
web/src/lib/types.ts (1)
1-42: LGTM! Type definitions are well-documented and properly structured.The type hierarchy clearly separates concerns:
BibEntryfor parsed BibTeX dataCustomInfofor blog-specific metadataBibliographyItemas the merged resultCustomInfoYamlfor the raw YAML structureThese types align with their usage across the parsing, merging, and data generation pipeline.
Makefile (1)
1-16: LGTM! Makefile correctly adapted to the new contents/ directory structure.The changes properly update file paths to use
CONTENTS_DIRand theimporttarget now processes.bibtexfiles from the correct location with helpful logging.web/scripts/generate-data.ts (1)
123-145: Main function orchestrates data generation correctly.The
main()function properly reads the source files, invokes parsing and merging, ensures output directory existence, and writes the final JSON with helpful logging.web/src/routes/+page.svelte (4)
66-98: Event handlers implement correct keyboard navigation and suggestion interaction.The handlers properly manage:
- Tag selection with state reset
- Arrow key navigation with boundary checks
- Enter key selection and Escape dismissal
- Suggestion visibility tied to tag mode
- Using
onmousedowninstead ofonclickto prevent blur race conditionsAfter fixing the
$derivedsyntax issues, update the function calls in these handlers (e.g.,isTagMode()→isTagMode,tagSuggestions()→tagSuggestions).
179-318: Bibliography item display is well-structured with proper styling and accessibility.The card layout effectively presents:
- Entry metadata (type, title, author, year, publisher, series)
- Custom info (tags, review)
- External links with secure attributes (
rel="noopener noreferrer")Tailwind v4 classes are correctly applied. After fixing the
$derivedsyntax, updatefilteredItems()tofilteredItemsin the loop (line 181).
320-328: Empty state handling provides clear user feedback.Appropriate messages for both search-no-results and no-data scenarios. After fixing the
$derivedsyntax, updatefilteredItems()tofilteredItemshere as well.
331-340: Footer displays helpful item counts.Clear indication of filtered vs total items when search is active. Update
filteredItems()tofilteredItemsafter fixing the$derivedsyntax.
|
|
||
| ## Development | ||
|
|
||
| Start local sever |
There was a problem hiding this comment.
Fix typo in documentation.
"Start local sever" should be "Start local server".
🔎 Proposed fix
-Start local sever
+Start local server🧰 Tools
🪛 LanguageTool
[grammar] ~11-~11: Ensure spelling is correct
Context: ...ation key. ## Development Start local sever bash cd web pnpm run dev
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
In @README.md at line 11, Update the README text that currently reads "Start
local sever" to the correct phrase "Start local server" (locate and replace the
string "Start local sever" in the README.md content).
| "dev": "pnpm generate-data && vite dev --port 30500 --open", | ||
| "build": "pnpm generate-data && vite build", | ||
| "preview": "vite preview", | ||
| "prepare": "svelte-kit sync || echo ''", |
There was a problem hiding this comment.
Avoid suppressing errors in the prepare script.
The || echo '' fallback silently swallows errors from svelte-kit sync, which could hide legitimate setup issues during package installation or in CI/CD environments.
🔎 Recommended fix
- "prepare": "svelte-kit sync || echo ''",
+ "prepare": "svelte-kit sync",If svelte-kit sync legitimately fails in some scenarios and you need to handle that gracefully, document why the fallback is necessary or use a more explicit error handling approach.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "prepare": "svelte-kit sync || echo ''", | |
| "prepare": "svelte-kit sync", |
🤖 Prompt for AI Agents
In @web/package.json at line 11, The package.json "prepare" script currently
appends "|| echo ''" which silently swallows failures from "svelte-kit sync";
remove the fallback so failures surface during install/CI (i.e., change the
"prepare" script to run "svelte-kit sync" only) or, if you need to tolerate
specific known errors, replace the silent fallback with explicit handling and a
clear comment explaining why (refer to the "prepare" script entry in
package.json).
| // Parse BibTeX content | ||
| function parseBibTeX(content: string): BibEntry[] { | ||
| const entries: BibEntry[] = []; | ||
| const entryRegex = /@(\w+)\s*\{\s*([^,]+)\s*,([^@]*)\}/g; | ||
| let match; | ||
|
|
||
| while ((match = entryRegex.exec(content)) !== null) { | ||
| const type = match[1].toLowerCase(); | ||
| const id = match[2].trim(); | ||
| const fieldsContent = match[3]; | ||
| const fields = parseFields(fieldsContent); | ||
|
|
||
| entries.push({ | ||
| id, | ||
| type, | ||
| title: fields.title || '', | ||
| author: fields.author || '', | ||
| year: parseInt(fields.year || '0', 10), | ||
| publisher: fields.publisher, | ||
| series: fields.series, | ||
| isbn: fields.isbn, | ||
| url: fields.url | ||
| }); | ||
| } | ||
|
|
||
| return entries; | ||
| } | ||
|
|
||
| function parseFields(content: string): Record<string, string> { | ||
| const fields: Record<string, string> = {}; | ||
| // Match field = {value} or field = "value" or field = bareword | ||
| const fieldRegex = /\s*(\w+)\s*=\s*(?:\{((?:[^{}]|\{[^{}]*\})*)\}|"([^"]*)"|(\w+))\s*,?/gs; | ||
| let match; | ||
|
|
||
| while ((match = fieldRegex.exec(content)) !== null) { | ||
| const name = match[1].toLowerCase(); | ||
| // value can be in braces (match[2]), quotes (match[3]), or be a bare word/number (match[4]) | ||
| const value = match[2] ?? match[3] ?? match[4]; | ||
| if (value !== undefined) { | ||
| fields[name] = value.trim(); | ||
| } | ||
| } | ||
|
|
||
| return fields; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Eliminate parser duplication—import from web/src/lib/parsers/bib.ts.
The parseBibTeX and parseFields functions are duplicated from web/src/lib/parsers/bib.ts. Import the parser instead to avoid maintaining duplicate code.
🔎 Proposed refactor
+import { parseBibTeX } from '../src/lib/parsers/bib.js';
+
-// Parse BibTeX content
-function parseBibTeX(content: string): BibEntry[] {
- const entries: BibEntry[] = [];
- const entryRegex = /@(\w+)\s*\{\s*([^,]+)\s*,([^@]*)\}/g;
- let match;
-
- while ((match = entryRegex.exec(content)) !== null) {
- const type = match[1].toLowerCase();
- const id = match[2].trim();
- const fieldsContent = match[3];
- const fields = parseFields(fieldsContent);
-
- entries.push({
- id,
- type,
- title: fields.title || '',
- author: fields.author || '',
- year: parseInt(fields.year || '0', 10),
- publisher: fields.publisher,
- series: fields.series,
- isbn: fields.isbn,
- url: fields.url
- });
- }
-
- return entries;
-}
-
-function parseFields(content: string): Record<string, string> {
- const fields: Record<string, string> = {};
- // Match field = {value} or field = "value" or field = bareword
- const fieldRegex = /\s*(\w+)\s*=\s*(?:\{((?:[^{}]|\{[^{}]*\})*)\}|"([^"]*)"|(\w+))\s*,?/gs;
- let match;
-
- while ((match = fieldRegex.exec(content)) !== null) {
- const name = match[1].toLowerCase();
- // value can be in braces (match[2]), quotes (match[3]), or be a bare word/number (match[4])
- const value = match[2] ?? match[3] ?? match[4];
- if (value !== undefined) {
- fields[name] = value.trim();
- }
- }
-
- return fields;
-}🤖 Prompt for AI Agents
In @web/scripts/generate-data.ts around lines 46 - 90, The file duplicates the
BibTeX parsing logic (functions parseBibTeX and parseFields); remove these local
function definitions and instead import the shared parser (exporting parseBibTeX
and parseFields and any needed types like BibEntry) from the central parser
module, update usages to use the imported parseBibTeX, and ensure the import is
added at the top and types are referenced correctly so there is no duplicate
implementation to maintain.
| const allTags = $derived(() => { | ||
| const tags = new Set<string>(); | ||
| for (const item of data.items) { | ||
| if (item.customInfo?.tags) { | ||
| for (const tag of item.customInfo.tags) { | ||
| tags.add(tag); | ||
| } | ||
| } | ||
| } | ||
| return Array.from(tags).sort(); | ||
| }); |
There was a problem hiding this comment.
Simplify $derived syntax—remove unnecessary function wrapper.
In Svelte 5, $derived expects an expression or block directly, not a function. The current $derived(() => {...}) pattern is incorrect.
🔎 Proposed fix
- const allTags = $derived(() => {
+ const allTags = $derived.by(() => {
const tags = new Set<string>();
for (const item of data.items) {
if (item.customInfo?.tags) {
@@ -20,5 +20,5 @@
}
}
return Array.from(tags).sort();
});Note: Use $derived.by() for complex derivations that require a function body. Alternatively, if the computation can be expressed as a single expression, use $derived(expression).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const allTags = $derived(() => { | |
| const tags = new Set<string>(); | |
| for (const item of data.items) { | |
| if (item.customInfo?.tags) { | |
| for (const tag of item.customInfo.tags) { | |
| tags.add(tag); | |
| } | |
| } | |
| } | |
| return Array.from(tags).sort(); | |
| }); | |
| const allTags = $derived.by(() => { | |
| const tags = new Set<string>(); | |
| for (const item of data.items) { | |
| if (item.customInfo?.tags) { | |
| for (const tag of item.customInfo.tags) { | |
| tags.add(tag); | |
| } | |
| } | |
| } | |
| return Array.from(tags).sort(); | |
| }); |
🤖 Prompt for AI Agents
In @web/src/routes/+page.svelte around lines 12 - 22, The $derived usage for
allTags is wrapped in a function which is invalid in Svelte 5; replace the
function wrapper with a direct expression or use $derived.by for a body-based
derivation. Update the allTags declaration to compute from data.items and
item.customInfo?.tags directly via $derived (or $derived.by if you need the
current multi-line loop semantics) so the returned value is an array of sorted
unique tags.
| const tagSuggestions = $derived(() => { | ||
| if (!isTagMode()) return []; | ||
| const query = tagQuery(); | ||
| return allTags().filter((tag) => tag.toLowerCase().includes(query)); | ||
| }); |
There was a problem hiding this comment.
Fix $derived usage for tagSuggestions.
Similar to allTags, this needs the correct Svelte 5 syntax.
🔎 Proposed fix
- const tagSuggestions = $derived(() => {
- if (!isTagMode()) return [];
- const query = tagQuery();
+ const tagSuggestions = $derived.by(() => {
+ if (!isTagMode) return [];
+ const query = tagQuery;
return allTags().filter((tag) => tag.toLowerCase().includes(query));
});Note: After fixing the isTagMode and tagQuery syntax as suggested above, update references here as well. Also update calls to allTags() if you refactor that too.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @web/src/routes/+page.svelte around lines 31 - 35, The $derived call for
tagSuggestions uses Svelte 4 syntax and must be replaced with Svelte 5 derived
usage: create tagSuggestions with the derived helper (not $derived) and list the
dependent stores (isTagMode, tagQuery, allTags) as inputs, then use the callback
that receives their values (e.g., ([$isTagMode, $tagQuery, $allTags]) => { if
(!$isTagMode) return []; return $allTags.filter(tag =>
tag.toLowerCase().includes($tagQuery)); }); also update any calls to allTags(),
isTagMode(), and tagQuery() to use the store values passed into the derived
callback (or their $-prefixed store values if used elsewhere) so the dependency
and reactive behavior are correct.
| const filteredItems = $derived(() => { | ||
| if (!searchQuery.trim()) return data.items; | ||
|
|
||
| // Tag search mode | ||
| if (isTagMode()) { | ||
| const query = tagQuery(); | ||
| if (!query) return data.items; | ||
| return data.items.filter((item) => | ||
| item.customInfo?.tags?.some((tag) => tag.toLowerCase().includes(query)) | ||
| ); | ||
| } | ||
|
|
||
| // Normal search mode | ||
| const query = searchQuery.toLowerCase(); | ||
| return data.items.filter((item) => { | ||
| // Search in entry ID (for romanized author names) | ||
| if (item.id.toLowerCase().includes(query)) return true; | ||
| // Search in title | ||
| if (item.title.toLowerCase().includes(query)) return true; | ||
| // Search in author | ||
| if (item.author.toLowerCase().includes(query)) return true; | ||
| // Search in tags | ||
| if (item.customInfo?.tags?.some((tag) => tag.toLowerCase().includes(query))) return true; | ||
| // Search in review | ||
| if (item.customInfo?.review?.toLowerCase().includes(query)) return true; | ||
| return false; | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Fix $derived usage for filteredItems.
Same issue—use $derived.by() for complex derivations.
🔎 Proposed fix
- const filteredItems = $derived(() => {
+ const filteredItems = $derived.by(() => {
if (!searchQuery.trim()) return data.items;
// Tag search mode
- if (isTagMode()) {
- const query = tagQuery();
+ if (isTagMode) {
+ const query = tagQuery;
if (!query) return data.items;
return data.items.filter((item) =>
item.customInfo?.tags?.some((tag) => tag.toLowerCase().includes(query))
);
}
// Normal search mode
const query = searchQuery.toLowerCase();
return data.items.filter((item) => {
// Search in entry ID (for romanized author names)
if (item.id.toLowerCase().includes(query)) return true;
// Search in title
if (item.title.toLowerCase().includes(query)) return true;
// Search in author
if (item.author.toLowerCase().includes(query)) return true;
// Search in tags
if (item.customInfo?.tags?.some((tag) => tag.toLowerCase().includes(query))) return true;
// Search in review
if (item.customInfo?.review?.toLowerCase().includes(query)) return true;
return false;
});
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const filteredItems = $derived(() => { | |
| if (!searchQuery.trim()) return data.items; | |
| // Tag search mode | |
| if (isTagMode()) { | |
| const query = tagQuery(); | |
| if (!query) return data.items; | |
| return data.items.filter((item) => | |
| item.customInfo?.tags?.some((tag) => tag.toLowerCase().includes(query)) | |
| ); | |
| } | |
| // Normal search mode | |
| const query = searchQuery.toLowerCase(); | |
| return data.items.filter((item) => { | |
| // Search in entry ID (for romanized author names) | |
| if (item.id.toLowerCase().includes(query)) return true; | |
| // Search in title | |
| if (item.title.toLowerCase().includes(query)) return true; | |
| // Search in author | |
| if (item.author.toLowerCase().includes(query)) return true; | |
| // Search in tags | |
| if (item.customInfo?.tags?.some((tag) => tag.toLowerCase().includes(query))) return true; | |
| // Search in review | |
| if (item.customInfo?.review?.toLowerCase().includes(query)) return true; | |
| return false; | |
| }); | |
| }); | |
| const filteredItems = $derived.by(() => { | |
| if (!searchQuery.trim()) return data.items; | |
| // Tag search mode | |
| if (isTagMode) { | |
| const query = tagQuery; | |
| if (!query) return data.items; | |
| return data.items.filter((item) => | |
| item.customInfo?.tags?.some((tag) => tag.toLowerCase().includes(query)) | |
| ); | |
| } | |
| // Normal search mode | |
| const query = searchQuery.toLowerCase(); | |
| return data.items.filter((item) => { | |
| // Search in entry ID (for romanized author names) | |
| if (item.id.toLowerCase().includes(query)) return true; | |
| // Search in title | |
| if (item.title.toLowerCase().includes(query)) return true; | |
| // Search in author | |
| if (item.author.toLowerCase().includes(query)) return true; | |
| // Search in tags | |
| if (item.customInfo?.tags?.some((tag) => tag.toLowerCase().includes(query))) return true; | |
| // Search in review | |
| if (item.customInfo?.review?.toLowerCase().includes(query)) return true; | |
| return false; | |
| }); | |
| }); |
🤖 Prompt for AI Agents
In @web/src/routes/+page.svelte around lines 37 - 64, The computed store
filteredItems is using $derived(...) but for complex derivations you must call
$derived.by(...); replace the $derived(() => { ... }) invocation that defines
filteredItems with $derived.by(() => { ... }) while keeping the same callback
body and any dependencies/imports intact so the tag-mode and normal search
branches (references: filteredItems, isTagMode, tagQuery, searchQuery,
data.items) continue to work.
Deploying kotetsu with
|
| Latest commit: |
62457cf
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ec70fd74.kotetsu.pages.dev |
| Branch Preview URL: | https://issue-3-web-ui.kotetsu.pages.dev |
User description
PR Type
Enhancement, Documentation
Description
Build complete SvelteKit web UI for bibliography management
Implement text and tag-based search with autocomplete suggestions
Add CI/CD pipeline for web application with path-based filtering
Document technology stack decisions in ADR 0004
Move bibliography files to contents directory structure
Diagram Walkthrough
File Walkthrough
3 files
Document SvelteKit and Cloudflare Pages selectionDocument SvelteKit project setup instructionsDocument web development setup instructions13 files
Build interactive bibliography display with searchLoad and merge bibliography data on serverParse BibTeX entries with regex-based parserParse YAML custom metadata by site IDMerge BibTeX entries with custom metadataFormat author names by language detectionDefine TypeScript types for bibliography dataCreate SvelteKit root HTML templateDefine SvelteKit global type declarationsSetup root layout with Tailwind stylesCreate library barrel export fileAdd two Japanese bibliography entriesAdd path-filtered CI jobs for web application9 files
Configure SvelteKit with Tailwind and DaisyUIConfigure Cloudflare adapter for deploymentSetup Vite with Tailwind and SvelteKit pluginsConfigure TypeScript for SvelteKit projectImport Tailwind and typography pluginAdd robots.txt for search engine crawlingConfigure pnpm workspace settingsEnforce Node.js engine version requirementMove bibliography files to contents directory3 files
Summary by CodeRabbit
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.