chore: pnpm format
All checks were successful
Frontend CI / build (push) Successful in 1m21s
PR Title Check / pr-title-check (pull_request) Successful in 17s

This commit is contained in:
SquidSpirit 2025-10-12 18:26:56 +08:00
parent 898ee86f91
commit 8b1d643531
27 changed files with 77 additions and 73 deletions

View File

@ -1,7 +1,7 @@
{ {
"useTabs": true, "useTabs": true,
"singleQuote": true, "singleQuote": true,
"trailingComma": "none", "trailingComma": "es5",
"printWidth": 100, "printWidth": 100,
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [ "overrides": [

View File

@ -18,13 +18,13 @@ export default ts.config(
...svelte.configs.prettier, ...svelte.configs.prettier,
{ {
languageOptions: { languageOptions: {
globals: { ...globals.browser, ...globals.node } globals: { ...globals.browser, ...globals.node },
}, },
rules: { rules: {
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
'no-undef': 'off' 'no-undef': 'off',
} },
}, },
{ {
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
@ -33,8 +33,8 @@ export default ts.config(
projectService: true, projectService: true,
extraFileExtensions: ['.svelte'], extraFileExtensions: ['.svelte'],
parser: ts.parser, parser: ts.parser,
svelteConfig svelteConfig,
} },
} },
} }
); );

View File

@ -19,7 +19,7 @@ Sentry.init({
replaysOnErrorSampleRate: 1.0, replaysOnErrorSampleRate: 1.0,
// If you don't want to use Session Replay, just remove the line below: // If you don't want to use Session Replay, just remove the line below:
integrations: [replayIntegration()] integrations: [replayIntegration()],
}); });
// If you have a custom error handler, pass it to `handleErrorWithSentry` // If you have a custom error handler, pass it to `handleErrorWithSentry`

View File

@ -12,7 +12,7 @@ import { Environment } from '$lib/environment';
Sentry.init({ Sentry.init({
dsn: Environment.SENTRY_DSN, dsn: Environment.SENTRY_DSN,
tracesSampleRate: 1, tracesSampleRate: 1,
enableLogs: true enableLogs: true,
}); });
export const handle: Handle = sequence(Sentry.sentryHandle(), ({ event, resolve }) => { export const handle: Handle = sequence(Sentry.sentryHandle(), ({ event, resolve }) => {

View File

@ -2,7 +2,7 @@ export enum StatusType {
Idle, Idle,
Loading, Loading,
Success, Success,
Error Error,
} }
export interface IdleState<T> { export interface IdleState<T> {

View File

@ -2,7 +2,7 @@
let { let {
label, label,
link, link,
isSelected isSelected,
}: { }: {
label: string; label: string;
link: string; link: string;

View File

@ -11,7 +11,7 @@
'在這裡', '在這裡',
'我會分享我的技術筆記、開發心得', '我會分享我的技術筆記、開發心得',
'還有各式各樣實用工具的評測與介紹', '還有各式各樣實用工具的評測與介紹',
'一起探索數位世界的無限可能吧!' '一起探索數位世界的無限可能吧!',
]; ];
let isReady: boolean = $state(false); let isReady: boolean = $state(false);

View File

@ -44,7 +44,7 @@
hour12: false, hour12: false,
hour: '2-digit', hour: '2-digit',
minute: '2-digit', minute: '2-digit',
second: '2-digit' second: '2-digit',
}); });
} }
</script> </script>

View File

@ -31,7 +31,7 @@
'資工系', '資工系',
'軟體工程', '軟體工程',
'遊戲', '遊戲',
'魷魚' '魷魚',
]; ];
// Initialize with placeholder to prevent flickering // Initialize with placeholder to prevent flickering

View File

