100 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
2025-12-16 03:15:41 +08:00
squid 002da33a89 NO-ISSUE Update version to 0.6.0 (#234)
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>
2025-12-16 02:19:32 +08:00
squid f6fc8b24d0 NO-ISSUE Sync main with release/0.5 (#233)
Frontend CI / build (push) Successful in 1m7s
Reviewed-on: #233
2025-12-16 02:06:28 +08:00
squid 47b1e289a3 Merge branch 'main' into release/0.5
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
squid ee838f0fc0 BLOG-197 Upgrade dependencies (#232)
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
squid 74c5069894 BLOG-199 Filter posts by label (#231)
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
squid 805807909d BLOG-227 RSS feed entry and generating script (#230)
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
squid b441416fc4 BLOG-198 Searching posts by title (#229)
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
squid 65ed7c170b BLOG-158 Implement image gallery dashboard (#228)
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
squid f1c823fe59 NO-ISSUE Update frontend version to 0.5.6 (#225)
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>
2025-11-13 00:57:13 +08:00
squid c14ce177b4 BLOG-223 Enhance markdown parsing (#224)
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
squid f4f3673607 BLOG-202 Add open graph and structured data for home page and posts overall page (#222)
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
squid 7661e35092 BLOG-219 Improve favicon for light and dark mode (#221)
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
squid 3aa8fd87d3 NO-ISSUE Sync main with release/0.5 (#220)
Frontend CI / build (push) Successful in 1m38s
Reviewed-on: #220
2025-11-11 00:27:15 +08:00
squid 38afb9edab Merge branch 'main' into release/0.5
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
squid 3b966d2c54 NO-ISSUE Update frontend version to 0.5.5 (#218)
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>
2025-11-04 04:29:39 +08:00
squid 1018603c5a BLOG-216 Revert BLOG-207 Remove unnecessary loads in onMount (#217)
Frontend CI / build (push) Successful in 1m25s
### Description

- As the title

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #216.

### Checklist

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

Reviewed-on: #217
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-04 04:27:04 +08:00
squid 819cf5bada BLOG-210 Using composition instead of inheritance on enhanced date (#215)
Frontend CI / build (push) Successful in 1m38s
Deployment / deployment (release) Successful in 5m10s
### Description

- As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #210.

### Checklist

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

Reviewed-on: #215
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-04 03:41:03 +08:00
squid 5b92b25d31 BLOG-210 Fix regex to replace all link with modulepreload (#214)
Frontend CI / build (push) Successful in 1m24s
Deployment / deployment (release) Successful in 13m11s
### Description

- As the title

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #210.

### Checklist

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

Reviewed-on: #214
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-04 00:44:32 +08:00
squid 166d41826c BLOG-210 Blocking script for google bot (#213)
Frontend CI / build (push) Successful in 1m25s
Deployment / deployment (release) Successful in 13m10s
### Description

- Using dynamic rendering

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #210.

### Checklist

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

Reviewed-on: #213
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-04 00:07:38 +08:00
squid 5b60cb9fc5 NO-ISSUE Update frontend version to 0.5.4 (#212)
Frontend CI / build (push) Successful in 1m39s
Deployment / deployment (release) Successful in 5m24s
### 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: #212
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-03 21:50:24 +08:00
squid 7950a19319 BLOG-210 Fix headless head element cannot be rendered (#211)
Frontend CI / build (push) Successful in 1m38s
### Description

- Move all `<svelte:head>` into +page.svelte component
- Testing with selenium `--headless` option

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #210.

### Checklist

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

Reviewed-on: #211
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-03 21:45:46 +08:00
squid 6fb2746c6e NO-ISSUE Update frontend version to 0.5.3 (#209)
Frontend CI / build (push) Successful in 1m38s
Deployment / deployment (release) Successful in 13m12s
### 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: #209
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-03 14:43:15 +08:00
squid 4fcc572cd1 BLOG-207 Remove unnecessary loads in onMount (#208)
Frontend CI / build (push) Successful in 1m37s
### Description

- As the title

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #207.

### Checklist

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

Reviewed-on: #208
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-11-03 14:37:12 +08:00
squid 686d3b0d73 NO-ISSUE Sync main with release/0.5 (#206)
Frontend CI / build (push) Successful in 1m39s
Deployment / deployment (release) Successful in 5m24s
Reviewed-on: #206
2025-10-27 10:06:07 +08:00
squid afc7763a8f Merge branch 'main' into release/0.5
Frontend CI / build (push) Successful in 1m48s
PR Title Check / pr-title-check (pull_request) Successful in 16s
2025-10-27 10:03:42 +08:00
squid 1c759d9388 NO-ISSUE Update frontend version to 0.5.2 (#204)
Frontend CI / build (push) Successful in 1m41s
Deployment / deployment (release) Successful in 13m43s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 17s
PR Title Check / pr-title-check (pull_request) Successful in 16s
### 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: #204
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-24 20:46:40 +08:00
squid c6d72ed3ba BLOG-201 Fix structured data not found issue (#203)
Frontend CI / build (push) Successful in 1m41s
### Description

- Place json-ld script into <svelte:head> section

### Package Changes

_No response_

### Screenshots

![截圖 2025-10-24 20.31.13.png](/attachments/e085b8b3-eaf4-4a95-ada1-6f273f72ca8c)

### Reference

Resolves #201.

### Checklist

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

Reviewed-on: #203
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-24 20:34:57 +08:00
squid bc0aff36c2 NO-ISSUE Update frontend version to 0.5.1 (#196)
Frontend CI / build (push) Successful in 1m41s
Deployment / deployment (release) Successful in 13m21s
### 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: #196
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-24 09:10:36 +08:00
squid 4bf09d9929 BLOG-194 Fix drawer close behavior by resetting scroll position (#195)
Frontend CI / build (push) Has been cancelled
### Description

- As the title.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #194.

### Checklist

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

Reviewed-on: #195
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-24 09:05:38 +08:00
squid 3957a8e96b NO-ISSUE Sync main with release/0.5 (#192)
Frontend CI / build (push) Successful in 1m45s
Reviewed-on: #192
2025-10-24 00:05:05 +08:00
squid 386be42762 NO-ISSUE Update project version to 0.5.0 (#191)
Frontend CI / build (push) Successful in 1m27s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 13s
PR Title Check / pr-title-check (pull_request) Successful in 12s
Deployment / deployment (release) Successful in 15m8s
### 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: #191
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-23 23:47:11 +08:00
squid 9c063657b8 BLOG-163 Fix dockerfile SecretsUsedInArgOrEnv issue (#189)
Frontend CI / build (push) Successful in 1m44s
Deployment / deployment (release) Has been cancelled
### Description

- Fix by removing unused environment variables from backend and frontend Dockerfiles. They have been set on hosting machine.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #163.

### Checklist

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

Reviewed-on: #189
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-23 23:32:54 +08:00
squid 495af12b36 BLOG-187 Fix labels overflow issue on post content page (#188)
Frontend CI / build (push) Successful in 1m27s
### Description

- Fix by adding `flex-wrap` on the lablels div

### Package Changes

_No response_

### Screenshots

|Original|Fixed|
|-|-|
|![](https://git.squidspirit.com/squid/blog/attachments/fc8a3c06-2fb1-4a73-ae89-c7c2857fbbcc)|![截圖 2025-10-23 22.59.13.png](/attachments/a8e17dbc-a97f-42f5-b50e-3228adef7c25)|

### Reference

Resolves #187.

### Checklist

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

Reviewed-on: #188
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-23 23:12:48 +08:00
squid d1a751d51f BLOG-161 Add app drawer on mobile devices (#186)
Frontend CI / build (push) Successful in 1m26s
### Description

- Enhance navbar hiding behavior
  - Hide when scrolling down
  - Show when scrolling up
- Add drawer on mobile device to show action links and toc
- Improve toc size on desktop
- Fix unsafe html tag

### Package Changes

_No response_

### Screenshots

|Scenario|Screenshot|
|-|-|
|Toggling drawer on home page|<video src="attachments/96879052-cdd5-46bf-be23-352bd04118b2" title="螢幕錄影 2025-10-23 22.28.30.mov" controls></video>|
|Click links on drawer|<video src="attachments/bb574a5d-0307-4b99-abec-5741a4ef16b1" title="螢幕錄影 2025-10-23 22.29.43.mov" controls></video>|
|Interating with toc on drawer|<video src="attachments/96cb8aa4-eeb3-498c-b5af-7817d7f124c1" title="螢幕錄影 2025-10-23 22.30.38.mov" controls></video>|
|Navbar auto hiding|<video src="attachments/ca6ff6ff-e755-4f6e-9e20-28b9060de6f1" title="螢幕錄影 2025-10-23 22.31.34.mov" controls></video>|

### Reference

Resolves #161.

### Checklist

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

Reviewed-on: #186
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-23 22:35:49 +08:00
squid e814dc85c1 BLOG-151 Enhance label operation in post editor (#185)
Frontend CI / build (push) Successful in 1m26s
### Description

- It supports the following operations through UI:
  - Search and add a label
  - Remove a label
  - Move the label previous or next
- Preserve the original input element for restoring

### Package Changes

_No response_

### Screenshots

<video src="attachments/95f34d52-b170-4782-8067-915cd757c9be" title="螢幕錄影 2025-10-23 05.14.53.mov" controls></video>

### Reference

Resolves #151.

### Checklist

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

Reviewed-on: #185
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-23 05:22:52 +08:00
squid 8761b4121a BLOG-183 Add hover effect when hovering on post preview (#184)
Frontend CI / build (push) Successful in 1m30s
### Description

- When hovering:

  - Scale 1.02x
  - Image shadow

### Package Changes

_No response_

### Screenshots

<video src="attachments/f3deae1c-c7b6-4d30-bec0-1186dc458f6a" title="螢幕錄影 2025-10-23 01.13.47.mov" controls></video>

### Reference

Resolves #183.

### Checklist

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

Reviewed-on: #184
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-23 01:16:49 +08:00
squid 30207d7360 BLOG-181 Refactor backend for input struct type (#182)
Frontend CI / build (push) Successful in 1m29s
### Description

Change the input data structure of each back-end feature (auth, image, label, post) from using Domain Entity to using a dedicated Params structure, and move errors from the application layer to the domain layer.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #181.

### Checklist

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

Reviewed-on: #182
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-21 01:56:03 +08:00
squid 307d9d06e4 BLOG-168 Code block syntax highlight (#179)
Frontend CI / build (push) Successful in 1m27s
### Description

- Using `highlight.js` for highlighting
- Allow `class` attribute for `<pre>` and `<span>` in `sanitizeHtml`

### Package Changes

```json
{
  "highlight.js": "^11.11.1"
}
```

### Screenshots

|Plain Text|Rust|
|-|-|
|![截圖 2025-10-20 20.23.06.png](/attachments/b4dddc3b-212c-49de-9666-ba769b60f8ac)|![截圖 2025-10-20 20.22.56.png](/attachments/40bbc71b-371d-48ae-8049-4a90fd92bc7f)|

### Reference

Resolves #168.

### Checklist

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

Reviewed-on: #179
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-20 20:27:58 +08:00
squid a5abc12b51 BLOG-172 Add open graph metadata on post content page (#178)
Frontend CI / build (push) Successful in 1m28s
### Description

> [!NOTE]
> New frontend environment: `PUBLIC_APP_BASE_URL`, which is frontend url.

### Package Changes

_No response_

### Screenshots

https://ogcheck.com/zh-hant

![截圖 2025-10-20 19.26.28.png](/attachments/bcf45e4b-99fb-4237-b9e3-0c39afeb5d83)

### Reference

Resolves #172.

See also: https://ogp.me/

### Checklist

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

Reviewed-on: #178
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-20 19:35:55 +08:00
squid f51c6610c4 BLOG-171 Order posts from latest to oldest (#177)
Frontend CI / build (push) Successful in 1m43s
### Description

Ordering logic:

1. Unpublished latest updated post (admin only)
2. ...
3. Unpublished oldest updated post (admin only)
4. Latest post
5. ...
6. Oldest post

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #171.

### Checklist

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

Reviewed-on: #177
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-20 18:41:09 +08:00
squid 1b94ad91f5 NO-ISSUE Merged from release/0.4 (#176)
Frontend CI / build (push) Successful in 1m43s
Reviewed-on: #176
2025-10-20 18:29:14 +08:00
squid aec6c8fc9b Merge branch 'main' into release/0.4
PR Title Check / pr-title-check (pull_request) Successful in 13s
Frontend CI / build (push) Successful in 1m43s
2025-10-20 18:27:12 +08:00
squid 6d0780126e BLOG-173 Image preview in post dashboard (#175)
Frontend CI / build (push) Successful in 1m42s
### Description

- Using shadcn hover card to implement.

### Package Changes

_No response_

### Screenshots

![截圖 2025-10-20 18.19.46.png](/attachments/544daccd-e485-4fba-89c6-ad5e0ffc06fe)

### Reference

Resolves #173, and also fixes #164.

### Checklist

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

Reviewed-on: #175
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-20 18:21:21 +08:00
squid 799eafaff7 BLOG-166 Fix image rendering on post preview (#174)
Frontend CI / build (push) Successful in 1m30s
### Description

- Fix the issue by adding `w-full`, `h-full`, and `object-cover`

### Package Changes

```json
{
  "prettier-plugin-tailwindcss": "^0.7.1"
}
```

### Screenshots

|Original Image|Preview|
|-|-|
|![截圖 2025-10-20 17.47.35.png](/attachments/a21373d2-5a38-48aa-a6e2-64aa056919d9)|![截圖 2025-10-20 17.52.59.png](/attachments/d3766376-ac1a-4b73-ad9f-b71f1c521378)|

### Reference

Resolves #173.

### Checklist

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

Reviewed-on: #174
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-20 17:58:03 +08:00
squid 82479bce24 BLOG-159 Reorder the page title (#170)
Frontend CI / build (push) Successful in 1m22s
### Description

- Current: ```<title>{content title} - {app title}</title>```

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #169.

### Checklist

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

Reviewed-on: #170
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-18 05:19:51 +08:00
squid cbc0082557 NO-ISSUE Update project version to 0.4.0 (#162)
Frontend CI / build (push) Successful in 1m21s
Deployment / deployment (release) Successful in 15m7s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 13s
PR Title Check / pr-title-check (pull_request) Successful in 14s
### Description

- As the title

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

_No Response_

### Checklist

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

Reviewed-on: #162
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-17 20:23:58 +08:00
squid de8a4cc430 BLOG-159 Fix truncating text style error on ToC (#160)
Frontend CI / build (push) Successful in 1m27s
Deployment / deployment (release) Failing after 9m14s
### Description

- Restrict `w` to fix the issue

### Package Changes

_No response_

### Screenshots

![截圖 2025-10-17 19.21.47.png](/attachments/4237a798-f324-4a4d-8e41-cb62ca67c285)

### Reference

Resolves #159.

### Checklist

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

Reviewed-on: #160
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-17 19:29:22 +08:00
squid 3d7086d1a5 BLOG-155 Make post semantic id immutable (#157)
Frontend CI / build (push) Successful in 6m48s
### Description

- Removed semantic_id field from backend and frontend updating dto

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #155.

### Checklist

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

Reviewed-on: #157
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-17 18:45:38 +08:00
squid a5e7476784 BLOG-154 Fix datetime input timezone issue (#156)
Frontend CI / build (push) Successful in 1m43s
### Description

https://gemini.google.com/share/540d93bc79fc

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #154.

### Checklist

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

Reviewed-on: #156
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-17 17:59:13 +08:00
squid aede5e4bf2 BLOG-89 Post ToC (#153)
Frontend CI / build (push) Successful in 1m43s
### Description

- Only support for desktop device

### Package Changes

```json
{
  		"@types/crypto-js": "^4.2.2",
		"crypto-js": "^4.2.0",
		"svelte": "^5.39.13",
}
```

### Screenshots

<video src="attachments/602a4934-532c-4f03-9202-9e5909718964" title="螢幕錄影 2025-10-17 清晨6.57.27.mov" controls></video>

### Reference

Resolves #89.

### Checklist

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

Reviewed-on: #153
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-17 07:06:30 +08:00