180 Commits

Author SHA1 Message Date
39e712036e BLOG-165 Adjust label text color based on background luminance for better contrast (#298)
All checks were successful
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
745a1de78a BLOG-296 Add MCP configuration and setup instructions (#297)
All checks were successful
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
815877efba BLOG-290 Migrate the frontend JS runtime to bun (#294)
All checks were successful
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
4a2a5cb7d6 NO-ISSUE Sync main with release/0.7 (#293)
All checks were successful
Frontend CI / build (push) Successful in 1m15s
Reviewed-on: #293
2026-04-30 13:43:16 +08:00
4a368c63ca NO-ISSUE Bump version to 0.7.3 (#292)
All checks were successful
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
5129ec760a BLOG-288 Fix RSS link at footer (#289)
All checks were successful
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
04667dbb51 NO-ISSUE Bump version to 0.7.2 (#287)
All checks were successful
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
0cd0568246 BLOG-282 Load SENTRY_AUTH_TOKEN in vite config (#286)
All checks were successful
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
cfe244a3be BLOG-282 Enhance Sentry configuration with release tracking and multi-project sourcemap uploads cont (#285)
All checks were successful
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
af06885427 BLOG-282 Enhance Sentry configuration with release tracking and multi-project sourcemap uploads (#284)
All checks were successful
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
e187df2b76 NO-ISSUE Bump version to 0.7.1 (#281)
All checks were successful
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
4a026fadbd BLOG-275 Update page title to use dynamic string constants in PostOverallPage (#280)
All checks were successful
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
9e0ad8f2c6 BLOG-274 Add title to terms page (#279)
All checks were successful
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
a499f9e9f3 BLOG-273 Make tooltip open when tapping on mobile (#278)
All checks were successful
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
0b6e749a38 BLOG-271 Fix scroll down when dialog closing by preventing auto focus on close (#277)
All checks were successful
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
718abe1b7f BLOG-272 Set input text size to 16px to avoid scaling on iOS device (#276)
All checks were successful
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
797f99bf97 BLOG-269 Install ca-certificates in runtime and correct Redis URL environment variable (#270)
All checks were successful
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
255ffbc91c NO-ISSUE Sync release/0.7 with main (#268)
All checks were successful
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
581bcee5f5 BLOG-261 Fix Supply Sentry auth token to frontend build to enable log and sourcemap uploads (cont.) (#267)
All checks were successful
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
b2b6748c48 NO-ISSUE Sync release/0.7 with main (#266)
Some checks failed
Frontend CI / build (push) Successful in 1m18s
Deployment / deployment (release) Has been cancelled
Reviewed-on: #266
2026-04-04 17:14:41 +08:00
d506254955 BLOG-261 Fix Supply Sentry auth token to frontend build to enable log and sourcemap uploads (#265)
All checks were successful
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
6f14a33215 BLOG-262 Resolve ONNX build failure by migrating Docker base image to Debian (#264)
All checks were successful
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
b6b86702d6 NO-ISSUE Bump version to 0.7.0 for backend and frontend packages (#260)
Some checks failed
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
d071585c85 BLOG-253 Update dependencies, enforce path resolution, and migrate to Svelte 5 reactivity patterns (#259)
All checks were successful
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
089dbe8fec BLOG-246 Implement Redis-based query embedding caching for semantic search (#257)
All checks were successful
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
2b59cd52ef BLOG-237 Implement AI-powered RAG search for posts using Qdrant and FastEmbed (#256)
All checks were successful
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
6fc2816299 BLOG-236 Introduce empty state handling for posts (#255)
All checks were successful
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
38510d1c8d BLOG-245 Implement dynamic terms pages and refactor Footer component (#254)
All checks were successful
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
3f4162f276 BLOG-240 Implement ETag and HTTP caching for image retrieval (#252)
All checks were successful
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
ff64bba17c NO-ISSUE Sync with release/0.6 (#251)
All checks were successful
Frontend CI / build (push) Successful in 1m18s
Reviewed-on: #251
2026-03-31 02:07:11 +08:00
40e4f8387e Merge branch 'main' into release/0.6
All checks were successful
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
49df26b919 BLOG-243 Implement safe image deletion and cross-entity reference tracking (#250)
All checks were successful
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
e3cc981344 BLOG-241 Remove unused SearchBar component and refine FilteringDialog styling (#249)
All checks were successful
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
50c1765488 BLOG-247 Update copyright year number (#248)
All checks were successful
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
ad24a18855 BLOG-238 Fix SSR error when requesting a post (#239)
All checks were successful
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
002da33a89 NO-ISSUE Update version to 0.6.0 (#234)
All checks were successful
Frontend CI / build (push) Successful in 1m8s
Deployment / deployment (release) Successful in 2m22s
### 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: #234
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.6.0
2025-12-16 02:19:32 +08:00
f6fc8b24d0 NO-ISSUE Sync main with release/0.5 (#233)
All checks were successful
Frontend CI / build (push) Successful in 1m7s
Reviewed-on: #233
2025-12-16 02:06:28 +08:00
47b1e289a3 Merge branch 'main' into release/0.5
All checks were successful
Frontend CI / build (push) Successful in 1m13s
PR Title Check / pr-title-check (pull_request) Successful in 15s
2025-12-16 02:04:35 +08:00
ee838f0fc0 BLOG-197 Upgrade dependencies (#232)
All checks were successful
Frontend CI / build (push) Successful in 1m6s
### Description

There isn't any breaking change!!

### Package Changes

#### Backend

```toml
actix-session = { version = "0.11.0", features = ["redis-session"] }
actix-web = "4.12.1"
anyhow = "1.0.100"
async-trait = "0.1.89"
chrono = "0.4.42"
log = "0.4.29"
percent-encoding = "2.3.2"
regex = "1.12.2"
sentry = { version = "0.46.0", features = ["actix", "anyhow"] }
serde = { version = "1.0.228", features = ["derive"] }
sqlx = { version = "0.8.6", features = [
    "chrono",
    "macros",
    "postgres",
    "runtime-tokio-rustls",
] }
tokio = { version = "1.48.0", features = ["full"] }
```

#### Frontend

```json
{
        "@eslint/compat": "^2.0.0",
		"@lucide/svelte": "^0.561.0",
		"@sveltejs/adapter-auto": "^7.0.0",
}
```

### Screenshots

_No response_

### Reference

Resolve #197.

### Checklist

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

Reviewed-on: #232
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-12-16 02:02:49 +08:00
74c5069894 BLOG-199 Filter posts by label (#231)
All checks were successful
Frontend CI / build (push) Successful in 1m6s
### Description

**Description**

This PR introduces a comprehensive filtering and search mechanism for the blog post list. It replaces the simple search bar with a robust filtering dialog that supports both keyword search and label selection. To enhance the mobile user experience, the `vaul-svelte` library has been integrated to provide native-feeling drawer interactions. Additionally, the filtering state is now synchronized with URL query parameters, allowing for shareable filtered views.

**Key Changes**

* **Dependencies:**
    * Added `vaul-svelte` (v1.0.0-next.7) for handling mobile drawer interactions.
* **UI Components:**
    * Implemented a set of generic `Drawer` components (`drawer-content`, `drawer-overlay`, etc.) wrapping `vaul-svelte`.
    * Renamed the existing application navigation drawer from `Drawer.svelte` to `NavigationDrawer.svelte` to avoid namespace conflicts.
    * Updated `Footer.svelte` to use the semantic `<footer>` tag.
* **Filtering Feature:**
    * **FilteringDialog:** Created a responsive form that renders as a `Dialog` on desktop and a `Drawer` on mobile. Includes inputs for keywords and a combobox for label selection.
    * **FilteringButton:** Added a floating action button (FAB) that adjusts its position relative to the footer visibility. It displays a "rainbow ring" animation when active filters are applied.
    * **PostOverallPage:** Replaced `SearchBar` with `FilteringDialog`. Implemented logic to sync form state with URL search parameters (`keyword`, `label_id`).
* **Data & Logic:**
    * Updated `routes/post/+page.server.ts` and `+page.svelte` to parse query parameters and hydrate both post and label stores server-side.
    * Added `setData` method to `PostsListedStore`.
    * Updated `PostContentPage` to link post tags directly to the filtered list view.

**Testing & Review Notes**

1.  **Functionality:**
    * Navigate to the post list page. Verify that clicking the new floating search button opens the filtering modal.
    * Test filtering by keyword, label, or both. Ensure the list updates correctly.
    * Verify that applying a filter updates the URL (e.g., `?keyword=test&label_id=1`). Refreshing the page should persist the filtered state.
2.  **Responsiveness:**
    * **Desktop:** Ensure the filter opens as a centered Dialog.
    * **Mobile:** Ensure the filter opens as a bottom-sheet Drawer.
3.  **Regression:**
    * Verify that the main navigation menu (`NavigationDrawer`) still functions correctly after the rename.
    * Check that the footer layout remains stable.

### Package Changes

```
"vaul-svelte": "1.0.0-next.7",
```

### Screenshots

|Scenario|Mobile|Desktop|
|-|-|-|
|Inactive|![localhost_5173_post(iPhone 12 Pro).png](/attachments/ecd5110c-fb12-4322-8c29-fadd15d1210a)|<img width="1680" alt="截圖 2025-12-16 01.45.58.png" src="attachments/4b478cda-33d4-4a9a-beb4-9cd4e5795350">|
|Active|![localhost_5173_post(iPhone 12 Pro) (1).png](/attachments/7dc2f543-c923-4f62-be42-b29680891126)|<img width="1680" alt="截圖 2025-12-16 01.43.57.png" src="attachments/30a02bbb-7915-484b-95cc-371e951c666d">|
|Configuration|![localhost_5173_post(iPhone 12 Pro) (2).png](/attachments/cb1f57e8-7fe3-4827-8027-2607dad6dc39)|<img width="1680" alt="截圖 2025-12-16 01.43.34.png" src="attachments/17fa73b6-5f87-4781-987b-484db5de7e91">|

### Reference

Resolves #199.

### Checklist

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

Reviewed-on: #231
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-12-16 01:47:56 +08:00
805807909d BLOG-227 RSS feed entry and generating script (#230)
All checks were successful
Frontend CI / build (push) Successful in 1m13s
### Description

#### Summary

This PR introduces a new `script/toolbox` directory containing a Python-based utility service designed to handle background tasks, specifically generating an RSS 2.0 feed (`generate_rss.py`). It includes the necessary Docker infrastructure to build and deploy this toolbox alongside the existing services.

Additionally, this change refactors the location of maintenance shell scripts to `script/pre-commit/` for better organization and optimizes the CI/CD deployment workflow by implementing Docker layer caching and removing redundant frontend build steps.

#### Key Changes

##### Toolbox & RSS

  * **New Service:** Added `script/toolbox/` containing a Python 3.14 environment, `Dockerfile`, and `requirements.txt`.
  * **RSS Logic:** Implemented `generate_rss.py` to fetch published posts from the PostgreSQL database, scrape site metadata, and generate a `feed.xml` file.
  * **Documentation:** Added `README.md` and `.env.example` for the toolbox service.

##### CI/CD & Infrastructure

  * **Workflow Update (`deployment.yaml`):**
      * Added a new job to build and push the `toolbox` Docker image.
      * Enabled `cache-from` and `cache-to` (registry caching) for Frontend, Backend, and Toolbox images to speed up build times.
  * **Frontend CI:** Removed the redundant `pnpm run build` step from `frontend-ci.yaml`.

##### Refactoring & UI

  * **Script Reorganization:** Moved `backend-check.sh`, `frontend-lint.sh`, and `sqlx-prepare.sh` into `script/pre-commit/` and updated their internal path resolution.
  * **Pre-commit:** Updated `.pre-commit-config.yaml` to reflect the new script locations.
  * **Frontend UI:** Added an RSS icon link to `Footer.svelte` pointing to the generated feed.

#### Testing & Review Notes

1.  **Toolbox Build:** Verify the `toolbox` image builds successfully:
    ```bash
    cd script/toolbox
    docker build -t blog-toolbox .
    ```
2.  **RSS Generation:**
      * Run the container locally with correct `.env` variables.
      * Execute `python generate_rss.py` inside the container.
      * Verify the output `feed.xml` validates against RSS 2.0 standards.
3.  **Pre-commit Hooks:** Run `pre-commit run --all-files` to ensure the path refactoring hasn't broken local linting/checking.
4.  **Deployment:** Monitor the next Gitea Action run to confirm registry caching is functioning correctly.

### Package Changes

```
python-dotenv==1.2.1
psycopg2-binary==2.9.11
requests==2.32.5
```

### Screenshots

> Screenshot at Feeder app on Android

<img src="/attachments/d791ac12-307c-43a7-a146-c423f2600c02" width="200px"/>

### Reference

Resolve #227.

### Checklist

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

Reviewed-on: #230
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-12-15 03:13:40 +08:00
b441416fc4 BLOG-198 Searching posts by title (#229)
All checks were successful
Frontend CI / build (push) Successful in 1m41s
### Description

## 📝 Summary
This PR implements the ability to search/filter posts by keyword. It introduces a search bar on the frontend and passes the search query through the architectural layers to the backend, where a basic title-based filtering is currently applied.

## 🛠️ Changes

### Backend (`backend/feature/post`)
- **API & Controller**: Updated `PostController` and `PostQueryDto` to accept an optional `keyword` parameter.
- **Use Case**: Updated `GetAllPostInfoUseCase` to propagate the `keyword` to the repository.
- **Repository**:
  - Updated `PostRepository` interface and implementation.
  - **Logic**: Implemented a temporary in-memory filter that checks if the `post.title` contains the keyword.
  - *Note*: Added a TODO to replace this string matching with vector/embedding search in the future.

### Frontend (`frontend/src/lib/post`)
- **UI Components**:
  - Created a new component `SearchBar.svelte` with:
    - Input debouncing (1000ms).
    - Clear button functionality.
    - Hover/Focus styling effects.
  - Integrated `SearchBar` into `PostOverallPage.svelte`.
- **State Management**: Updated `PostsListedStore` to handle the `keyword` trigger.
- **Gateway/API**: Updated `PostListQueryDto` and `PostRepositoryImpl` to append the `keyword` to the URL search parameters when calling the backend.

## ⚠️ Technical Notes
- **Search Scope**: Currently, the search is **strictly limited to the post title**. The logic is explicitly filtering: `post.title.contains(keyword)`.
- **Performance**:
  - The frontend implements a **1-second debounce** on the input to reduce unnecessary API calls.
  - The backend filtering happens *after* fetching post info from the DB service (in `map` logic). As indicated in the code comments, this is a provisional solution pending a switch to embedding-based search.

### Package Changes

_No response_

### Screenshots

![Screenshot From 2025-12-13 23-27-46.png](/attachments/ec505c72-c72f-4102-b7fb-1125d07ffbd3)

![image.png](/attachments/9a1c3686-a5d6-4fe5-a341-7daaf53e8142)

### Reference

Resolve #198.

### Checklist

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

Reviewed-on: #229
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-12-13 23:37:25 +08:00
65ed7c170b BLOG-158 Implement image gallery dashboard (#228)
All checks were successful
Frontend CI / build (push) Successful in 1m37s
### Description

## Feature: Implement Image Gallery and Detail Views

### Summary

This PR introduces full functionality for listing images and viewing image details. It extends the backend to support retrieving image metadata without binary data and updates the frontend to display a gallery grid and a dedicated detail page. Additionally, it enforces absolute imports in the frontend codebase via updated ESLint rules.

### 🏗️ Backend Changes (`/backend`)

**New Endpoints**

  * **`GET /image`**: Retrieves a list of all non-deleted images (metadata only: ID and MIME type).
  * **`GET /image/{id}/info`**: Retrieves specific metadata for an image ID without fetching the binary blob.

**Architecture & Refactoring**

  * **Domain Entities**: Split the concept of an image into `ImageInfo` (metadata) and `Image` (contains `ImageInfo` + binary data). This allows listing images efficiently without loading heavy binary data from storage.
  * **Use Cases**: Added `ListImagesUseCase` and `GetImageInfoUseCase`.
  * **Repository**: Updated `ImageRepository` and `ImageDbService` to support listing and metadata retrieval using new SQLx queries.
  * **API**: Updated `openapi` documentation to include the new routes.

### 🖥️ Frontend Changes (`/frontend`)

**UI & Components**

  * **Image Dashboard (`/dashboard/image`)**:
      * Implemented a grid layout gallery.
      * Added hover effects showing Image ID and MIME type.
      * Added quick actions: **Copy URL** and **View Details**.
  * **Image Details (`/dashboard/image/[id]`)**:
      * Created a dedicated page to view full-size images.
      * Displays a table with ID, MIME Type, and URL (with copy functionality).
  * **Utilities**: Added `copyToClipboard.ts` utility with Toast notification integration.

**State Management**

  * Added `ImagesListedStore` to handle fetching and state for the gallery.
  * Added `ImageLoadedStore` to handle fetching state for the detail view.
  * Updated `Container` to inject these new dependencies.

**Developer Experience (DX)**

  * **ESLint**: Added `eslint-plugin-import`.
  * **Rules**: Enforced absolute imports (`import/no-relative-parent-imports`) to maintain a cleaner project structure (CSS files are exempted).

-----

### 🔍 Technical Details

#### 1\. Optimization: Splitting Metadata vs. Data

To prevent high memory usage and latency when listing images, the backend now distinguishes between fetching *info* and fetching *blobs*.

#### 2\. ESLint Configuration

New rules now prevent `../../` spaghetti imports in TypeScript files.

```javascript
// Invalid
import { foo } from '../../common/foo';

// Valid
import { foo } from '$lib/common/foo';
```

-----

### 🧪 Testing

1.  **Gallery View**:
      * Navigate to `/dashboard/image`.
      * Verify that previously uploaded images appear in the grid.
      * Hover over an image and click the "Copy" icon; verify the URL is in your clipboard.
2.  **Detail View**:
      * Click the "Eye" icon on an image card.
      * Verify redirection to `/dashboard/image/[id]`.
      * Verify the image loads and metadata matches.
3.  **Upload**:
      * Upload a new image via the dashboard.
      * Verify the list updates automatically upon success.

### Package Changes

"eslint-plugin-import": "^2.32.0",

### Screenshots

![image.png](/attachments/41fe9d43-f091-4132-82be-869047c55d2e)

![image.png](/attachments/6d9d1145-d88f-43ae-a9d7-24c64cdeedf5)

### Reference

Resolve #158.

### Checklist

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

Reviewed-on: #228
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-12-12 23:19:12 +08:00
f1c823fe59 NO-ISSUE Update frontend version to 0.5.6 (#225)
All checks were successful
Frontend CI / build (push) Successful in 1m26s
Deployment / deployment (release) Successful in 14m58s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 16s
PR Title Check / pr-title-check (pull_request) Successful in 15s
### 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: #225
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.5.6
2025-11-13 00:57:13 +08:00
c14ce177b4 BLOG-223 Enhance markdown parsing (#224)
All checks were successful
Frontend CI / build (push) Successful in 1m38s
### Description

- Implemented by `markdown-it-attrs`
- For usage, please refer to https://github.com/arve0/markdown-it-attrs

Example:

```markdown
| 頁面名稱與說明 {style="text-wrap: nowrap"} | 螢幕錄影 {style="text-wrap: nowrap"} |
| - | - |
| **歡迎頁面**:這邊我們有做一點小巧思,讓背景有一個 **流光** 效果,給人一種香火鼎盛的感覺。 {style="vertical-align: top"} | ![歡迎頁面](https://squidspirit.com/api/image/28){style="display: block; margin: 0"} |
| **搖一搖抽籤**:透過加速度感應的方式,以搖動手機模擬實際抽籤的動作,觸發抽籤動畫。 {style="vertical-align: top"} | ![搖一搖抽籤](https://squidspirit.com/api/image/29){style="display: block; margin: 0"} |
```

### Package Changes

```json
{
  "@types/markdown-it-attrs": "^4.1.3",
  "markdown-it-attrs": "^4.3.1",
}
```

### Screenshots

![截圖 2025-11-13 00.25.51.png](/attachments/151ede71-93f2-4f56-b51f-819be8060317)

### Reference

Resolves #223.

Also see: https://github.com/arve0/markdown-it-attrs

### Checklist

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

Reviewed-on: #224
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-13 00:52:44 +08:00
f4f3673607 BLOG-202 Add open graph and structured data for home page and posts overall page (#222)
All checks were successful
Frontend CI / build (push) Successful in 1m30s
### Description

- Make structured data be able to be passed different type of data.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #202.

### Checklist

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

Reviewed-on: #222
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-11 05:14:30 +08:00
7661e35092 BLOG-219 Improve favicon for light and dark mode (#221)
All checks were successful
Frontend CI / build (push) Successful in 1m26s
### Description

Merge [logo-light.svg](/frontend/static/icon/logo-light.svg) and [logo-dark.svg](/frontend/static/icon/logo-light.svg) as a single [favicon.svg](/frontend/static/favicon.svg)

### Package Changes

_No response_

### Screenshots

|Dark Mode|Light Mode|
|-|-|
|![image.png](/attachments/f21bfa60-cf8b-4331-935f-752cfe307385)|![image.png](/attachments/a9e20b3a-10b8-4f37-a577-5be7c4648dd3)|

### Reference

Resolves #219.

https://gemini.google.com/share/75ba68b088e3

### Checklist

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

Reviewed-on: #221
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-11 01:46:47 +08:00
3aa8fd87d3 NO-ISSUE Sync main with release/0.5 (#220)
All checks were successful
Frontend CI / build (push) Successful in 1m38s
Reviewed-on: #220
2025-11-11 00:27:15 +08:00
38afb9edab Merge branch 'main' into release/0.5
All checks were successful
Frontend CI / build (push) Successful in 1m40s
PR Title Check / pr-title-check (pull_request) Successful in 16s
2025-11-11 00:24:51 +08:00
3b966d2c54 NO-ISSUE Update frontend version to 0.5.5 (#218)
All checks were successful
Frontend CI / build (push) Successful in 1m24s
Deployment / deployment (release) Successful in 13m14s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 16s
PR Title Check / pr-title-check (pull_request) Successful in 15s
### 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: #218
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
v0.5.5
2025-11-04 04:29:39 +08:00