@ -5,7 +5,7 @@ export const ColorResponseSchema = z.object({
red: z.number().int().min(0).max(255), red: z.number().int().min(0).max(255),
green: z.number().int().min(0).max(255), green: z.number().int().min(0).max(255),
blue: z.number().int().min(0).max(255), blue: z.number().int().min(0).max(255),
alpha: z.number().int().min(0).max(255) alpha: z.number().int().min(0).max(255),
}); });
export class ColorResponseDto { export class ColorResponseDto {
@ -27,7 +27,7 @@ export class ColorResponseDto {
red: parsedJson.red, red: parsedJson.red,
green: parsedJson.green, green: parsedJson.green,
blue: parsedJson.blue, blue: parsedJson.blue,
alpha: parsedJson.alpha alpha: parsedJson.alpha,
}); });
} }
@ -36,7 +36,7 @@ export class ColorResponseDto {
red: this.red, red: this.red,
green: this.green, green: this.green,
blue: this.blue, blue: this.blue,
alpha: this.alpha alpha: this.alpha,
}); });
} }
} }

View File

@ -5,7 +5,7 @@ import { z } from 'zod';
export const LabelResponseSchema = z.object({ export const LabelResponseSchema = z.object({
id: z.int32(), id: z.int32(),
name: z.string(), name: z.string(),
color: ColorResponseSchema color: ColorResponseSchema,
}); });
export class LabelResponseDto { export class LabelResponseDto {
@ -24,7 +24,7 @@ export class LabelResponseDto {
return new LabelResponseDto({ return new LabelResponseDto({
id: parsedJson.id, id: parsedJson.id,
name: parsedJson.name, name: parsedJson.name,
color: ColorResponseDto.fromJson(parsedJson.color) color: ColorResponseDto.fromJson(parsedJson.color),
}); });
} }
@ -32,7 +32,7 @@ export class LabelResponseDto {
return new Label({ return new Label({
id: this.id, id: this.id,
name: this.name, name: this.name,
color: this.color color: this.color,
}); });
} }
} }

View File

@ -9,7 +9,7 @@ export const PostInfoResponseSchema = z.object({
description: z.string(), description: z.string(),
preview_image_url: z.url(), preview_image_url: z.url(),
labels: z.array(LabelResponseSchema), labels: z.array(LabelResponseSchema),
published_time: z.iso.datetime({ offset: true }).nullable() published_time: z.iso.datetime({ offset: true }).nullable(),
}); });
export class PostInfoResponseDto { export class PostInfoResponseDto {
@ -54,7 +54,7 @@ export class PostInfoResponseDto {
description: parsedJson.description, description: parsedJson.description,
previewImageUrl: new URL(parsedJson.preview_image_url), previewImageUrl: new URL(parsedJson.preview_image_url),
labels: parsedJson.labels.map((label) => LabelResponseDto.fromJson(label)), labels: parsedJson.labels.map((label) => LabelResponseDto.fromJson(label)),
publishedTime: published_time publishedTime: published_time,
}); });
} }
@ -66,7 +66,7 @@ export class PostInfoResponseDto {
description: this.description, description: this.description,
previewImageUrl: this.previewImageUrl, previewImageUrl: this.previewImageUrl,
labels: this.labels.map((label) => label.toEntity()), labels: this.labels.map((label) => label.toEntity()),
publishedTime: this.publishedTime publishedTime: this.publishedTime,
}); });
} }
} }

View File

@ -1,6 +1,6 @@
import { import {
PostInfoResponseDto, PostInfoResponseDto,
PostInfoResponseSchema PostInfoResponseSchema,
} from '$lib/post/adapter/gateway/postInfoResponseDto'; } from '$lib/post/adapter/gateway/postInfoResponseDto';
import { Post } from '$lib/post/domain/entity/post'; import { Post } from '$lib/post/domain/entity/post';
import z from 'zod'; import z from 'zod';
@ -8,7 +8,7 @@ import z from 'zod';
export const PostResponseSchema = z.object({ export const PostResponseSchema = z.object({
id: z.int32(), id: z.int32(),
info: PostInfoResponseSchema, info: PostInfoResponseSchema,
content: z.string() content: z.string(),
}); });
export class PostResponseDto { export class PostResponseDto {
@ -27,7 +27,7 @@ export class PostResponseDto {
return new PostResponseDto({ return new PostResponseDto({
id: parsedJson.id, id: parsedJson.id,
info: PostInfoResponseDto.fromJson(parsedJson.info), info: PostInfoResponseDto.fromJson(parsedJson.info),
content: parsedJson.content content: parsedJson.content,
}); });
} }
@ -35,7 +35,7 @@ export class PostResponseDto {
return new Post({ return new Post({
id: this.id, id: this.id,
info: this.info.toEntity(), info: this.info.toEntity(),
content: this.content content: this.content,
}); });
} }
} }

