diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java index 64f77eee..5ea7d706 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java @@ -64,9 +64,9 @@ class MeControllerTest { "Assist with code review", "ACTIVE", 12L, - 3L, + 3, null, - 0L, + 0, "1.0.0", 11L, "PUBLISHED", diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/NamespaceWorkflowContractTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/NamespaceWorkflowContractTest.java index 167880fc..8c796f22 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/NamespaceWorkflowContractTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/NamespaceWorkflowContractTest.java @@ -95,7 +95,7 @@ class NamespaceWorkflowContractTest { mockMvc.perform(post("/api/web/namespaces") .with(csrf()) - .with(auth("owner-1")) + .with(auth("owner-1", Set.of("SKILL_ADMIN"))) .requestAttr("userId", "owner-1") .contentType(org.springframework.http.MediaType.APPLICATION_JSON) .content("{\"slug\":\"team-flow\",\"displayName\":\"Team Flow\",\"description\":\"workflow\"}")) @@ -166,13 +166,17 @@ class NamespaceWorkflowContractTest { } private RequestPostProcessor auth(String userId) { + return auth(userId, Set.of()); + } + + private RequestPostProcessor auth(String userId, Set platformRoles) { PlatformPrincipal principal = new PlatformPrincipal( userId, userId, userId + "@example.com", "", "session", - Set.of() + platformRoles ); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( principal, diff --git a/web/src/features/skill/markdown-frontmatter.test.ts b/web/src/features/skill/markdown-frontmatter.test.ts new file mode 100644 index 00000000..7a0f2535 --- /dev/null +++ b/web/src/features/skill/markdown-frontmatter.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest' +import { stripMarkdownFrontmatter } from './markdown-frontmatter' + +describe('stripMarkdownFrontmatter', () => { + it('removes yaml frontmatter from markdown content', () => { + const markdown = `--- +name: demo +description: sample +--- + +# Title +` + + expect(stripMarkdownFrontmatter(markdown)).toBe('\n# Title\n') + }) + + it('removes toml frontmatter from markdown content', () => { + const markdown = `+++ +name = "demo" ++++ + +Body` + + expect(stripMarkdownFrontmatter(markdown)).toBe('\nBody') + }) + + it('returns the original content when no frontmatter exists', () => { + expect(stripMarkdownFrontmatter('# Title')).toBe('# Title') + }) +}) diff --git a/web/src/features/skill/markdown-frontmatter.ts b/web/src/features/skill/markdown-frontmatter.ts new file mode 100644 index 00000000..121486fc --- /dev/null +++ b/web/src/features/skill/markdown-frontmatter.ts @@ -0,0 +1,5 @@ +const FRONTMATTER_PATTERN = /^(---[\t ]*\r?\n[\s\S]*?\r?\n---[\t ]*(?:\r?\n|$)|\+\+\+[\t ]*\r?\n[\s\S]*?\r?\n\+\+\+[\t ]*(?:\r?\n|$))/ + +export function stripMarkdownFrontmatter(content: string) { + return content.replace(FRONTMATTER_PATTERN, '') +} diff --git a/web/src/features/skill/markdown-renderer.tsx b/web/src/features/skill/markdown-renderer.tsx index c0da8d07..9478b105 100644 --- a/web/src/features/skill/markdown-renderer.tsx +++ b/web/src/features/skill/markdown-renderer.tsx @@ -1,31 +1,14 @@ import ReactMarkdown from 'react-markdown' import rehypeHighlight from 'rehype-highlight' import rehypeSanitize from 'rehype-sanitize' -import remarkFrontmatter from 'remark-frontmatter' import remarkGfm from 'remark-gfm' -import type { Root } from 'mdast' -import { visit } from 'unist-util-visit' +import { stripMarkdownFrontmatter } from './markdown-frontmatter' interface MarkdownRendererProps { content: string className?: string } -function remarkStripFrontmatter() { - return (tree: Root) => { - visit(tree, (node, index, parent) => { - if (!parent || index === undefined) { - return - } - - const nodeType = String(node.type) - if (nodeType === 'yaml' || nodeType === 'toml') { - parent.children.splice(index, 1) - } - }) - } -} - export function MarkdownRenderer({ content, className }: MarkdownRendererProps) { const containerClassName = [ className, @@ -33,11 +16,12 @@ export function MarkdownRenderer({ content, className }: MarkdownRendererProps) ] .filter(Boolean) .join(' ') + const normalizedContent = stripMarkdownFrontmatter(content) return (
( @@ -69,7 +53,7 @@ export function MarkdownRenderer({ content, className }: MarkdownRendererProps) ), }} > - {content} + {normalizedContent}
)