chore: checkpoint ai search comments and i18n foundation
This commit is contained in:
@@ -32,6 +32,10 @@ export interface Comment {
|
||||
avatar: string | null;
|
||||
content: string | null;
|
||||
reply_to: string | null;
|
||||
reply_to_comment_id: number | null;
|
||||
scope: 'article' | 'paragraph';
|
||||
paragraph_key: string | null;
|
||||
paragraph_excerpt: string | null;
|
||||
approved: boolean | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
@@ -42,7 +46,16 @@ export interface CreateCommentInput {
|
||||
nickname: string;
|
||||
email?: string;
|
||||
content: string;
|
||||
scope?: 'article' | 'paragraph';
|
||||
paragraphKey?: string;
|
||||
paragraphExcerpt?: string;
|
||||
replyTo?: string | null;
|
||||
replyToCommentId?: number | null;
|
||||
}
|
||||
|
||||
export interface ParagraphCommentSummary {
|
||||
paragraph_key: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface ApiTag {
|
||||
@@ -98,6 +111,23 @@ export interface ApiSiteSettings {
|
||||
social_email: string | null;
|
||||
location: string | null;
|
||||
tech_stack: string[] | null;
|
||||
ai_enabled: boolean;
|
||||
}
|
||||
|
||||
export interface AiSource {
|
||||
slug: string;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
score: number;
|
||||
chunk_index: number;
|
||||
}
|
||||
|
||||
export interface AiAskResponse {
|
||||
question: string;
|
||||
answer: string;
|
||||
sources: AiSource[];
|
||||
indexed_chunks: number;
|
||||
last_indexed_at: string | null;
|
||||
}
|
||||
|
||||
export interface ApiSearchResult {
|
||||
@@ -153,6 +183,9 @@ export const DEFAULT_SITE_SETTINGS: SiteSettings = {
|
||||
email: 'mailto:hello@termi.dev',
|
||||
},
|
||||
techStack: ['Astro', 'Svelte', 'Tailwind CSS', 'TypeScript'],
|
||||
ai: {
|
||||
enabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
const formatPostDate = (dateString: string) => dateString.slice(0, 10);
|
||||
@@ -244,6 +277,9 @@ const normalizeSiteSettings = (settings: ApiSiteSettings): SiteSettings => ({
|
||||
email: settings.social_email || DEFAULT_SITE_SETTINGS.social.email,
|
||||
},
|
||||
techStack: settings.tech_stack?.length ? settings.tech_stack : DEFAULT_SITE_SETTINGS.techStack,
|
||||
ai: {
|
||||
enabled: Boolean(settings.ai_enabled),
|
||||
},
|
||||
});
|
||||
|
||||
class ApiClient {
|
||||
@@ -293,14 +329,32 @@ class ApiClient {
|
||||
return posts.find(post => post.slug === slug) || null;
|
||||
}
|
||||
|
||||
async getComments(postSlug: string, options?: { approved?: boolean }): Promise<Comment[]> {
|
||||
async getComments(
|
||||
postSlug: string,
|
||||
options?: {
|
||||
approved?: boolean;
|
||||
scope?: 'article' | 'paragraph';
|
||||
paragraphKey?: string;
|
||||
}
|
||||
): Promise<Comment[]> {
|
||||
const params = new URLSearchParams({ post_slug: postSlug });
|
||||
if (options?.approved !== undefined) {
|
||||
params.set('approved', String(options.approved));
|
||||
}
|
||||
if (options?.scope) {
|
||||
params.set('scope', options.scope);
|
||||
}
|
||||
if (options?.paragraphKey) {
|
||||
params.set('paragraph_key', options.paragraphKey);
|
||||
}
|
||||
return this.fetch<Comment[]>(`/comments?${params.toString()}`);
|
||||
}
|
||||
|
||||
async getParagraphCommentSummary(postSlug: string): Promise<ParagraphCommentSummary[]> {
|
||||
const params = new URLSearchParams({ post_slug: postSlug });
|
||||
return this.fetch<ParagraphCommentSummary[]>(`/comments/paragraphs/summary?${params.toString()}`);
|
||||
}
|
||||
|
||||
async createComment(comment: CreateCommentInput): Promise<Comment> {
|
||||
return this.fetch<Comment>('/comments', {
|
||||
method: 'POST',
|
||||
@@ -309,7 +363,11 @@ class ApiClient {
|
||||
nickname: comment.nickname,
|
||||
email: comment.email,
|
||||
content: comment.content,
|
||||
scope: comment.scope,
|
||||
paragraphKey: comment.paragraphKey,
|
||||
paragraphExcerpt: comment.paragraphExcerpt,
|
||||
replyTo: comment.replyTo,
|
||||
replyToCommentId: comment.replyToCommentId,
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -398,6 +456,13 @@ class ApiClient {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async askAi(question: string): Promise<AiAskResponse> {
|
||||
return this.fetch<AiAskResponse>('/ai/ask', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ question }),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const api = new ApiClient(API_BASE_URL);
|
||||
|
||||
Reference in New Issue
Block a user