View File

@ -43,7 +43,7 @@ export class ColorViewModel {
red: Math.round(r * 255), red: Math.round(r * 255),
green: Math.round(g * 255), green: Math.round(g * 255),
blue: Math.round(b * 255), blue: Math.round(b * 255),
alpha: 255 alpha: 255,
}); });
} }
@ -52,7 +52,7 @@ export class ColorViewModel {
red: color.red, red: color.red,
green: color.green, green: color.green,
blue: color.blue, blue: color.blue,
alpha: color.alpha alpha: color.alpha,
}); });
} }
@ -115,7 +115,7 @@ export class ColorViewModel {
red: this.red, red: this.red,
green: this.green, green: this.green,
blue: this.blue, blue: this.blue,
alpha: this.alpha alpha: this.alpha,
}; };
} }
} }

View File

@ -1,6 +1,6 @@
import { import {
ColorViewModel, ColorViewModel,
type DehydratedColorProps type DehydratedColorProps,
} from '$lib/post/adapter/presenter/colorViewModel'; } from '$lib/post/adapter/presenter/colorViewModel';
import type { Label } from '$lib/post/domain/entity/label'; import type { Label } from '$lib/post/domain/entity/label';
@ -19,7 +19,7 @@ export class LabelViewModel {
return new LabelViewModel({ return new LabelViewModel({
id: label.id, id: label.id,
name: label.name, name: label.name,
color: ColorViewModel.fromEntity(label.color) color: ColorViewModel.fromEntity(label.color),
}); });
} }
@ -27,7 +27,7 @@ export class LabelViewModel {
return new LabelViewModel({ return new LabelViewModel({
id: props.id, id: props.id,
name: props.name, name: props.name,
color: ColorViewModel.rehydrate(props.color) color: ColorViewModel.rehydrate(props.color),
}); });
} }
@ -35,7 +35,7 @@ export class LabelViewModel {
return { return {
id: this.id, id: this.id,
name: this.name, name: this.name,
color: this.color.dehydrate() color: this.color.dehydrate(),
}; };
} }
} }

View File

@ -8,7 +8,7 @@ export type PostEvent = PostLoadedEvent;
export class PostBloc { export class PostBloc {
private readonly state = writable<PostState>({ private readonly state = writable<PostState>({
status: StatusType.Idle status: StatusType.Idle,
}); });
constructor( constructor(
@ -17,7 +17,7 @@ export class PostBloc {
) { ) {
this.state.set({ this.state.set({
status: StatusType.Idle, status: StatusType.Idle,
data: initialData data: initialData,
}); });
} }
@ -44,7 +44,7 @@ export class PostBloc {
const postViewModel = PostViewModel.fromEntity(post); const postViewModel = PostViewModel.fromEntity(post);
const result: PostState = { const result: PostState = {
status: StatusType.Success, status: StatusType.Success,
data: postViewModel data: postViewModel,
}; };
this.state.set(result); this.state.set(result);
@ -53,7 +53,7 @@ export class PostBloc {
} }
export enum PostEventType { export enum PostEventType {
PostLoadedEvent PostLoadedEvent,
} }
interface PostLoadedEvent { interface PostLoadedEvent {

View File

@ -1,6 +1,6 @@
import { import {
LabelViewModel, LabelViewModel,
type DehydratedLabelProps type DehydratedLabelProps,
} from '$lib/post/adapter/presenter/labelViewModel'; } from '$lib/post/adapter/presenter/labelViewModel';
import type { PostInfo } from '$lib/post/domain/entity/postInfo'; import type { PostInfo } from '$lib/post/domain/entity/postInfo';
@ -39,7 +39,7 @@ export class PostInfoViewModel {
description: postInfo.description, description: postInfo.description,
previewImageUrl: postInfo.previewImageUrl, previewImageUrl: postInfo.previewImageUrl,
labels: postInfo.labels.map((label) => LabelViewModel.fromEntity(label)), labels: postInfo.labels.map((label) => LabelViewModel.fromEntity(label)),
publishedTime: postInfo.publishedTime publishedTime: postInfo.publishedTime,
}); });
} }
@ -56,7 +56,7 @@ export class PostInfoViewModel {
description: props.description, description: props.description,
previewImageUrl: new URL(props.previewImageUrl), previewImageUrl: new URL(props.previewImageUrl),
labels: props.labels.map((label) => LabelViewModel.rehydrate(label)), labels: props.labels.map((label) => LabelViewModel.rehydrate(label)),
publishedTime: publishedTime publishedTime: publishedTime,
}); });
} }
@ -76,7 +76,7 @@ export class PostInfoViewModel {
description: this.description, description: this.description,
previewImageUrl: this.previewImageUrl.href, previewImageUrl: this.previewImageUrl.href,
labels: this.labels.map((label) => label.dehydrate()), labels: this.labels.map((label) => label.dehydrate()),
publishedTime: this.publishedTime?.getTime() ?? null publishedTime: this.publishedTime?.getTime() ?? null,
}; };
} }
} }

