195 Commits
Author SHA1 Message Date
squid e822d40943 NO-ISSUE Sync with release/0.8 (#315)
Reviewed-on: #315
2026-09-28 11:37:32 +08:00
squid 389fc4ecfd NO-ISSUE Bump version to 0.8.0 (#314)
Deployment / build-image (frontend, ./frontend, ./frontend/Containerfile, IMAGE_REPO_FRONTEND) (release) Successful in 5m41s
Deployment / build-image (backend, ./backend, ./backend/Containerfile, IMAGE_REPO_BACKEND) (release) Successful in 8h14m10s
Frontend CI / build (pull_request) Successful in 8h3m51s
PR Title Check / pr-title-check (pull_request) Successful in 8h0m1s
Deployment / package-manifest (release) Successful in 8h0m2s
Backend CI / build (pull_request) Successful in 26m34s
### Description

None

### Package Changes

None

### Screenshots

None

### Reference

None

### Checklist

- [x] A milestone is set
- [ ] The related issues has been linked to this branch

Reviewed-on: #314
Co-authored-by: SquidSpirit <squid@squidspirit.com>
v0.8.0
2026-09-24 16:12:47 +08:00
squid f357a7b20f NO-ISSUE Improve CI efficiency (#313)
### Description

- Add cache for downloading tool chain.

### Package Changes

None

### Screenshots

None

### Reference

None

### Checklist

- [x] A milestone is set
- [ ] The related issues has been linked to this branch

Reviewed-on: #313
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-24 16:09:26 +08:00
squid 8473e8e827 NO-ISSUE Bump version to 0.8.0-beta.3 (#312)
Backend CI / build (push) Successful in 8h1m58s
Frontend CI / build (push) Successful in 8h6m33s
Deployment / build-image (frontend, ./frontend, ./frontend/Containerfile, IMAGE_REPO_FRONTEND) (release) Successful in 8h2m38s
Deployment / build-image (backend, ./backend, ./backend/Containerfile, IMAGE_REPO_BACKEND) (release) Successful in 27m45s
Deployment / package-manifest (release) Successful in 28s
### Description

None

### Package Changes

None

### Screenshots

None

### Reference

None

### Checklist

- [x] A milestone is set
- [ ] The related issues has been linked to this branch

Reviewed-on: #312
Co-authored-by: SquidSpirit <squid@squidspirit.com>
v0.8.0-beta.3
2026-09-24 14:45:53 +08:00
squid 0d8567b164 NO-ISSUE Parameterize deployment manifests by instance (#311)
Frontend CI / build (push) Successful in 8h0m37s
Backend CI / build (push) Successful in 30m12s
### Description

Parameterize the deployment manifests so a second instance (for example
`blog-beta`) can be generated without hand-editing `blog` to `blog-beta`.

- `Makefile` gains `NAME` (default `blog`), `FRONTEND_PORT` / `BACKEND_PORT`,
  and `BACKEND_IMAGE` / `BACKEND_TAG` / `FRONTEND_IMAGE` / `FRONTEND_TAG`.
- Sources are resolved per instance: `<NAME>.secret.yaml`, `<NAME>.config.yaml`
  and `<NAME>.pod.yaml` are used when present, otherwise the shared `blog.*`
  template is used.
- The manifests use `__NAME__`, `__FRONTEND_PORT__`, `__BACKEND_PORT__`,
  `__*_IMAGE__` and `__*_TAG__` placeholders, substituted by the Makefile.
- Image repositories are no longer affected by the name substitution; the
  registry/repo and the tag are configurable independently.
- The generated bundle is now `<NAME>.bundle.yaml` (production is still
  `blog.bundle.yaml`).
- `README.md` updated accordingly.

Usage examples:

```sh
make                                   # writes blog.bundle.yaml
make NAME=blog-beta BACKEND_TAG=latest FRONTEND_TAG=latest
make NAME=blog-beta FRONTEND_PORT=10021 BACKEND_PORT=10022
```

### Package Changes

None

### Screenshots

N/A

### Reference

No related issue (NO-ISSUE).

### Checklist

- [x] A milestone is set
- [ ] The related issues has been linked to this branch

---

Generated by Zed with Deepseek Flash

Reviewed-on: #311
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-24 14:25:40 +08:00
squid a8c9b0cc8a NO-ISSUE Bump version to 0.8.0-beta.2 (#310)
Frontend CI / build (push) Successful in 8h0m36s
Backend CI / build (push) Successful in 7m38s
Deployment / build-image (frontend, ./frontend, ./frontend/Containerfile, IMAGE_REPO_FRONTEND) (release) Successful in 3m5s
Deployment / build-image (backend, ./backend, ./backend/Containerfile, IMAGE_REPO_BACKEND) (release) Successful in 8h13m45s
Deployment / package-manifest (release) Successful in 8h0m3s
### Description

None

### Package Changes

None

### Screenshots

None

### Reference

None

### Checklist

- [x] A milestone is set
- [ ] The related issues has been linked to this branch

Reviewed-on: #310
Co-authored-by: SquidSpirit <squid@squidspirit.com>
v0.8.0-beta.2
2026-09-24 05:27:10 +08:00
squid 765688a302 BLOG-308 Fix deployment failed (#309)
Frontend CI / build (push) Successful in 1m18s
Backend CI / build (push) Successful in 8h2m21s
### Description

- Use `GITEA_TOKEN` instead of tokens set by ourselves.

### Package Changes

None

### Screenshots

None

### Reference

None

### Checklist

- [x] A milestone is set
- [ ] The related issues has been linked to this branch

Reviewed-on: #309
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-24 05:14:05 +08:00
squid b195f3b257 NO-ISSUE Bump version to 0.8.0-beta.1 (#307)
Frontend CI / build (push) Successful in 8h1m9s
Backend CI / build (push) Successful in 8m3s
Deployment / build-image (frontend, ./frontend, ./frontend/Containerfile, IMAGE_REPO_FRONTEND) (release) Successful in 8h3m31s
Deployment / build-image (backend, ./backend, ./backend/Containerfile, IMAGE_REPO_BACKEND) (release) Successful in 22m16s
Deployment / package-manifest (release) Failing after 41s
### Description

None

### Package Changes

None

### Screenshots

None

### Reference

None

### Checklist

- [x] A milestone is set
- [ ] The related issues has been linked to this branch

Reviewed-on: #307
Co-authored-by: SquidSpirit <squid@squidspirit.com>
v0.8.0-beta.1
2026-09-24 04:16:08 +08:00
squid 959c921506 NO-ISSUE Update dependencies and adopt Apache-2.0 license (#306)
Frontend CI / build (push) Successful in 1m13s
Backend CI / build (push) Successful in 8h2m21s
## Description

Routine dependency maintenance for the Rust backend and the SvelteKit frontend, removal of the now-unused `crypto-js` package, and re-licensing of the project under Apache-2.0 with a separate trademark policy.

- Bumped workspace crates (`actix-web`, `fastembed` 5→7, `qdrant-client`, `sentry`, `utoipa` 5→6, …) and adapted to breaking API changes: `fastembed`'s `InitOptions`→`TextInitOptions`, and `sentry`'s struct-literal `ClientOptions` now uses the builder API.
- Bumped frontend tooling/runtime deps (Svelte, Vite 8, TypeScript 6, ESLint 10, Tailwind 4.3, Prettier plugins, `markdown-it` 15, `@sentry/sveltekit`); `vite.config.ts` now uses a JSON import attribute.
- Removed `crypto-js` / `@types/crypto-js`. Markdown heading anchors now use readable slugs with `-N` suffixes for duplicates instead of MD5 hashes.
- Replaced MIT + custom license with Apache-2.0 (`LICENSE`, `NOTICE`), added `TRADEMARK.md`, removed `LICENSE.md`, and updated the README. Added `license` fields to `package.json` and all Cargo manifests.

⚠️ Behavior change: heading anchor IDs changed from MD5 hashes to readable slugs, so any existing `#hash` deep links to headings will no longer resolve.

## Package Changes

| Package | Action | Version |
| --- | --- | --- |
| actix-multipart | Updated | 0.7.2 -> 0.8.4 |
| actix-web | Updated | 4.13.0 -> 4.15.0 |
| anyhow | Updated | 1.0.102 -> 1.0.104 |
| async-trait | Updated | 0.1.89 -> 0.1.92 |
| chrono | Updated | 0.4.44 -> 0.4.45 |
| env_logger | Updated | 0.11.10 -> 0.11.11 |
| fastembed | Updated | 5.13.0 -> 7.1.0 |
| futures | Updated | 0.3.32 -> 0.3.34 |
| log | Updated | 0.4.29 -> 0.4.34 |
| qdrant-client | Updated | 1.17.0 -> 1.19.0 |
| regex | Updated | 1.12.3 -> 1.13.1 |
| sentry | Updated | 0.47.0 -> 0.49.3 |
| serde | Updated | 1.0.228 -> 1.0.229 |
| serde_json | Updated | 1.0.145 -> 1.0.151 |
| text-splitter | Updated | 0.29.3 -> 0.32.0 |
| tokenizers | Updated | 0.22.2 -> 0.23.2 |
| tokio | Updated | 1.51.0 -> 1.51.1 |
| utoipa | Updated | 5.4.0 -> 6.0.0 |
| utoipa-redoc | Updated | 6.0.0 -> 7.0.0 |
| crypto-js | Removed | 4.2.0 |
| @types/crypto-js | Removed | 4.2.2 |
| @sentry/sveltekit | Updated | 10.51.0 -> 10.75.3 |
| @eslint/compat | Updated | 2.0.5 -> 2.1.1 |
| @eslint/js | Updated | 9.39.4 -> 10.0.1 |
| @fortawesome/fontawesome-free | Updated | 7.2.0 -> 7.3.1 |
| @internationalized/date | Updated | 3.12.1 -> 3.12.4 |
| @lucide/svelte | Updated | 1.14.0 -> 1.47.0 |
| @sentry/vite-plugin | Updated | 5.2.1 -> 5.4.0 |
| @sveltejs/kit | Updated | 2.58.0 -> 2.70.3 |
| @sveltejs/vite-plugin-svelte | Updated | 6.2.4 -> 7.3.1 |
| @tailwindcss/typography | Updated | 0.5.19 -> 0.5.20 |
| @tailwindcss/vite | Updated | 4.2.4 -> 4.3.3 |
| @types/markdown-it | Updated | 14.1.2 -> 14.2.0 |
| @types/node | Updated | 22.19.17 -> 26.6.2 |
| @typescript/native | Added | 7.0.2 |
| bits-ui | Updated | 2.18.0 -> 2.19.3 |
| eslint | Updated | 9.39.4 -> 10.11.0 |
| eslint-plugin-simple-import-sort | Updated | 13.0.0 -> 14.0.0 |
| eslint-plugin-svelte | Updated | 3.17.1 -> 3.23.0 |
| globals | Updated | 17.5.0 -> 17.12.0 |
| highlight.js | Updated | 11.11.1 -> 11.12.0 |
| markdown-it | Updated | 14.1.1 -> 15.0.2 |
| markdown-it-attrs | Updated | 4.3.1 -> 5.0.1 |
| prettier | Updated | 3.8.3 -> 3.9.9 |
| prettier-plugin-svelte | Updated | 3.5.1 -> 4.1.1 |
| prettier-plugin-tailwindcss | Updated | 0.7.2 -> 0.8.1 |
| svelte | Updated | 5.55.5 -> 5.57.1 |
| svelte-check | Updated | 4.4.6 -> 4.7.6 |
| svelte-sonner | Updated | 1.1.1 -> 1.2.1 |
| tailwind-merge | Updated | 3.5.0 -> 3.7.0 |
| tailwind-variants | Updated | 3.2.2 -> 3.3.1 |
| tailwindcss | Updated | 4.2.4 -> 4.3.3 |
| typescript | Updated | 5.9.3 -> 6.0.3 |
| typescript-eslint | Updated | 8.59.1 -> 8.70.1 |
| vite | Updated | 7.3.1 -> 8.3.0 |
| zod | Updated | 4.4.1 -> 4.6.5 |

## Screenshots

N/A — no intended UI changes. The only user-facing behavior shift is the heading anchor-ID format noted above.

## Reference

None (NO-ISSUE — no tracking issue).

## Checklist

- [x] A milestone is set
- [ ] The related issues has been linked to this branch

---

Generated by Zed coding agent with Deepseek Flash

Reviewed-on: #306
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-24 04:01:41 +08:00
squid 145f491243 BLOG-235 Improve deployment workflow (#305)
Backend CI / build (push) Successful in 6m53s
Frontend CI / build (push) Successful in 1m29s
## Description

Improve the deployment workflow:

- Add Kubernetes manifests under `manifest/`: a Pod running Postgres, Qdrant, Redis, backend and frontend, with Secret/config templates, a Makefile that bundles them into a single file, and a README. The generated bundle is git-ignored.
- Add a backend CI workflow running `cargo check`, `cargo clippy` and `cargo test`.
- Rework the deployment workflow: build the frontend and backend images in parallel via a matrix, and add a job that packages `manifest/` as a release artifact.

## Package Changes

None

## Screenshots

N/A

## Reference

Resolves #235

## Checklist

- [x] A milestone is set
- [x] The related issues has been linked to this branch

---

Generated by Zed with Deepseek Flash

Reviewed-on: #305
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-24 03:44:56 +08:00
squid 75272f36fc BLOG-302 Keep the mobile search drawer inside the visible area above the keyboard (#304)
Frontend CI / build (push) Successful in 12m58s
### Description

On a physical Android device, opening the mobile search drawer could leave it with an abnormal height: once the on-screen keyboard opened, the drawer's top edge — and the focused keyword input — ended up above the top of the screen. It happened roughly half the time, and always with autofocus.

Root cause, measured on device with a `visualViewport` listener:

- Chrome on Android **pans** the visual viewport to reveal the focused input, so `visualViewport.offsetTop` grows (up to ~230px in the captures) while `window.innerHeight` and `visualViewport.height` stay unchanged.
- Panning only fires `visualViewport` **`scroll`** events, but `vaul-svelte` subscribed to `resize` only, so `onVisualViewportChange` never ran while the viewport was panned. The drawer kept the `bottom` offset computed for its previous position (`bottom = innerHeight - visualViewport.height`) and was left floating above the visible area.
- With the keyboard open, Chrome could also stretch the drawer past its natural height, which showed up as a blank gap between the keyword input and the footer buttons.
- Dismissing the keyboard did not restore the drawer, because the `isInput(activeElement) || keyboardIsOpen` guard can be false at that moment (`blur` fires before the resize event, and `keyboardIsOpen` is a delta-derived toggle).

Fix — `frontend/patches/vaul-svelte@1.0.0-next.7.patch` (applied by bun via `patchedDependencies`):

1. subscribe to `visualViewport` `scroll` in addition to `resize`, so the repositioning also runs while the viewport is panned;
2. subtract `visualViewport.offsetTop` from the drawer's `bottom`, so the drawer rests on the keyboard instead of above it;
3. cap the drawer height at its natural height and at the visible area, so keyboard repositioning can only make it shorter;
4. clear the inline `height`/`bottom` once the keyboard is gone, restoring the drawer's natural layout.

`frontend/Containerfile` additionally copies `patches/` into both install stages, since `bun install --frozen-lockfile` fails with "Couldn't find patch file" once `patchedDependencies` is declared.

Verified on a physical Android 10 device (Chrome 153): the drawer's `bottom` now tracks `(innerHeight - visualViewport.height) - visualViewport.offsetTop`, the drawer stays fully inside the visible area, and dismissing the keyboard restores it.

Upstream context: the same logic lives in React `vaul` — emilkowalski/vaul#619 ("The drawer goes outside the screen on iOS when input is clicked"). PR #633 covers part of it but is unmerged, so this stays a local patch until then.

### Package Changes

None. No package was added, removed or version-bumped. `vaul-svelte@1.0.0-next.7` is now patched locally (`patchedDependencies` → `patches/vaul-svelte@1.0.0-next.7.patch`).

### Screenshots

N/A — this is an on-screen-keyboard interaction fix, nothing is visible while the keyboard is closed. (Before/after `visualViewport` measurements are quoted in the Description.)

### Reference

Resolves #302

### Checklist

- [x] A milestone is set
- [x] The related issues has been linked to this branch

---
Generated by Zed coding agent with Deepseek Flash

Reviewed-on: #304
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-24 02:00:45 +08:00
squid 573d0265f8 BLOG-117 Auto generate sitemap and rss file (#303)
Frontend CI / build (push) Successful in 12m2s
## Description

This PR generates sitemap.xml and feed.xml from the frontend instead of the manual/toolbox script, so
the public URL scheme lives in one place and the documents are never hand-mutated.

- New SvelteKit server routes /sitemap.xml and /feed.xml, generated on demand with a short
  Cache-Control (public, max-age=300). There is no server-side cache, so the app regenerates on a
  cache miss; browsers, crawlers and any intermediary honour the header.
- A shared PostPage entity and ListPostPagesUseCase map published posts to their public URLs using
  resolve(), so the frontend route table stays the single source of truth.
- sitemap.xml (sitemap package) lists every published post plus the static pages only the frontend
  knows about: home, post list, and the terms documents.
- feed.xml (feed package) contains only posts, capped at the 20 most recent. Static pages never
  appear in the feed.
- The backend now exposes, per post, the preview image mime type and size (for a valid RSS enclosure)
  and the post updated_time. The frontend uses updated_time as sitemap lastmod, while the feed pubDate
  remains the published time.

## Package Changes

| Package | Action | Version |
| --- | --- | --- |
| feed | Added | 6.0.0 |
| sitemap | Added | 9.0.1 |
| sanitize-html | Updated | 2.17.3 -> 2.17.7 |

No Cargo.toml changes.

## Screenshots

N/A (XML endpoints).

## Reference

Resolves #117

## Checklist

- [x] A milestone is set
- [x] The related issues has been linked to this branch

---
Generated by Zed coding agent with Deepseek Flash

Reviewed-on: #303
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-23 22:39:42 +08:00
squid 4dde993368 BLOG-200 Enable Google AdSense (#301)
Frontend CI / build (push) Successful in 1m14s
### Description

Integrate Google AdSense into the blog frontend in a configuration-driven way, and add the legal disclosures AdSense requires.

- Load `adsbygoogle.js` through `GoogleAdsense.svelte`, gated by `PUBLIC_ADSENSE_CLIENT_ID` (mirrors the existing `GoogleAnalytics.svelte` pattern), and remove the hardcoded script from `app.html`.
- Emit `<meta name="google-adsense-account">` from `<svelte:head>` so site ownership stays verifiable from the server-rendered HTML even though the script itself is injected client-side.
- Disclose third-party advertising/analytics (Google AdSense & Analytics cookies, personalized-ad opt-out links, EEA/UK/Switzerland consent) in the privacy policy.
- Also includes a `NO-ISSUE` cleanup replacing obsolete Tailwind utility classes in `TitleScreenAnimatedTags.svelte`.

### Package Changes

None

### Screenshots

N/A — integration/config only, no visible UI change until Auto Ads is enabled in the AdSense dashboard.

### Reference

Resolves #200

### Checklist

- [x] A milestone is set
- [x] The related issues has been linked to this branch

---

Generated by Zed agent with Deepseek Flash

Reviewed-on: #301
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-23 19:19:51 +08:00
squid 3697b25403 BLOG-291 Rework the search entry point as a navbar launcher (#300)
Frontend CI / build (push) Successful in 1m14s
## Description

Rework the search entry point so users can actually find it, instead of a floating button tucked in the bottom-right corner where it gets overlooked.

- Add a navbar search launcher: a search-bar-styled button on desktop and an icon button on mobile. Both open the existing search-and-filter dialog (desktop) / drawer (mobile), keeping the keyword and label fields.
- Remove the floating action button and its now-dead wiring: delete `FilteringButton`, drop the dialog from `PostOverallPage`, and remove the unused `LabelsListedStore` provider and SSR label load on `/post`.
- Indicate active state: a filter icon inside the desktop launcher when a label filter is set, and a dot badge on the mobile launcher when a keyword or label is active.
- Focus the keyword input when the dialog opens, and add `aria-expanded` / `aria-haspopup` to the launcher plus `aria-label` / `aria-expanded` to the navigation drawer buttons.
- Unify every scrim (dialog, drawer, navigation drawer) on a blurred `bg-black/15 backdrop-blur-md` overlay.
- Compensate the fixed navbar with the scrollbar width while a dialog locks scrolling, so the page no longer shifts when the scrollbar disappears.
- Lazy-load the search dialog so its code (dialog/drawer/command, zod) no longer ships on every page. Measured initial JS: `/` and `/post` drop from 301 kB to 239 kB gzip (-20%).

## Package Changes

_No response_

## Screenshots

<!-- TODO: add screenshots of the navbar search launcher and the search dialog -->

## Reference

Resolves #291

## Checklist

- [x] A milestone is set
- [x] The related issues has been linked to this branch

---

Generated by Zed coding agent with Deepseek Flash

Reviewed-on: #300
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-23 06:09:07 +08:00
squid 3a1bd1f45d BLOG-205 Add social media share buttons (#299)
Frontend CI / build (push) Successful in 8h1m33s
## Description

Add social media sharing to the post content page.

- Extract a reusable `ShareButton` component that renders an `<a>` when a share URL is provided, or a `<button>` for click-based actions, and takes the icon through a `children` snippet.
- Add share buttons on the post page: LinkedIn, Facebook, X, and Email, plus a native share button (Web Share API) that is shown on mobile only.
- Add share URL builders to `Links` (`EMAIL_SHARE`, `FACEBOOK_SHARE`, `LINKEDIN_SHARE`, `X_SHARE`) and the related labels to `Strings`.
- Move the footer icon labels into `Strings` and add a project skill documenting the frontend strings/links conventions.

## Package Changes

_No response_

## Screenshots

<!-- TODO: add screenshots of the share buttons -->

## Reference

Resolves #205

## Checklist

- [x] A milestone is set
- [x] The related issues has been linked to this branch

---

Generated by Zed coding agent with Deepseek Flash

Reviewed-on: #299
Co-authored-by: SquidSpirit <squid@squidspirit.com>
2026-09-23 03:38:38 +08:00
squid 39e712036e BLOG-165 Adjust label text color based on background luminance for better contrast (#298)
Frontend CI / build (push) Successful in 1m39s
### Description

This PR introduces support for dark background colors on post labels.

Specifically, the following changes have been made:
1. **Luminance & Color Contrast**: Added `isDark` and `contrastingTextColor` getters in `ColorViewModel`. The color contrast detection has been improved to use the standard sRGB relative luminance formula (per WCAG 2.x standard) with a threshold of `0.179` to determine if a background is dark, ensuring much more accurate contrast adjustments than a simple YIQ calculation.
2. **Text Color Adjustment**: Sets the text color of the `PostLabel` dynamically to `#ffffff` if the background is dark, and inherits the default color if the background is light.
3. **Indicator Dot Styling**: Adjusts the background color of the small decorative dot inside `PostLabel` using a lighter variant (`lighten(0.4)`) on dark labels, and a darker variant (`darken(0.2)`) on light labels to ensure visual clarity.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #165

### Checklist

- [x] A milestone is set
- [x] The related issues has been linked to this branch

Reviewed-on: #298
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-05-23 00:31:57 +08:00
squid 745a1de78a BLOG-296 Add MCP configuration and setup instructions (#297)
Frontend CI / build (push) Successful in 58s
### Description

This PR configures and registers MCP (Model Context Protocol) servers to enhance AI-assisted development workflow, corrects a typo in the PR template, and documents the setup instructions in `README.md`.

Specifically, the following changes are introduced:
- **MCP Configuration**: Created `.agents/mcp_config.json` to register MCP servers for `gitea`, `svelte`, and `cratesio` domains.
- **Documentation**: Updated `README.md` with step-by-step instructions to set up the Go environment, install `cratesio-mcp`, and configure the Gitea access token.
- **Template Correction**: Corrected a typo in `.gitea/PULL_REQUEST_TEMPLATE.yaml`.
- **Git Ignore**: Added `.antigravitycli` directory to `.gitignore` to prevent tracking of local CLI configurations.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #296.

### Checklist

- [x] A milestone is set
- [x] The related issues has been linked to this branch

Reviewed-on: #297
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-05-22 23:42:27 +08:00
squid 815877efba BLOG-290 Migrate the frontend JS runtime to bun (#294)
Frontend CI / build (push) Successful in 1m33s
### Description

This change migrates the frontend environment from Node.js/pnpm to Bun to improve build performance and runtime efficiency. The migration includes updating CI/CD workflows, Docker configurations, and the SvelteKit adapter. Additionally, the PR introduces stricter linting rules via eslint-plugin-simple-import-sort and a global restriction on relative imports to enforce the use of path aliases ($lib/...), ensuring a cleaner and more maintainable import structure across the project. Backend environment variables were also renamed for consistency.

### Package Changes

Package | Action | Version
-- | -- | --
svelte-adapter-bun | Added | 1.0.1
eslint-plugin-simple-import-sort | Added | 13.0.0
@types/node | Added | 22.19.17
eslint-plugin-import | Removed | 2.32.0
@sveltejs/adapter-node | Removed | 5.5.4
svelte | Updated | 5.55.1 -> 5.55.5
@sentry/sveltekit | Updated | 10.47.0 -> 10.51.0
@sveltejs/kit | Updated | 2.55.0 -> 2.58.0
tailwindcss | Updated | 4.2.2 -> 4.2.4
typescript-eslint | Updated | 8.58.0 -> 8.59.1

### Screenshots

_No response_

### Reference

Resolves #290.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #294
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-05-14 17:26:39 +08:00
squid 4a2a5cb7d6 NO-ISSUE Sync main with release/0.7 (#293)
Frontend CI / build (push) Successful in 1m15s
Reviewed-on: #293
2026-04-30 13:43:16 +08:00
squid 4a368c63ca NO-ISSUE Bump version to 0.7.3 (#292)
Frontend CI / build (push) Successful in 1m15s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 13s
PR Title Check / pr-title-check (pull_request) Successful in 13s
Deployment / deployment (release) Successful in 3m26s
### Description

- As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

_No issue_

### Checklist

- [x] A milestone is set
- [ ] The related issuse has been linked to this branch

Reviewed-on: #292
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.7.3
2026-04-30 13:37:50 +08:00
squid 5129ec760a BLOG-288 Fix RSS link at footer (#289)
Frontend CI / build (push) Successful in 1m24s
### Description

- As the title

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #288.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #289
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-30 13:23:47 +08:00
squid 04667dbb51 NO-ISSUE Bump version to 0.7.2 (#287)
Frontend CI / build (push) Successful in 1m23s
Deployment / deployment (release) Successful in 26m25s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

_No issue_

### Checklist

- [x] A milestone is set
- [ ] The related issuse has been linked to this branch

Reviewed-on: #287
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.7.2
2026-04-25 04:18:17 +08:00
squid 0cd0568246 BLOG-282 Load SENTRY_AUTH_TOKEN in vite config (#286)
Frontend CI / build (push) Successful in 1m23s
### Description

#284 Cont.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #282.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #286
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-25 04:14:12 +08:00
squid cfe244a3be BLOG-282 Enhance Sentry configuration with release tracking and multi-project sourcemap uploads cont (#285)
Frontend CI / build (push) Successful in 1m23s
### Description

#284 Cont.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #282.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #285
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-25 03:51:08 +08:00
squid af06885427 BLOG-282 Enhance Sentry configuration with release tracking and multi-project sourcemap uploads (#284)
Frontend CI / build (push) Successful in 2m9s
### Description

#### Description

This pull request refactors our Sentry configuration to improve error tracking accuracy and support multi-project deployments. We've added explicit release versioning and environment tracking to both the client and server hooks. Additionally, sourcemap uploading has been decoupled from the default SvelteKit Sentry wrapper; we are now directly utilizing `@sentry/vite-plugin` to iterate over an array of projects. This ensures that sourcemaps are correctly uploaded to both the `blog-frontend` and `blog-beta-frontend` Sentry projects during the build process.

#### Key Changes

* **`frontend/src/hooks.client.ts` & `frontend/src/hooks.server.ts`**: Updated `Sentry.init()` to include explicit `release` (`App.__VERSION__`) and `environment` (`process.env.NODE_ENV || 'development'`) tracking.
* **`frontend/vite.config.ts`**:
    * Removed inline `sourceMapsUploadOptions` from `sentrySvelteKit()`.
    * Imported and configured `@sentry/vite-plugin` to map over an array of `sentryProjects` (`blog-frontend`, `blog-beta-frontend`), ensuring sourcemaps and release versions are tied to multiple target projects during Vite's build phase.

#### Testing/Review Notes

* **Build Verification:** Run a production build (`pnpm build`) and verify that the build output logs confirm sourcemap uploads to both `blog-frontend` and `blog-beta-frontend` Sentry projects.
* **Tag Verification:** Trigger a manual error in your local development environment and check the Sentry dashboard to confirm that the `environment` (e.g., "development") and `release` tags are correctly attached to the event payload.
* **Regression Check:** Ensure that client-side and server-side routing continues to function without Sentry initialization errors.

### Package Changes

* **Added:** `@sentry/vite-plugin` (`^5.2.0`) in `package.json` to handle dedicated Vite build integrations.
* **Updated:** Relevant `@sentry/*` build dependencies (e.g., `babel-plugin-component-annotate`, `bundler-plugin-core`, `rollup-plugin`) bumped to `5.2.0` in `pnpm-lock.yaml` to align with the new Vite plugin addition.

### Screenshots

_No response_

### Reference

Resolves #282.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #284
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-25 03:44:20 +08:00
squid e187df2b76 NO-ISSUE Bump version to 0.7.1 (#281)
Frontend CI / build (push) Successful in 1m17s
Deployment / deployment (release) Successful in 3m2s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

_No issue_

### Checklist

- [x] A milestone is set
- [ ] The related issuse has been linked to this branch

Reviewed-on: #281
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.7.1
2026-04-05 05:23:51 +08:00
squid 4a026fadbd BLOG-275 Update page title to use dynamic string constants in PostOverallPage (#280)
Frontend CI / build (push) Successful in 1m17s
Deployment / deployment (release) Successful in 2m35s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #275.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #280
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-05 05:08:58 +08:00
squid 9e0ad8f2c6 BLOG-274 Add title to terms page (#279)
Frontend CI / build (push) Successful in 1m16s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #274.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #279
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-05 04:52:48 +08:00
squid a499f9e9f3 BLOG-273 Make tooltip open when tapping on mobile (#278)
Frontend CI / build (push) Successful in 1m15s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #273.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #278
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-05 04:29:37 +08:00
squid 0b6e749a38 BLOG-271 Fix scroll down when dialog closing by preventing auto focus on close (#277)
Frontend CI / build (push) Successful in 1m24s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #271.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #277
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-05 04:02:12 +08:00
squid 718abe1b7f BLOG-272 Set input text size to 16px to avoid scaling on iOS device (#276)
Frontend CI / build (push) Successful in 1m18s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

![截圖 2026-04-05 02.22.01.png](/attachments/da6392c2-c68e-4de5-a6bc-982b763360ca)

### Reference

Resolves #272.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #276
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-05 02:23:47 +08:00
squid 797f99bf97 BLOG-269 Install ca-certificates in runtime and correct Redis URL environment variable (#270)
Frontend CI / build (push) Successful in 1m17s
Deployment / deployment (release) Successful in 2m37s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #269.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #270
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.7.0
2026-04-04 19:01:56 +08:00
squid 255ffbc91c NO-ISSUE Sync release/0.7 with main (#268)
Frontend CI / build (push) Successful in 1m17s
Deployment / deployment (release) Successful in 21m49s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

_No issue_

### Checklist

- [x] A milestone is set
- [ ] The related issuse has been linked to this branch

Reviewed-on: #268
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-04 17:37:02 +08:00
squid 581bcee5f5 BLOG-261 Fix Supply Sentry auth token to frontend build to enable log and sourcemap uploads (cont.) (#267)
Frontend CI / build (push) Successful in 1m18s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 14s
PR Title Check / pr-title-check (pull_request) Successful in 13s
### Description

Refer to #265.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #261.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #267
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-04 17:34:42 +08:00
squid b2b6748c48 NO-ISSUE Sync release/0.7 with main (#266)
Frontend CI / build (push) Successful in 1m18s
Deployment / deployment (release) Has been cancelled
Reviewed-on: #266
2026-04-04 17:14:41 +08:00
squid d506254955 BLOG-261 Fix Supply Sentry auth token to frontend build to enable log and sourcemap uploads (#265)
Frontend CI / build (push) Successful in 1m20s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 13s
PR Title Check / pr-title-check (pull_request) Successful in 16s
### Description

#### Summary

This resolves the issue where Sentry logs and source maps were failing to upload during the deployment pipeline (BLOG-261). The core problem was that the frontend build environment lacked the necessary authentication credentials to communicate with the Sentry API. I've updated the deployment workflow to pass the `SENTRY_AUTH_TOKEN` from our CI secrets into the Docker build context, and updated the frontend Dockerfile to securely mount and utilize this token during the build phase. A minor descriptive adjustment was also made to our pre-commit config.

#### Key Changes

* **`.gitea/workflows/deployment.yaml`**: Injected the `SENTRY_AUTH_TOKEN` into the build container's secrets configuration.
* **`frontend/Dockerfile`**: Configured secure secret mounting (`--mount=type=secret`) to read `SENTRY_AUTH_TOKEN` and expose it as an environment variable specifically during the `pnpm run build` execution.
* **`.pre-commit-config.yaml`**: Renamed the `frontend-lint` hook from "frontend lint" to "frontend lint & check" to better reflect its underlying script behavior.

#### Testing/Review Notes
* Trigger a deployment build in the CI/CD pipeline to test the workflow changes.
* Check the build logs for the frontend container; verify that the Sentry plugin successfully detects the token and uploads the sourcemaps/releases without throwing an authentication error.
* Ensure no token leakage occurs in the standard CI output logs or the final compiled Docker image layers.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #261.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #265
Co-authored-by: squid <squid@squidspirit.com>
Co-committed-by: squid <squid@squidspirit.com>
2026-04-04 17:11:02 +08:00
squid 6f14a33215 BLOG-262 Resolve ONNX build failure by migrating Docker base image to Debian (#264)
Frontend CI / build (push) Successful in 1m18s
### Description

#### Summary

This PR addresses the ONNX runtime build failures in the backend container. The core issue originates from Alpine Linux's use of `musl` libc, which causes compatibility conflicts with the pre-compiled C/C++ binaries required by the ONNX ecosystem. By migrating both the builder and runner stages to Debian-based slim images (which utilize standard `glibc`), we provide a compatible compilation environment and resolve the build breakage. Furthermore, this update adds the required environment configuration for the Qdrant vector database.

#### Key Changes

* **`backend/Dockerfile`:**
  * Switched the base builder image from `rust:1-alpine` to `rust:1-slim`.
  * Replaced the `apk` package manager logic with `apt-get` to provision `build-essential`, `libssl-dev`, and `pkg-config` for the Rust build process.
  * Updated the runner stage image from `alpine:latest` to `debian:trixie-slim` to ensure runtime compatibility with the compiled `glibc` binary.
  * Added `ENV QDRANT_URL=http://127.0.0.1:6334` to expose the vector database endpoint to the server.

#### Testing/Review Notes

* Run a clean build of the backend image locally (`docker build -t backend:test ./backend`) and confirm that the cargo build process bypasses the previous ONNX compilation errors.
* Start the resulting container and verify that the server boots successfully without missing shared library errors (`.so`).
* Ensure your local or CI test environments can reach the specified `QDRANT_URL` or override it as necessary during runtime.

### Package Changes

* **System Packages (Dockerfile):**
  * *Removed:* `build-base`, `openssl-dev`, `openssl-libs-static` (Alpine).
  * *Added:* `build-essential`, `libssl-dev`, `pkg-config` (Debian).
* **Application Dependencies:** No changes to `Cargo.toml` or `Cargo.lock`.

### Screenshots

_No response_

### Reference

Resolves #262.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #264
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-04 17:00:51 +08:00
squid b6b86702d6 NO-ISSUE Bump version to 0.7.0 for backend and frontend packages (#260)
Frontend CI / build (push) Successful in 1m19s
Deployment / deployment (release) Failing after 12m18s
### Description

As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

_No issue_

### Checklist

- [x] A milestone is set
- [ ] The related issuse has been linked to this branch

Reviewed-on: #260
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-04 04:56:04 +08:00
squid d071585c85 BLOG-253 Update dependencies, enforce path resolution, and migrate to Svelte 5 reactivity patterns (#259)
Frontend CI / build (push) Successful in 1m24s
### Description

#### Description

This PR introduces a significant update to core frontend dependencies, including Svelte, Vite, Tailwind CSS, and Sentry, to keep the tooling up to date. To comply with the updated linting rules and SvelteKit routing standards, all static and interpolated anchor links have been migrated to use the `$app/paths` `resolve` function. Additionally, component reactivity has been modernized to properly leverage Svelte 5 paradigms, replacing static `$state` assignments with lazy-evaluated functions and transitioning direct parameter reads to `$derived` data properties.

#### Key Changes

* **Routing & Path Resolution:** Imported `resolve` from `$app/paths` across navigation, layout, and dashboard components (e.g., `Footer.svelte`, `Navbar.svelte`, `PostPreview.svelte`) to wrap `href` attributes, ensuring correct base path routing. Added `/* eslint-disable svelte/no-navigation-without-resolve */` where necessary.
* **Reactivity Enhancements:** * Migrated `$state` initializations in dialog components (`EditLabelDialog.svelte`, `EditPostDialog.svelte`, `FilteringDialog.svelte`) to use functional wrappers (e.g., `$state((() => defaultValues)())`) to ensure proper evaluation.
    * Updated components and pages to utilize `$derived(data.id)` instead of extracting directly from `params`, accompanied by corresponding `id` returns in `+page.server.ts` load functions.
    * Refactored initial data population in root page components to use getter functions (`getInitialData()`) before passing them to container stores.
* **UI/Layout Updates:** * Changed container max-widths from `max-w-screen-xl` to `max-w-7xl` in `Footer.svelte`, `Navbar.svelte`, and `NotFoundPage.svelte`.
    * Added `rel="external"` attributes to all outbound links in the footer.
    * Replaced hardcoded policy link constants with dynamic parameterized routes.
* **Store Interfaces:** Formatted implementation signatures for improved readability in `postsListedStore.ts`.

#### Testing/Review Notes

* Run `pnpm install` before testing to synchronize the massive updates in `pnpm-lock.yaml`.
* Navigate through the application (especially the dashboard and policy links in the footer) to verify that `$app/paths` `resolve` accurately handles all internal routing.
* Test the `EditPostDialog` and `EditLabelDialog` components to verify that default form values initialize and persist correctly under the new functional `$state` declarations.
* Confirm external links in the footer open correctly without triggering router intercepts.

### Package Changes

* **Updated:** * `svelte` (`5.39.13` -> `5.55.1`)
    * `@sveltejs/kit` (`2.22.0` -> `2.55.0`)
    * `vite` (`7.0.4` -> `7.3.1`)
    * `tailwindcss` (`4.0.0` -> `4.2.2`)
    * `eslint` (`9.18.0` -> `9.39.4`)
    * `@sentry/sveltekit` (`10.1.0` -> `10.47.0`)
    * `typescript-eslint` (`8.20.0` -> `8.58.0`)
    * Various other `@sveltejs/*`, `@tailwindcss/*`, and minor tooling dependencies bumped to their latest compatible versions.

### Screenshots

_No response_

### Reference

Resolves #253.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #259
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-04 04:49:35 +08:00
squid 089dbe8fec BLOG-246 Implement Redis-based query embedding caching for semantic search (#257)
Frontend CI / build (push) Successful in 1m17s
### Description

#### Summary

This PR introduces a Redis-backed caching layer for search query embeddings. By caching the vector embeddings of search strings, we eliminate redundant embedding generation for repeated queries, significantly improving search latency and reducing CPU/compute overhead. Additionally, the Redis connection management has been refactored to use `deadpool-redis`, establishing a shared connection pool that now serves both the newly implemented embedding cache and the existing Actix session store.

#### Key Changes

* **Search Caching Logic:** Introduced the `SearchQueryEmbeddingCache` trait and implemented `SearchQueryEmbeddingCacheRedisImpl`. Cache keys are uniquely bound to the specific model revision to prevent vector dimension mismatches if the underlying embedding model is updated.
* **Repository Integration:** Modified `SearchRepositoryImpl` to check the Redis cache before calling the text embedding model. Cache misses trigger embedding generation and automatically populate the cache.
* **Redis Pooling:** Abstracted Redis configuration into a dedicated `RedisConfiguration` struct utilizing `deadpool_redis::Pool`.
* **Session Store Refactor:** Updated `actix-session` to use `RedisSessionStore::builder_pooled()`, migrating it to the shared Redis connection pool instead of managing its own standalone connection.
* **Configuration & Env:** Added `REDIS_SESSION_PREFIX` and `REDIS_SEARCH_QUERY_EMBEDDING_PREFIX` to `.env.example` for namespace isolation.
* **Frontend Copy:** Updated the search placeholder text in `frontend/src/lib/strings.ts` to clarify that the search uses vector comparison.

#### Testing/Review Notes

* Ensure a local Redis instance is running and accessible via `REDIS_URL`.
* Execute a semantic search query. Verify via `redis-cli` that a new cache key is generated matching the pattern configured by the prefix and model revision (e.g., `search:query_embedding:<model>:<query>`).
* Execute the same search query a second time. Verify that the search executes noticeably faster and bypasses the local embedding generation phase.
* Verify that user authentication and session states remain fully functional to ensure the `actix-session` pool refactor introduced no regressions.

### Package Changes

* **Added:** `deadpool-redis` (`0.22.1`), `deadpool` (`0.12.3`), `deadpool-runtime` (`0.1.4`)
* **Added:** `redis` (`0.32.7` with features: `aio`, `connection-manager`, `tokio-comp`)
* **Added:** `serde_json` (`1.0.145`) to `backend/Cargo.toml` and `backend/feature/search/Cargo.toml`
* **Updated:** `actix-session` (`0.11.0`) - Added the `redis-pool` feature.

### Screenshots

_No response_

### Reference

Resolves #246.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #257
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-04 03:09:02 +08:00
squid 2b59cd52ef BLOG-237 Implement AI-powered RAG search for posts using Qdrant and FastEmbed (#256)
Frontend CI / build (push) Successful in 1m17s
### Description

### Summary

This PR replaces the legacy string-matching search logic with an AI-powered semantic vector search (Retrieval-Augmented Generation) for blog posts. By integrating Qdrant as the vector database and FastEmbed for generating local text embeddings, the application now allows users to perform context-aware semantic searches or ask natural language questions to find relevant post content. The backend text processing leverages markdown splitting and tokenization to chunk post content efficiently. The frontend has been updated to provide UI hints regarding this new AI search capability, utilizing a newly added Tooltip component.

### Key Changes

* **Backend - New Search Module:** Created a new `search` workspace feature containing `SearchRepository`, `SearchVectorDbService`, and `SearchService`.
* **Backend - Post Integration:** Integrated `SearchService` into `CreatePostUseCase`, `UpdatePostUseCase`, and `GetAllPostInfoUseCase`. Post creations and updates now automatically trigger vector document indexing. Retrieving posts with a keyword query now delegates to the vector search to return contextually relevant post IDs.
* **Backend - Embeddings & Vector DB:** Added dependency injection and configurations for Qdrant (`QdrantConfiguration`) and local embeddings (`EmbeddingConfiguration`), utilizing the `BAAI/bge-large-zh-v1.5` model for optimized Chinese text embeddings.
* **Backend - Environment & Build:** Added required Qdrant and embedding environment variables to `.env.example`. Updated the `Dockerfile` to utilize parallel compilation (`cargo build --release -j2`) for faster build times.
* **Frontend - UI Components:** Implemented a new `Tooltip` component architecture using `bits-ui`.
* **Frontend - User Experience:** Updated the `FilteringDialog` to display a tooltip hint explaining the new RAG-based semantic search to users. Updated `strings.ts` with relevant placeholders and instructions.

### Testing/Review Notes

* Ensure a local Qdrant instance is running and accessible at the URL defined in your `.env` (default is `http://127.0.0.1:6334`).
* On initial backend startup, verify that the `BAAI/bge-large-zh-v1.5` embedding model downloads successfully to your designated `EMBEDDING_CACHE_DIR`.
* Create or edit an existing post in the system. Check your Qdrant dashboard/API to verify that the post's content chunks are successfully vectorized and upserted into the `blog_post` collection.
* Perform a search query using natural language in the frontend to confirm semantic retrieval returns the expected posts.
* Verify that the new tooltip renders correctly when hovering over the info icon in the search dialog.

### Package Changes

* **Backend (`Cargo.toml` / `Cargo.lock`):**
    * **Added:** `fastembed` (5.13.0), `qdrant-client` (1.17.0), `text-splitter` (0.29.3, with `markdown` and `tokenizers` features), `tokenizers` (0.22.2).
    * **Updated:** `tokio` (from 1.50.0 to 1.51.0).
* **Frontend (`package.json` / `pnpm-lock.yaml`):**
    * **Updated:** `bits-ui` (from ^2.11.5 to ^2.16.5), `@internationalized/date` (from ^3.10.0 to ^3.12.0).

### Screenshots

_No response_

### Reference

Resolves #237.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #256
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-03 20:59:02 +08:00
squid 6fc2816299 BLOG-236 Introduce empty state handling for posts (#255)
Frontend CI / build (push) Successful in 1m18s
### Description

### Summary

This pull request refactors the static `ErrorPage` component into a dynamic, reusable `NotFoundPage`. By accepting a `title` prop, the component can now handle various "not found" or empty states across the application rather than just standard HTTP 404 errors. Notably, this implementation is now utilized in the `PostOverallPage` to gracefully handle scenarios where no posts are returned (e.g., empty database or zero search filter results). Minor structural UI adjustments and centralized string constants were also added to support these updates.

### Key Changes

* **`frontend/src/lib/common/framework/ui/NotFoundPage.svelte`**:
    * Renamed from `ErrorPage.svelte`.
    * Added a `title` prop to replace hardcoded '404' strings.
    * Updated flexbox utility classes (`flex-wrap`, `shrink-0`) to improve layout responsiveness on smaller viewports.
* **`frontend/src/lib/post/framework/ui/PostOverallPage.svelte`**:
    * Introduced an `isPostsEmpty` derived state.
    * Added conditional rendering to display the `NotFoundPage` with an `EMPTY_POSTS` string when no articles are available.
* **`frontend/src/lib/strings.ts`**:
    * Added new constants: `EMPTY_POSTS` ('查無文章'), `NOT_FOUND_CODE` ('404'), and `POST` ('文章') to centralize copy.
* **`frontend/src/routes/+error.svelte` & `frontend/src/routes/dashboard/+layout.svelte`**:
    * Replaced legacy `ErrorPage` imports with `NotFoundPage`.
    * Passed `Strings.NOT_FOUND_CODE` as the required title prop to maintain previous 404 behavior.
* **`frontend/src/lib/post/framework/ui/FilteringButton.svelte`**:
    * Changed the wrapper element from a `<button>` to a `<div>`.

###Testing/Review Notes

* Navigate to an invalid URL to ensure the global `+error.svelte` routing still correctly renders the "404" page.
* Visit the main posts page (`/post`). Apply a filter (keyword or label) that is guaranteed to return zero results. Verify that the new "Empty Posts" (查無文章) view is rendered correctly and the layout does not break.
* Verify that unauthenticated access to the dashboard still triggers the standard 404 view via the layout wrapper.

### Package Changes

_No response_

### Screenshots

<video src="attachments/93d87bc9-48d0-4d50-bb29-f335ecfa0cdb" title="螢幕錄影 2026-04-02 14.58.18.mov" controls></video>

### Reference

Resolves #236.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #255
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-02 17:15:57 +08:00
squid 38510d1c8d BLOG-245 Implement dynamic terms pages and refactor Footer component (#254)
Frontend CI / build (push) Successful in 1m16s
### Description

This pull request introduces dynamic markdown-based pages for site policies and refactors the global Footer component to improve maintainability and responsive design. The hardcoded static links and UI sections within the Footer were extracted into centralized configuration classes (`Links` and `Strings`) and modular Svelte snippets. Furthermore, we established a new dynamic route `/terms/[name]` capable of serving static markdown files, specifically introducing the Privacy Policy and AI Usage Policy to the platform.

### Key Changes

* **`frontend/src/lib/common/framework/ui/Footer.svelte`:** Upgraded the script block to TypeScript. Refactored the markup using Svelte `#snippet` blocks (`desktopLayout`, `mobileLayout`, `icons`, `policies`, etc.) for cleaner component structure. Integrated centralized strings and links.
* **`frontend/src/lib/links.ts`:** Created a new centralized abstraction for external application URLs (YouTube, Email, RSS, Source Code).
* **`frontend/src/lib/strings.ts`:** Enforced `string` types on existing constants and added new centralized UI strings (`PRIVICY_POLICY`, `AI_USAGE_POLICY`, `TOC`).
* **`frontend/src/lib/post/framework/ui/PostContentPage.svelte`:** Replaced hardcoded "Table of Contents" text with the newly centralized `Strings.TOC` property.
* **`frontend/src/routes/terms/[name]/+page.ts` & `+page.svelte`:** Implemented a dynamic route parameter that fetches corresponding markdown files from the static directory and safely renders them using the existing `MarkdownRenderer` component. Throws a 404 if the markdown file is absent.
* **`frontend/static/terms/*.md`:** Added `privacy-policy.md` and `ai-usage-policy.md` assets containing the platform's official policies.

### Testing/Review Notes

* Verify the global Footer renders correctly on both mobile and desktop viewports.
* Click through the external links in the Footer (YouTube, Email, RSS, Git) to ensure they route to the appropriate targets.
* Navigate to `/terms/privacy-policy` and `/terms/ai-usage-policy` to confirm the markdown documents are fetched and rendered properly.
* Manually test an invalid terms route (e.g., `/terms/does-not-exist`) to verify the 404 error handling functions as expected.
* Open a standard post page to ensure the Table of Contents header correctly displays the localized string.

### Package Changes

_No response_

### Screenshots

|Footer|Terms|
|-|-|
|![截圖 2026-04-01 01.25.17.png](/attachments/d5362512-b5af-40f1-8acb-feb656d0cb26)|![截圖 2026-04-01 01.25.30.png](/attachments/c2f7ac20-75e5-4bfd-82e0-9499067a4b1f)|

### Reference

Resolves #245.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #254
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-04-01 01:37:00 +08:00
squid 3f4162f276 BLOG-240 Implement ETag and HTTP caching for image retrieval (#252)
Frontend CI / build (push) Successful in 1m16s
### Description

This PR introduces HTTP caching mechanisms to the image retrieval endpoint to optimize bandwidth consumption and improve client-side load times. By implementing `ETag` generation and validation alongside aggressive `Cache-Control` directives, the server can now negotiate caching with the client. If the client holds a valid cached version of the image, the server will return a `304 Not Modified` response, entirely bypassing the need to re-transmit the binary image payload.

#### Key Changes

* **`backend/feature/image/src/framework/web/get_image_by_id_handler.rs`:**
    * Modified `get_image_by_id_handler` to accept `HttpRequest` for header inspection.
    * Added conditional logic to evaluate the `If-None-Match` header against a newly generated ETag.
    * Injected `ETag` and `Cache-Control` (`public, max-age=31536000, immutable`) headers into both `200 OK` and `304 Not Modified` responses.
    * Implemented `is_not_modified` helper function to accurately parse and validate `If-None-Match` candidates, including wildcard (`*`) support.
    * Implemented `generate_etag` utilizing the `Fnv64` hashing algorithm for highly performant, non-cryptographic hashing of the image binary data.

#### Testing/Review Notes

1.  Initiate a `GET` request to retrieve an image by its ID. Confirm the response returns a `200 OK` status, includes the binary payload, and exposes both `ETag` and `Cache-Control` headers.
2.  Copy the returned `ETag` value.
3.  Initiate a secondary `GET` request for the same image ID, explicitly passing the copied ETag value within the `If-None-Match` HTTP header.
4.  Verify that the server responds with a `304 Not Modified` status code, omitting the image body payload, while still returning the correct caching headers.

### Package Changes

* **Added:** `fnv_rs` (`v0.4.4`) - Introduced for high-performance FNV-1a hashing required during ETag computation.
* **Added:** `paste` (`v1.0.15`) - Transitive dependency resolved via `fnv_rs`.

### Screenshots

![截圖 2026-03-31 02.41.56.png](/attachments/91997c97-34ec-43e9-9f9d-eeddef7d123c)

### Reference

Resolves #240.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #252
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-03-31 02:49:58 +08:00
squid ff64bba17c NO-ISSUE Sync with release/0.6 (#251)
Frontend CI / build (push) Successful in 1m18s
Reviewed-on: #251
2026-03-31 02:07:11 +08:00
squid 40e4f8387e Merge branch 'main' into release/0.6
PR Title Check / pr-title-check (pull_request) Successful in 13s
Frontend CI / build (push) Successful in 1m18s
2026-03-31 02:05:18 +08:00
squid 49df26b919 BLOG-243 Implement safe image deletion and cross-entity reference tracking (#250)
Frontend CI / build (push) Successful in 1m16s
### Description

This PR introduces the ability to delete uploaded images while ensuring data integrity by preventing the deletion of images currently referenced by any posts. It adds a new `post_image` relational table to track these references dynamically. The frontend has been updated to support deleting images from the dashboard, visually disabling the deletion action for in-use images. Additionally, we've extracted image parsing logic into a dedicated service, updated dependencies across the backend, and strengthened our pre-commit hooks to ensure deeper code validation.

#### Key Changes

* **Database Migrations**:
    * Created the `post_image` join table to establish a relationship between posts and images and track active references.
* **Backend - Image Module**:
    * Added `DeleteImageUseCase` and `delete_image_handler` (`DELETE /image/{id}`) to handle safe deletions.
    * Refactored the internal entity `ImageInfo` to `ImageMetaData` and introduced an `is_referred` boolean on API DTOs.
    * Updated `GetImageInfoUseCase` and `ListImagesUseCase` to query the `PostRepository` for reference counts, determining if an image is safe to delete.
* **Backend - Post Module**:
    * Introduced `ImageExtractor` to automatically parse Markdown content and the `preview_image_url` to extract embedded image IDs.
    * Updated `CreatePostUseCase`, `UpdatePostUseCase`, and `PostDbServiceImpl` to synchronize `post_image` relationships upon post creation/updates based on the extracted IDs.
    * Refactored label validation logic into a centralized `LabelRelationService`.
* **Frontend**:
    * Implemented `ImageDeletedStore` and the corresponding `DeleteImageUseCase`.
    * Updated `ImageOverallDashboardPage` to display a delete button, which disables automatically if `isReferred` is true.
    * Changed `previewImageUrl` types from `URL` to `string` in post DTOs, ViewModels, and UI components (`OpenGraph`, `StructuredData`) to simplify state hydration/serialization.
* **Tooling & CI**:
    * Updated `.pre-commit-config.yaml` to restrict hook executions to their specific directories (`^backend/` and `^frontend/`).
    * Added `cargo test` to `backend-check.sh` and `pnpm check` to `frontend-lint.sh`.

#### Testing/Review Notes

* **Database setup**: Run `sqlx migrate run` to apply the new `post_image` table migration.
* **Upload & Reference**: Upload an image via the dashboard. Create a new post referencing the image (either within the Markdown content or as the preview image).
* **Deletion constraint check**: Navigate to the Image Dashboard. Verify the delete button for the referenced image is disabled. Sending a direct `DELETE` request to the API should return a `400 Bad Request` with the `ReferencedImage` error.
* **Successful deletion**: Remove the image reference from the post (or delete the post). Return to the Image Dashboard and verify the image can now be successfully deleted.
* **Tooling check**: Verify that committing changes locally correctly runs `cargo test` and `pnpm check` without failures.

### Package Changes

**Backend (`Cargo.toml` / `Cargo.lock`)**:

* Updated `actix-web` from **4.12.1** to **4.13.0**
* Updated `tokio` from **1.48.0** to **1.50.0**
* Updated `chrono` from **0.4.42** to **0.4.44**
* Updated `anyhow` from **1.0.100** to **1.0.102**
* Updated `env_logger` from **0.11.8** to **0.11.10**
* Updated `futures` from **0.3.31** to **0.3.32**
* Updated `regex` from **1.12.2** to **1.12.3**
* Updated `sentry` from **0.46.0** to **0.47.0**
* Various transitive dependencies updated to reflect the new primary bumps.

### Screenshots

|Is Not Referred|Is Referred|
|-|-|
|![截圖 2026-03-31 01.00.44.png](/attachments/5ffcdc68-1bd3-49bc-abb0-670a1a0740b6)|![截圖 2026-03-31 01.00.31.png](/attachments/2d56a720-ebb8-4d17-aa20-f3a966606d45)|

### Reference

Resolves #243.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #250
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-03-31 02:04:01 +08:00
squid e3cc981344 BLOG-241 Remove unused SearchBar component and refine FilteringDialog styling (#249)
Frontend CI / build (push) Successful in 1m3s
### Description

#### Description/Summary

This pull request streamlines our frontend UI architecture by removing the deprecated `SearchBar.svelte` component, pointing toward a consolidation of search input handling. To maintain a consistent and polished user experience, the keyword input within `FilteringDialog.svelte` has been updated with refined padding and placeholder typography. Additionally, a root `.gitignore` file has been initialized to establish standard exclusions for environment configurations and OS-specific artifacts, preventing accidental commits of sensitive or unnecessary files.

#### Key Changes

* **`.gitignore`:** Added root ignore file to exclude `.env` configurations and macOS `.DS_Store` artifacts from version control.
* **`FilteringDialog.svelte`:** Enhanced the `Input` component by injecting utility classes (`px-4 py-2 text-sm placeholder:text-gray-500`) for improved spatial alignment, sizing, and placeholder legibility.
* **`SearchBar.svelte`:** Deleted the component entirely, including its localized debounce logic, state management, and DOM elements, to reduce technical debt and component duplication.

### Package Changes

_No response_

### Screenshots

![截圖 2026-03-11 14.55.28.png](/attachments/7524c9fc-d082-417d-9b64-93fc46fc8752)

### Reference

Resolves #241.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #249
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2026-03-11 14:58:57 +08:00
squid 50c1765488 BLOG-247 Update copyright year number (#248)
Frontend CI / build (push) Successful in 1m8s
Deployment / deployment (release) Successful in 11m48s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 15s
PR Title Check / pr-title-check (pull_request) Successful in 13s
### Description

Make the number sync with real year number.

### Package Changes

_No response_

### Screenshots

![image.png](/attachments/ccd2533b-8119-48cf-957b-bf86e3e6bbc3)

### Reference

Resolves #

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #248
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.6.2
2026-01-31 22:38:23 +08:00
squid ad24a18855 BLOG-238 Fix SSR error when requesting a post (#239)
Frontend CI / build (push) Successful in 1m7s
Deployment / deployment (release) Successful in 3m8s
### Description

Fix by using `Environment` attribute instead of from `window` api.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolve #238.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #239
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.6.1
2025-12-16 03:15:41 +08:00