Procházet zdrojové kódy

build: integrate changelog parser with Vite build process

- Add releaseNotesPlugin to Vite config that runs during buildStart
- Automatically generate release JSON files during webview build
- Update .gitignore to exclude generated release notes files
- Ensure release notes are always up-to-date with latest changelog

feat: add release notes type definitions and build utilities

- Define TypeScript interfaces for release notes structure
- Add changelog parser script for generating JSON from markdown
- Establish type safety for release notes feature
- Enable automated release notes generation during build

feat: add shared types and message interfaces for release notes

- Add release version tracking to global settings
- Define extension-webview message interface for release notes
- Add webview-extension message interface for release notes data
- Establish communication protocol for release notes feature
Chris Hasson před 4 měsíci
rodič
revize
b5408d4be8

+ 1 - 0
packages/types/src/global-settings.ts

@@ -168,6 +168,7 @@ export const globalSettingsSchema = z.object({
 	terminalCommandApiConfigId: z.string().optional(), // kilocode_change
 	ghostServiceSettings: ghostServiceSettingsSchema, // kilocode_change
 	hasPerformedOrganizationAutoSwitch: z.boolean().optional(), // kilocode_change
+	lastViewedReleaseVersion: z.string().optional(), // kilocode_change: Track last viewed release version
 	includeTaskHistoryInEnhance: z.boolean().optional(),
 	historyPreviewCollapsed: z.boolean().optional(),
 	reasoningBlockCollapsed: z.boolean().optional(),

+ 3 - 0
webview-ui/.gitignore

@@ -11,6 +11,9 @@
 # production
 /build
 
+# kilocode_change - Generated release notes files
+/src/generated/
+
 # misc
 .DS_Store
 .env.local

+ 144 - 0
webview-ui/scripts/generate-release-notes.mjs

@@ -0,0 +1,144 @@
+#!/usr/bin/env node
+
+import fs from 'fs'
+import path from 'path'
+import { fileURLToPath } from 'url'
+
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = path.dirname(__filename)
+
+console.log('🚀 Starting changelog parsing...')
+
+// Paths
+const changelogPath = path.resolve(__dirname, '../../CHANGELOG.md')
+const outputDir = path.resolve(__dirname, '../src/generated/releases')
+
+console.log('📖 Reading changelog from:', changelogPath)
+console.log('📁 Output directory:', outputDir)
+
+// Check if changelog exists
+if (!fs.existsSync(changelogPath)) {
+    console.error('❌ Changelog not found at:', changelogPath)
+    process.exit(1)
+}
+
+// Read changelog
+const changelogContent = fs.readFileSync(changelogPath, 'utf-8')
+console.log('📄 Changelog loaded, length:', changelogContent.length, 'characters')
+
+// Parse releases
+const releases = []
+const versionPattern = /^## \[v(\d+\.\d+\.\d+)\]/gm
+let match
+
+while ((match = versionPattern.exec(changelogContent)) !== null) {
+    const version = match[1]
+    const startIndex = match.index
+
+    // Find next version or end of file
+    versionPattern.lastIndex = startIndex + 1
+    const nextMatch = versionPattern.exec(changelogContent)
+    versionPattern.lastIndex = match.index + match[0].length // Reset for next iteration
+
+    const endIndex = nextMatch ? nextMatch.index : changelogContent.length
+    const sectionContent = changelogContent.slice(startIndex, endIndex)
+
+    // Parse the section
+    const lines = sectionContent.split('\n')
+    const rawChanges = []
+
+    for (let i = 1; i < lines.length; i++) {
+        const line = lines[i].trim()
+        if (line.startsWith('- ')) {
+            // Extract basic info
+            const item = {
+                description: line.replace(/^- /, '').trim(),
+                category: 'other'
+            }
+
+            // Extract PR number
+            const prMatch = line.match(/\[#(\d+)\]/)
+            if (prMatch) {
+                item.prNumber = parseInt(prMatch[1])
+            }
+
+            // Extract commit hash
+            const commitMatch = line.match(/\[`([a-f0-9]+)`\]/)
+            if (commitMatch) {
+                item.commitHash = commitMatch[1]
+            }
+
+            // Extract author
+            const authorMatch = line.match(/Thanks \[@(\w+)\]/)
+            if (authorMatch) {
+                item.author = authorMatch[1]
+            }
+
+            // Extract description after "! - "
+            const descMatch = line.match(/! - (.+)$/)
+            if (descMatch) {
+                item.description = descMatch[1].trim()
+            }
+
+            rawChanges.push(item)
+        }
+    }
+
+    if (rawChanges.length > 0) {
+        const release = {
+            version,
+            features: [],
+            fixes: [],
+            improvements: [],
+            breakingChanges: [],
+            rawChanges
+        }
+
+        // Simple categorization
+        rawChanges.forEach(item => {
+            const desc = item.description.toLowerCase()
+            if (desc.includes('fix') || desc.includes('bug')) {
+                item.category = 'fix'
+                release.fixes.push(item)
+            } else if (desc.includes('break')) {
+                item.category = 'breaking'
+                release.breakingChanges.push(item)
+            } else if (desc.includes('add') || desc.includes('new')) {
+                item.category = 'feature'
+                release.features.push(item)
+            } else if (desc.includes('improve') || desc.includes('update')) {
+                item.category = 'improvement'
+                release.improvements.push(item)
+            }
+        })
+
+        releases.push(release)
+        console.log(`📝 Parsed release v${version} with ${rawChanges.length} changes`)
+    }
+}
+
+console.log(`✅ Found ${releases.length} releases`)
+
+// Limit to the last 10 releases to keep build size manageable
+const MAX_RELEASES = 10
+const limitedReleases = releases.slice(0, MAX_RELEASES)
+console.log(`🔢 Limiting to ${limitedReleases.length} most recent releases (from ${releases.length} total)`)
+
+// Create output directory
+if (!fs.existsSync(outputDir)) {
+    fs.mkdirSync(outputDir, { recursive: true })
+    console.log('📁 Created output directory')
+}
+
+// Generate single releases.json file with current version and recent releases
+const releaseData = {
+    currentVersion: limitedReleases[0]?.version || "0.0.0",
+    releases: limitedReleases
+}
+
+const releasesPath = path.join(outputDir, 'releases.json')
+fs.writeFileSync(releasesPath, JSON.stringify(releaseData, null, 2))
+console.log(`💾 Generated releases.json with ${limitedReleases.length} releases`)
+console.log(`📋 Current version: ${releaseData.currentVersion}`)
+
+console.log('🎉 Changelog parsing completed successfully!')

+ 29 - 0
webview-ui/src/types/release-notes.ts

@@ -0,0 +1,29 @@
+export interface ReleaseItem {
+	description: string
+	prNumber?: number
+	commitHash?: string
+	author?: string
+	category: ReleaseItemCategory
+	details?: string
+}
+
+export type ReleaseItemCategory = "feature" | "fix" | "improvement" | "breaking" | "other"
+
+export interface ReleaseNote {
+	version: string
+	date?: string
+	features: ReleaseItem[]
+	fixes: ReleaseItem[]
+	improvements: ReleaseItem[]
+	breakingChanges: ReleaseItem[]
+	rawChanges: ReleaseItem[]
+}
+
+export interface ReleaseNotesModalProps {
+	isOpen: boolean
+	onClose: () => void
+	currentVersion: string
+	releaseNotes: ReleaseNote[]
+	lastViewedVersion?: string
+	onVersionViewed: (version: string) => void
+}

+ 25 - 1
webview-ui/vite.config.ts

@@ -51,6 +51,23 @@ const persistPortPlugin = (): Plugin => ({
 	},
 })
 
+const releaseNotesPlugin = (): Plugin => ({
+	name: "generate-release-notes",
+	buildStart() {
+		console.log("[Release Notes Plugin] Generating release notes...")
+		try {
+			execSync("node scripts/generate-release-notes.mjs", {
+				cwd: __dirname,
+				stdio: "inherit",
+			})
+			console.log("[Release Notes Plugin] Release notes generated successfully")
+		} catch (error) {
+			console.error("[Release Notes Plugin] Failed to generate release notes:", error)
+			// Don't fail the build, just warn
+		}
+	},
+})
+
 // https://vitejs.dev/config/
 export default defineConfig(({ mode }) => {
 	let outDir = "../src/webview-ui/build"
@@ -91,7 +108,14 @@ export default defineConfig(({ mode }) => {
 		define["process.env.PKG_OUTPUT_CHANNEL"] = JSON.stringify("Kilo-Code-Nightly")
 	}
 
-	const plugins: PluginOption[] = [react(), tailwindcss(), persistPortPlugin(), wasmPlugin(), sourcemapPlugin()]
+	const plugins: PluginOption[] = [
+		react(),
+		tailwindcss(),
+		persistPortPlugin(),
+		wasmPlugin(),
+		sourcemapPlugin(),
+		releaseNotesPlugin(),
+	]
 
 	return {
 		plugins,