View File

@ -8,7 +8,7 @@ export type PostListEvent = PostListLoadedEvent;
export class PostListBloc { export class PostListBloc {
private readonly state = writable<PostListState>({ private readonly state = writable<PostListState>({
status: StatusType.Idle status: StatusType.Idle,
}); });
constructor( constructor(
@ -17,7 +17,7 @@ export class PostListBloc {
) { ) {
this.state.set({ this.state.set({
status: StatusType.Idle, status: StatusType.Idle,
data: initialData data: initialData,
}); });
} }
@ -38,7 +38,7 @@ export class PostListBloc {
const postViewModels = posts.map((post) => PostInfoViewModel.fromEntity(post)); const postViewModels = posts.map((post) => PostInfoViewModel.fromEntity(post));
const result: PostListState = { const result: PostListState = {
status: StatusType.Success, status: StatusType.Success,
data: postViewModels data: postViewModels,
}; };
this.state.set(result); this.state.set(result);
@ -47,7 +47,7 @@ export class PostListBloc {
} }
export enum PostListEventType { export enum PostListEventType {
PostListLoadedEvent PostListLoadedEvent,
} }
export interface PostListLoadedEvent { export interface PostListLoadedEvent {

View File

@ -1,6 +1,6 @@
import { import {
PostInfoViewModel, PostInfoViewModel,
type DehydratedPostInfoProps type DehydratedPostInfoProps,
} from '$lib/post/adapter/presenter/postInfoViewModel'; } from '$lib/post/adapter/presenter/postInfoViewModel';
import type { Post } from '$lib/post/domain/entity/post'; import type { Post } from '$lib/post/domain/entity/post';
@ -19,7 +19,7 @@ export class PostViewModel {
return new PostViewModel({ return new PostViewModel({
id: post.id, id: post.id,
info: PostInfoViewModel.fromEntity(post.info), info: PostInfoViewModel.fromEntity(post.info),
content: post.content content: post.content,
}); });
} }
@ -27,7 +27,7 @@ export class PostViewModel {
return new PostViewModel({ return new PostViewModel({
id: props.id, id: props.id,
info: PostInfoViewModel.rehydrate(props.info), info: PostInfoViewModel.rehydrate(props.info),
content: props.content content: props.content,
}); });
} }
@ -35,7 +35,7 @@ export class PostViewModel {
return { return {
id: this.id, id: this.id,
info: this.info.dehydrate(), info: this.info.dehydrate(),
content: this.content content: this.content,
}; };
} }
} }

View File

