diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml new file mode 100644 index 0000000..7c66c01 --- /dev/null +++ b/.github/workflows/release-windows.yml @@ -0,0 +1,45 @@ +name: Release Windows + +on: + push: + tags: + - 'v*.*.*' + +env: + NODE_VERSION: 20 + PNPM_VERSION: 10.32.1 + +jobs: + release: + runs-on: windows-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Verify tag matches package version + shell: bash + run: | + VERSION="$(node -p "JSON.parse(require('node:fs').readFileSync('package.json', 'utf8')).version")" + if [[ "v${VERSION}" != "${GITHUB_REF_NAME}" ]]; then + echo "Tag ${GITHUB_REF_NAME} does not match package.json version ${VERSION}" >&2 + exit 1 + fi + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Build app + run: pnpm build + - name: Build Windows release artifacts + run: pnpm exec node ./scripts/run-electron-builder.cjs --win nsis --x64 --arm64 --publish never + - name: Publish GitHub release + uses: softprops/action-gh-release@v2 + with: + files: | + dist/*.exe + dist/*.exe.blockmap + dist/latest.yml diff --git a/electron-builder.yml b/electron-builder.yml index 458c209..50173a6 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -34,8 +34,14 @@ dmg: type: link path: /Applications win: + icon: resources/icon.ico + artifactName: Agent-Trace-${version}-${arch}.${ext} target: - nsis +nsis: + oneClick: false + allowToChangeInstallationDirectory: true + deleteAppDataOnUninstall: true linux: target: - AppImage diff --git a/package.json b/package.json index 364386d..d6a5d9a 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "pack": "pnpm build && node ./scripts/run-electron-builder.cjs --dir", "dist": "pnpm build && node ./scripts/run-electron-builder.cjs", "dist:mac": "pnpm build && node ./scripts/run-electron-builder.cjs --mac", + "dist:win": "pnpm build && node ./scripts/run-electron-builder.cjs --win", "release:mac": "bash ./scripts/release.sh", "preview": "electron-vite preview", "typecheck": "tsc --noEmit && tsc -p tsconfig.node.json --noEmit", diff --git a/resources/icon.icns b/resources/icon.icns index 100b47a..259890f 100644 Binary files a/resources/icon.icns and b/resources/icon.icns differ diff --git a/resources/icon.ico b/resources/icon.ico new file mode 100644 index 0000000..32d376a Binary files /dev/null and b/resources/icon.ico differ diff --git a/resources/icon.png b/resources/icon.png index fe08369..d94511d 100644 Binary files a/resources/icon.png and b/resources/icon.png differ diff --git a/resources/icon.svg b/resources/icon.svg index 9892c27..cd0015a 100644 --- a/resources/icon.svg +++ b/resources/icon.svg @@ -1,38 +1,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + \ No newline at end of file diff --git a/src/main/index.ts b/src/main/index.ts index 475ea8e..1b5b22b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -26,6 +26,7 @@ function createWindow(): void { minHeight: 600, titleBarStyle: "hiddenInset", trafficLightPosition: { x: 12, y: 11 }, + autoHideMenuBar: process.platform === "win32", webPreferences: { preload: join(__dirname, "../preload/index.js"), contextIsolation: true, diff --git a/src/main/update/update-service.ts b/src/main/update/update-service.ts index 5acce13..e61098e 100644 --- a/src/main/update/update-service.ts +++ b/src/main/update/update-service.ts @@ -102,7 +102,7 @@ export function createUpdateService({ isPackaged = false, }: CreateUpdateServiceOptions): UpdateService { const listeners = new Set<(state: UpdateState) => void>(); - const supported = platform === "darwin" && isPackaged; + const supported = (platform === "darwin" || platform === "win32") && isPackaged; let state = createDefaultUpdateState( currentVersion, supported ? null : UNSUPPORTED_AUTO_UPDATE_MESSAGE, diff --git a/src/renderer/src/components/content-block.tsx b/src/renderer/src/components/content-block.tsx index 546783b..f2de611 100644 --- a/src/renderer/src/components/content-block.tsx +++ b/src/renderer/src/components/content-block.tsx @@ -14,7 +14,7 @@ export function ContentBlock({ block }: ContentBlockProps) { const text = block.text ?? ""; return ( -
+
{text}
); @@ -36,7 +36,7 @@ export function ContentBlock({ block }: ContentBlockProps) { Thinking
{expanded && ( -
+
{block.text}
)} diff --git a/src/renderer/src/components/context-chip.tsx b/src/renderer/src/components/context-chip.tsx index a9f3f81..785b314 100644 --- a/src/renderer/src/components/context-chip.tsx +++ b/src/renderer/src/components/context-chip.tsx @@ -107,7 +107,7 @@ export function ContextChip({
{expanded && (
-
+          
             {content}
           
diff --git a/src/renderer/src/components/message-block.tsx b/src/renderer/src/components/message-block.tsx index 25172df..7bd10d5 100644 --- a/src/renderer/src/components/message-block.tsx +++ b/src/renderer/src/components/message-block.tsx @@ -173,7 +173,7 @@ export function MessageBlock({ message, rawMode }: MessageBlockProps) { ))} ) : ( -
+
{previewText}
)} diff --git a/src/renderer/src/components/other-view.tsx b/src/renderer/src/components/other-view.tsx index 8ca4bb7..4fdcb0a 100644 --- a/src/renderer/src/components/other-view.tsx +++ b/src/renderer/src/components/other-view.tsx @@ -56,7 +56,7 @@ function extractInjectedItems(messages: NormalizedMessage[]): InjectedItem[] { function SectionContent({ section }: { section: InspectorSection }) { if (section.kind === "text") { return ( -
+      
         {section.text}
       
); diff --git a/src/renderer/src/components/tools-view.tsx b/src/renderer/src/components/tools-view.tsx index 52d7be8..5cfb4ea 100644 --- a/src/renderer/src/components/tools-view.tsx +++ b/src/renderer/src/components/tools-view.tsx @@ -111,7 +111,7 @@ function ToolItem({ tool, rawMode }: { tool: NormalizedTool; rawMode: boolean }) Description
{descExpanded && ( -
+
{tool.description}
)} diff --git a/src/renderer/src/components/ui/markdown-renderer.tsx b/src/renderer/src/components/ui/markdown-renderer.tsx index 10a8cd6..86fcd5e 100644 --- a/src/renderer/src/components/ui/markdown-renderer.tsx +++ b/src/renderer/src/components/ui/markdown-renderer.tsx @@ -8,13 +8,73 @@ interface MarkdownRendererProps { className?: string; } +function normalizeXmlContent(text: string): string { + const lines = text.split('\n'); + const result: string[] = []; + let xmlDepth = 0; + let inXmlBlock = false; + + for (const line of lines) { + const trimmed = line.trim(); + + // Detect XML tags + const hasOpenTag = /<[\w-]+>/.test(trimmed); + const hasCloseTag = /<\/[\w-]+>/.test(trimmed); + const isStandaloneOpenTag = /^<[\w-]+>$/.test(trimmed); + const isStandaloneCloseTag = /^<\/[\w-]+>$/.test(trimmed); + + // Track if we're inside an XML block + if (hasOpenTag || hasCloseTag) { + inXmlBlock = true; + } + + // For lines inside XML blocks, normalize indentation + if (inXmlBlock) { + // Empty lines + if (!trimmed) { + result.push(''); + continue; + } + + // Standalone closing tag - decrease depth first + if (isStandaloneCloseTag) { + xmlDepth = Math.max(0, xmlDepth - 1); + const indent = '\u00A0\u00A0'.repeat(xmlDepth); + result.push(indent + trimmed); + continue; + } + + // Standalone opening tag + if (isStandaloneOpenTag) { + const indent = '\u00A0\u00A0'.repeat(xmlDepth); + result.push(indent + trimmed); + xmlDepth++; + continue; + } + + // Content inside XML tags - use current depth + const indent = '\u00A0\u00A0'.repeat(xmlDepth); + result.push(indent + trimmed); + } else { + // Outside XML blocks, preserve original line + result.push(line); + } + } + + return result.join('\n'); +} + export function MarkdownRenderer({ content, className }: MarkdownRendererProps) { + // Normalize XML content indentation, then escape tags + let processedContent = normalizeXmlContent(content); + processedContent = processedContent.replace(//g, '>'); + const components: Components = { // Custom component styles to match theme - h1: ({ node, ...props }) =>

, - h2: ({ node, ...props }) =>

, - h3: ({ node, ...props }) =>

, - p: ({ node, ...props }) =>

, + h1: ({ node, ...props }) =>

, + h2: ({ node, ...props }) =>

, + h3: ({ node, ...props }) =>

, + p: ({ node, ...props }) =>

, code: ({ node, className: codeClassName, children, ...props }) => { const match = /language-(\w+)/.exec(codeClassName || ''); const isInline = !match; @@ -24,14 +84,28 @@ export function MarkdownRenderer({ content, className }: MarkdownRendererProps) return {children}; }, a: ({ node, ...props }) => , - ul: ({ node, ...props }) =>