2026-05-01 20:39:09 +08:00
|
|
|
import type { ArticleDetail } from '@geo/shared-types'
|
2026-04-07 16:07:21 +08:00
|
|
|
|
|
|
|
|
export interface ArticleActionState {
|
2026-05-01 20:39:09 +08:00
|
|
|
showPublish: boolean
|
|
|
|
|
showEdit: boolean
|
|
|
|
|
showPreview: boolean
|
|
|
|
|
showCopy: boolean
|
|
|
|
|
showDelete: boolean
|
2026-05-07 10:55:44 +08:00
|
|
|
showRegenerate: boolean
|
2026-04-07 16:07:21 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-07 10:55:44 +08:00
|
|
|
export interface ArticleActionSource {
|
|
|
|
|
generate_status?: string | null
|
|
|
|
|
source_type?: string | null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const regeneratableSourceTypes = new Set(['template', 'custom_generation', 'imitation', 'kol'])
|
|
|
|
|
|
|
|
|
|
export function resolveArticleActionState(
|
|
|
|
|
articleOrGenerateStatus?: ArticleActionSource | string | null,
|
|
|
|
|
): ArticleActionState {
|
|
|
|
|
const generateStatus =
|
|
|
|
|
typeof articleOrGenerateStatus === 'string'
|
|
|
|
|
? articleOrGenerateStatus
|
|
|
|
|
: articleOrGenerateStatus?.generate_status
|
|
|
|
|
const sourceType =
|
|
|
|
|
typeof articleOrGenerateStatus === 'string' ? undefined : articleOrGenerateStatus?.source_type
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (generateStatus === 'completed') {
|
2026-04-07 16:07:21 +08:00
|
|
|
return {
|
|
|
|
|
showPublish: true,
|
|
|
|
|
showEdit: true,
|
|
|
|
|
showPreview: true,
|
|
|
|
|
showCopy: true,
|
|
|
|
|
showDelete: true,
|
2026-05-07 10:55:44 +08:00
|
|
|
showRegenerate: false,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-07 16:07:21 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (generateStatus === 'draft') {
|
2026-04-07 16:07:21 +08:00
|
|
|
return {
|
|
|
|
|
showPublish: false,
|
|
|
|
|
showEdit: true,
|
|
|
|
|
showPreview: false,
|
|
|
|
|
showCopy: false,
|
|
|
|
|
showDelete: true,
|
2026-05-07 10:55:44 +08:00
|
|
|
showRegenerate: false,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-07 16:07:21 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
if (generateStatus === 'failed') {
|
2026-04-25 21:36:44 +08:00
|
|
|
return {
|
|
|
|
|
showPublish: false,
|
|
|
|
|
showEdit: false,
|
|
|
|
|
showPreview: false,
|
|
|
|
|
showCopy: false,
|
|
|
|
|
showDelete: true,
|
2026-05-07 10:55:44 +08:00
|
|
|
showRegenerate: sourceType ? regeneratableSourceTypes.has(sourceType) : false,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-25 21:36:44 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-07 16:07:21 +08:00
|
|
|
return {
|
|
|
|
|
showPublish: false,
|
|
|
|
|
showEdit: false,
|
|
|
|
|
showPreview: true,
|
|
|
|
|
showCopy: false,
|
|
|
|
|
showDelete: true,
|
2026-05-07 10:55:44 +08:00
|
|
|
showRegenerate: false,
|
2026-05-01 20:39:09 +08:00
|
|
|
}
|
2026-04-07 16:07:21 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
export function buildArticleClipboardContent(
|
|
|
|
|
detail: Pick<ArticleDetail, 'title' | 'markdown_content'>,
|
|
|
|
|
): string {
|
2026-05-20 17:00:57 +08:00
|
|
|
const title = detail.title?.trim() ?? ''
|
|
|
|
|
const markdown = detail.markdown_content?.trim() ?? ''
|
|
|
|
|
|
|
|
|
|
if (!title) {
|
|
|
|
|
return markdown
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!markdown) {
|
|
|
|
|
return `# ${title}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getLeadingH1Text(markdown) === title) {
|
|
|
|
|
return markdown
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [`# ${title}`, markdown].join('\n\n').trim()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getLeadingH1Text(markdown: string): string | null {
|
|
|
|
|
const normalizedMarkdown = markdown.replace(/^\uFEFF/, '').trimStart()
|
|
|
|
|
const firstLine = normalizedMarkdown.split(/\r?\n/, 1)[0]?.trim() ?? ''
|
|
|
|
|
const match = firstLine.match(/^#\s+(.+?)\s*#*$/)
|
|
|
|
|
return match?.[1]?.trim() ?? null
|
2026-04-07 16:07:21 +08:00
|
|
|
}
|