@ -32,7 +32,7 @@
{/if} {/if}
{/if} {/if}
</svelte:head> </svelte:head>
<article class="prose prose-gray container pb-10"> <article class="container prose pb-10 prose-gray">
{#if state.data} {#if state.data}
<PostContentHeader postInfo={state.data.info} /> <PostContentHeader postInfo={state.data.info} />
<div class="max-w-3xl"> <div class="max-w-3xl">

View File

@ -17,7 +17,11 @@
} }
</script> </script>
<a class="flex cursor-pointer flex-col gap-y-6" href="/post/{postInfo.semanticId}" title={postInfo.title}> <a
class="flex cursor-pointer flex-col gap-y-6"
href="/post/{postInfo.semanticId}"
title={postInfo.title}
>
<div class="relative aspect-video overflow-hidden rounded-2xl bg-gray-200"> <div class="relative aspect-video overflow-hidden rounded-2xl bg-gray-200">
<img <img
class="rounded-2xl object-cover transition-opacity duration-300 class="rounded-2xl object-cover transition-opacity duration-300

View File

@ -5,7 +5,7 @@
headline, headline,
description, description,
datePublished, datePublished,
image image,
}: { }: {
headline: string; headline: string;
description: string; description: string;
@ -19,7 +19,7 @@
headline: headline, headline: headline,
description: description, description: description,
datePublished: datePublished.toISOString(), datePublished: datePublished.toISOString(),
image: image.href image: image.href,
}); });
const jsonLdScript = $derived( const jsonLdScript = $derived(

View File

@ -7,6 +7,6 @@ export const load: PageServerLoad = async ({ locals }) => {
const state = await postListBloc.dispatch({ event: PostListEventType.PostListLoadedEvent }); const state = await postListBloc.dispatch({ event: PostListEventType.PostListLoadedEvent });
return { return {
dehydratedData: state.data?.map((post) => post.dehydrate()) dehydratedData: state.data?.map((post) => post.dehydrate()),
}; };
}; };

View File

@ -7,13 +7,13 @@ export const load: PageServerLoad = async ({ locals, params }) => {
const state = await postBloc.dispatch({ const state = await postBloc.dispatch({
event: PostEventType.PostLoadedEvent, event: PostEventType.PostLoadedEvent,
id: params.id id: params.id,
}); });
if (!state.data) { if (!state.data) {
error(404, { message: 'Post not found' }); error(404, { message: 'Post not found' });
} }
return { return {
dehydratedData: state.data.dehydrate() dehydratedData: state.data.dehydrate(),
}; };
}; };

View File

@ -11,8 +11,8 @@ const config = {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter. // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters. // See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter() adapter: adapter(),
} },
}; };
export default config; export default config;

View File

@ -9,10 +9,10 @@ module.exports = {
'--tw-prose-headings': 'var(--color-gray-800)', '--tw-prose-headings': 'var(--color-gray-800)',
'--tw-prose-links': 'var(--color-gray-800)', '--tw-prose-links': 'var(--color-gray-800)',
'--tw-prose-bold': 'var(--color-gray-800)', '--tw-prose-bold': 'var(--color-gray-800)',
'--tw-prose-quotes': 'var(--color-gray-800)' '--tw-prose-quotes': 'var(--color-gray-800)',
} },
} },
}) }),
} },
} },
}; };

View File

@ -10,22 +10,22 @@ export default defineConfig({
sentrySvelteKit({ sentrySvelteKit({
sourceMapsUploadOptions: { sourceMapsUploadOptions: {
org: 'squidspirit', org: 'squidspirit',
project: 'blog-beta-frontend' project: 'blog-beta-frontend',
} },
}), }),
tailwindcss(), tailwindcss(),
sveltekit() sveltekit(),
], ],
define: { define: {
'App.__VERSION__': JSON.stringify(version) 'App.__VERSION__': JSON.stringify(version),
}, },
server: { server: {
proxy: { proxy: {
'/api': { '/api': {
target: 'http://127.0.0.1:8080', target: 'http://127.0.0.1:8080',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') rewrite: (path) => path.replace(/^\/api/, ''),
} },
} },
} },
}); });