Keyword Cannibalization Fixer
Run ID: 69c94f2aa17964d77e86d8d42026-03-29SEO
PantheraHive BOS
BOS Dashboard

Identify and resolve keyword cannibalization issues where multiple pages compete for the same terms.

Workflow Step 1 of 2: Identify Keyword Cannibalization Issues

This document outlines the identification of potential keyword cannibalization issues on your website. Keyword cannibalization occurs when multiple pages on your site target and rank for the same or very similar keywords, effectively competing against each other in search engine results. This dilutes your authority, confuses search engines about which page is most relevant, and can lead to lower overall rankings and traffic.

1. Understanding Keyword Cannibalization

Keyword cannibalization is a common SEO challenge that can inadvertently arise from:

  • Extensive Content Creation: Producing many articles around similar topics.
  • Poorly Defined Content Strategy: Lack of clear keyword mapping for each page.
  • Website Structure Issues: Overlapping category, tag, and product/service pages.
  • Outdated Content Management: Not updating or consolidating old content.

Why it's critical to fix:

  • Diluted Page Authority: Instead of one strong page, you have multiple weaker pages.
  • Confused Search Engines: Google struggles to determine the most authoritative page, potentially ranking none optimally.
  • Lower Rankings: Your competing pages might rank lower than they would individually.
  • Wasted Crawl Budget: Search engines spend resources crawling similar pages.
  • Inaccurate Analytics: Difficulty in attributing conversions and performance to the correct page.
  • Suboptimal User Experience: Users might land on a less relevant page.

2. Methodology for Identification

To accurately identify keyword cannibalization, we employ a systematic approach involving data collection and analysis. While a full analysis requires access to your specific website data, this section details the standard methodology and provides illustrative examples of identified issues.

2.1. Data Collection & Integration

The following data sources are crucial for a comprehensive analysis:

  • Google Search Console (GSC) Data:

* Performance Report: Queries, clicks, impressions, CTR, average position, and most importantly, the URLs ranking for specific queries. This is the primary source for identifying multiple URLs ranking for the same keyword.

* Sitemaps: To understand all indexed pages.

  • Website Content Audit:

* Page Content: Detailed review of on-page text, headings (H1s, H2s), meta titles, and descriptions for keyword targeting.

* Internal Linking Structure: How pages link to each other and what anchor text is used.

* Information Architecture: Overall site structure, categories, tags, and topic clusters.

  • SEO Tools Data (e.g., SEMrush, Ahrefs, Moz):

* Keyword Rankings: Tracking which URLs rank for target keywords over time.

* Site Audit Reports: Identifying pages with similar content or keyword targeting.

* Competitor Analysis: Understanding how competitors structure their content.

  • Google Analytics Data:

* Page Performance: Understanding traffic and engagement metrics for potentially cannibalizing pages.

2.2. Analysis Techniques

Once data is collected, we apply the following analytical techniques:

  1. GSC Query-URL Mapping:

* Filter GSC performance reports to identify queries where multiple URLs from your domain appear in the search results (even if one is on page 1 and another on page 3).

* Pay close attention to queries showing fluctuating primary URLs or multiple URLs receiving impressions for the same term.

  1. Keyword-Page Mapping Review:

* Cross-reference your intended keyword-to-page mapping with actual ranking data.

* Identify instances where two or more pages were intentionally or unintentionally optimized for the same primary keyword.

  1. Content Overlap Analysis:

* Manually review the content of identified competing pages.

* Assess the degree of topic overlap, target audience, and keyword usage. Are they providing unique value or largely redundant information?

  1. Internal Link Audit:

* Examine internal links pointing to competing pages. Are multiple pages being linked with the same exact anchor text for a specific keyword? This can send mixed signals to search engines.

  1. SERP Analysis:

* Perform manual searches for your target keywords. Observe which of your pages appear in the results and their relative positions. This provides a real-time snapshot of the problem.

3. Identified Potential Keyword Cannibalization Issues (Hypothetical Examples)

Based on the described methodology, here are some hypothetical examples of keyword cannibalization issues that could be identified on a typical website. These examples illustrate the type of output you would receive from a real analysis, detailing the conflicting pages, the target keyword, and the potential impact.


Issue 1: Overlapping Blog Posts

  • Target Keyword: "best running shoes for marathon"
  • Competing URLs:

* example.com/blog/top-marathon-running-shoes-2023 (Published: Jan 2023)

* example.com/blog/ultimate-guide-marathon-footwear (Published: May 2022)

  • Potential Impact:

* Both pages are vying for the same search intent and keyword, diluting authority.

* The older "Ultimate Guide" may be losing relevance, but still competes with the newer, more up-to-date post.

* Search engines might struggle to decide which page is more authoritative for current marathon shoe recommendations, leading to lower rankings for both than if one page consolidated the information.

  • Initial Observation: GSC data shows fluctuating rankings between these two URLs for "best running shoes for marathon" and related long-tail queries. Content review confirms significant overlap in product recommendations and advice.

Issue 2: Category Page vs. Product/Service Page

  • Target Keyword: "CRM software for small business"
  • Competing URLs:

* example.com/products/crm-software (Category Page listing various CRM solutions)

* example.com/services/small-business-crm-solutions (Specific Service Page)

  • Potential Impact:

The category page should ideally rank for broader terms like "CRM software," while the service page should target more specific, conversion-oriented terms related to your* solution.

* Here, both are competing for a mid-funnel term, potentially confusing users and search engines about which page best serves the user's intent. The category page might be too general, and the service page might lack the breadth of options users expect for a general "CRM software" search.

  • Initial Observation: Search Console shows both URLs receiving impressions and clicks for "CRM software for small business." The category page often outranks the service page, but the service page has higher conversion potential if it were to rank for its specific offerings.

Issue 3: Landing Page vs. Blog Post (Lead Generation)

  • Target Keyword: "how to improve website speed"
  • Competing URLs:

* example.com/landing-page/website-speed-optimization-guide (Lead magnet landing page)

* example.com/blog/10-ways-to-boost-website-performance (Informational blog post)

  • Potential Impact:

* Both pages provide information on website speed, but with different ultimate goals (lead generation vs. informational content).

The blog post might be more comprehensive and detailed for a user seeking information*, while the landing page might be too concise or gated.

* Search engines might prioritize the blog post for informational queries, reducing visibility for the landing page that aims to capture leads.

  • Initial Observation: GSC indicates both pages appearing in SERP for "how to improve website speed," with the blog post consistently ranking higher but the landing page showing lower, inconsistent rankings.

Issue 4: Multiple Product Review Pages

  • Target Keyword: "best noise cancelling headphones"
  • Competing URLs:

* example.com/reviews/bose-qc45-review (Specific product review)

* example.com/reviews/sony-wh1000xm5-review (Specific product review)

* example.com/reviews/noise-cancelling-headphones-comparison (Comparison article)

  • Potential Impact:

* While individual product reviews are distinct, the comparison page directly competes for the broad "best noise cancelling headphones" term.

* If the individual reviews are also heavily optimized for "best noise cancelling headphones" (e.g., in their meta titles), they can dilute the authority of the dedicated comparison page.

* The comparison page should be the primary authority for the broad "best" term, while individual reviews target specific model names.

  • Initial Observation: GSC shows all three URLs receiving impressions for "best noise cancelling headphones." The comparison page's ranking fluctuates, and individual product reviews sometimes appear for the broad "best" term, indicating an unoptimized structure.

4. Next Steps: Resolution Strategy (Step 2 of 2)

Now that potential keyword cannibalization issues have been identified (or the methodology for identification has been established), the next crucial step is to develop and implement a resolution strategy.

Step 2 will focus on:

  • Determining the Canonical Page: Identifying which page should be the primary authority for each keyword.
  • Implementing Fixes: Utilizing various SEO techniques such as:

* Content Consolidation/Merging: Combining weaker pages into a stronger, comprehensive one.

* Canonicalization: Using rel="canonical" tags to signal the preferred URL.

* Redirections: Implementing 301 redirects for outdated or less important pages.

* Content Optimization: Reworking content to target distinct keywords and user intents.

* Internal Linking Adjustments: Directing link equity to the preferred page.

* Noindexing: Removing irrelevant pages from search engine indexing.

  • Monitoring and Reporting: Tracking the impact of implemented changes on rankings and traffic.

This comprehensive identification serves as the foundation for a targeted and effective resolution strategy, ensuring your website's content works synergistically rather than competitively.

gemini Output

Keyword Cannibalization Fixer: Resolution Strategies & Action Plan

This document outlines a comprehensive strategy to identify, prioritize, and resolve keyword cannibalization issues across your website. Keyword cannibalization occurs when multiple pages on your site compete for the same or very similar keywords, confusing search engines and diluting your pages' authority. Resolving these issues is crucial for improving search engine rankings, increasing organic traffic, and enhancing user experience.


Understanding Keyword Cannibalization

Definition: Keyword cannibalization is a phenomenon where two or more pages on your website are optimized for the same keyword or a very similar set of keywords, leading to competition between your own pages in search engine results.

Negative Impacts:

  • Diluted Page Authority: Instead of one strong page ranking for a keyword, search engines see multiple weaker pages, splitting authority and making it harder for any single page to rank highly.
  • Confused Search Engines: Google's algorithms may struggle to determine which page is most relevant, leading to inconsistent rankings, fluctuating positions, or ranking a less optimal page.
  • Wasted Crawl Budget: Search engine crawlers spend time indexing and evaluating multiple similar pages, potentially missing more important content.
  • Lower Click-Through Rate (CTR): If multiple pages appear in SERPs, users might click on a less relevant or authoritative page, or simply get confused by the options.
  • Reduced Conversion Rates: Users landing on a less optimized page might have a poorer experience, leading to lower engagement and conversion rates.
  • Internal Link Dilution: Internal links pointing to multiple pages with similar intent can weaken the overall link equity flow to your primary target page.

Phase 1: Identification of Keyword Cannibalization Issues

Identifying cannibalization requires a systematic approach using various tools and data sources.

1. Google Search Console (GSC) Analysis

  • Access: Navigate to Performance > Search results > Queries.
  • Method:

1. Identify a core keyword you suspect might be cannibalized.

2. Filter the "Queries" report by this specific keyword.

3. Click on the keyword to see the Pages tab.

4. Look for: Multiple URLs appearing for the exact same query or very similar queries over time. Pay attention to fluctuating URLs, where different pages rank for the same term on different days.

  • Actionable Insight: This directly shows which pages Google considers relevant for a given query. If you see more than one, it's a strong indicator.

2. SEO Tool Analysis (e.g., SEMrush, Ahrefs, Moz)

  • Method 1: Organic Research/Keyword Explorer:

1. Enter your domain into the tool's "Organic Research" or "Site Explorer" section.

2. Go to "Positions" or "Organic Keywords" report.

3. Export the entire list of keywords and their ranking URLs.

4. Look for: Filter this data by keyword. Identify instances where the same keyword (or very close variations) is associated with multiple ranking URLs.

  • Method 2: Site Audit Features:

1. Run a comprehensive site audit using the tool.

2. Many tools have specific checks for "multiple pages ranking for the same keyword" or "duplicate content issues."

  • Actionable Insight: These tools provide a macro view of your entire keyword portfolio, making it easier to spot patterns of cannibalization at scale.

3. Manual Site Search

  • Method: Use Google's site: operator: site:yourdomain.com "your target keyword"
  • Look for: Observe how many pages Google returns for that exact keyword. If multiple pages appear that seem to target the same primary intent, it's a potential issue.
  • Actionable Insight: This simulates how Google indexes and sees your content, offering a quick spot-check.

4. Content Audit & Intent Mapping

  • Method: Create a spreadsheet mapping all your important pages to their primary target keyword and their intended user search intent (e.g., informational, transactional, navigational, commercial investigation).
  • Look for: Pages with identical target keywords and identical search intent.
  • Actionable Insight: This proactive approach helps identify potential cannibalization before it impacts rankings and clarifies the purpose of each page.

Phase 2: Prioritization of Cannibalization Issues

Not all cannibalization issues are created equal. Prioritize fixes based on potential impact and effort.

Prioritization Criteria:

  1. Business Impact: Does the cannibalized keyword relate to a core product/service, high-value conversion, or significant revenue stream? (High Priority)
  2. Search Volume: Is the keyword a high-volume term that could bring substantial organic traffic? (High Priority)
  3. Ranking Potential: Are the cannibalizing pages already ranking reasonably well (e.g., page 2-3) and could achieve page 1 with a fix?
  4. Ranking Volatility: Are the pages frequently swapping positions for the target keyword? This indicates high confusion for search engines.
  5. Ease of Fix: How complex will the resolution be? (e.g., simple redirect vs. major content overhaul).
  6. Page Authority/Backlinks: Which of the cannibalizing pages has the most backlinks or highest domain authority? This can inform which page to keep/strengthen.

Phase 3: Resolution Strategies for Keyword Cannibalization

Once identified and prioritized, implement one or more of the following strategies to resolve cannibalization.

1. Content Consolidation & Merging (Recommended for High Overlap)

  • When to Use: When two or more pages cover nearly identical topics, target the exact same user intent, and one page is significantly weaker or outdated.
  • How to Execute:

1. Identify the Strongest Page: Choose the page with the highest authority (more backlinks), best rankings, and most comprehensive content. This will be your primary page.

2. Merge Content: Take the valuable, unique content from the weaker page(s) and integrate it into the strongest page. Ensure the consolidated page is comprehensive, well-structured, and provides a superior user experience.

3. Implement 301 Redirects: Set up permanent 301 redirects from the URLs of the weaker, now-deleted/merged pages to the URL of the consolidated, strongest page. This passes link equity and tells search engines the content has permanently moved.

4. Update Internal Links: Update all internal links that previously pointed to the weaker pages to now point to the consolidated URL.

  • Outcome: Creates one authoritative, comprehensive resource for the target keyword, consolidating link equity and improving ranking potential.

2. Content De-optimization & Restructuring (Recommended for Partial Overlap)

  • When to Use: When pages have some overlap but could serve distinct user intents or target slightly different long-tail keyword variations. You want to keep both pages but differentiate them.
  • How to Execute:

1. Define Unique Intent: Clearly identify a distinct, specific user intent or a long-tail keyword variation for each page.

2. Re-optimize Weaker Page: Adjust the title tag, meta description, H1 heading, and body content of the "cannibalizing" page to focus solely on its new, distinct keyword and intent. Remove instances of the primary target keyword from this page.

3. Strengthen Primary Page: Ensure the primary target page is unequivocally optimized for the main keyword.

4. Internal Linking: Use clear, descriptive anchor text for internal links that guides users and search engines to the most relevant page for a specific query.

  • Outcome: Each page serves a unique purpose, preventing internal competition and allowing both to rank for their respective target terms.

3. Implement Canonical Tags (Use with Caution for Duplicates)

  • When to Use: When pages are extremely similar or near-duplicates, but you must keep both URLs for specific reasons (e.g., different sorting options, print versions, slight regional variations, A/B testing). This tells search engines which version is the "master."
  • How to Execute: Add a <link rel="canonical" href="[preferred_URL]"> tag in the <head> section of the non-preferred page, pointing to the URL of the preferred, authoritative version.
  • Important Note: Canonical tags are a suggestion to search engines, not a directive. They are best for near-duplicate content, not for pages with genuinely different content that happen to share keywords. 301 redirects are generally stronger for consolidating authority.
  • Outcome: Search engines consolidate ranking signals to the canonical URL, preventing the non-canonical page from competing.

4. Adjust Internal Linking Strategy

  • When to Use: As a supplementary strategy to reinforce your chosen authoritative page for a specific keyword.
  • How to Execute:

1. Review Anchor Text: Audit your internal links. Ensure that all internal links using the target keyword as anchor text point only to the preferred, authoritative page for that keyword.

2. Diversify Anchor Text: For links to other pages, use varied and descriptive anchor text that reflects the specific content of the destination page, avoiding the primary target keyword of your authoritative page.

3. Contextual Linking: Place internal links naturally within relevant content to provide context and guide users and search engines.

  • Outcome: Strengthens the authority of your primary page and helps search engines understand the intended relevance of each page.

5. No-index (Use Sparingly)

  • When to Use: If a page is truly low-value, thin, or a pure duplicate that you don't want indexed at all, and you cannot merge or redirect it. This should be a last resort for cannibalization.
  • How to Execute: Add <meta name="robots" content="noindex"> to the <head> section of the page you wish to remove from the search index.
  • Important Note: This completely removes the page from search results. Ensure you are not no-indexing content that could potentially be useful or linked to by external sites.

Phase 4: Monitoring and Maintenance

Resolving keyword cannibalization is an ongoing process.

1. Post-Implementation Monitoring

  • Google Search Console: Continuously monitor the "Performance" report for the affected keywords. Look for improvements in rankings, impressions, and clicks for your preferred URL. Ensure that the cannibalizing URLs no longer appear for the target keyword.
  • Rank Tracking Tools: Track the keywords daily or weekly to observe ranking stability and improvements for the intended page.
  • Analytics (Google Analytics 4): Monitor organic traffic to the consolidated or re-optimized pages to see if the changes have a positive impact on user engagement and conversions.

2. Regular Audits

  • Monthly/Quarterly Checks: Revisit the identification steps (GSC, SEO tools) on a regular basis to catch new instances of cannibalization, especially after launching new content or making site-wide changes.
  • New Content Strategy: Implement a clear content strategy that emphasizes unique keyword targeting and user intent for every new page created. This preventative measure is key to avoiding future cannibalization.

Next Steps & Recommendations

To proceed with the "Keyword Cannibalization Fixer" workflow, we recommend the following:

  1. Data Access: Provide access to
Copy all content
Full output as text
Download ZIP
IDE-ready project ZIP
Copy share link
Permanent URL for this run
Get Embed Code
Embed this result on any website
Print / Save PDF
Use browser print dialog
\n\n\n"); var hasSrcMain=Object.keys(extracted).some(function(k){return k.indexOf("src/main")>=0;}); if(!hasSrcMain) zip.file(folder+"src/main."+ext,"import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './index.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n \n \n \n)\n"); var hasSrcApp=Object.keys(extracted).some(function(k){return k==="src/App."+ext||k==="App."+ext;}); if(!hasSrcApp) zip.file(folder+"src/App."+ext,"import React from 'react'\nimport './App.css'\n\nfunction App(){\n return(\n
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n
\n )\n}\nexport default App\n"); zip.file(folder+"src/index.css","*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#f0f2f5;color:#1a1a2e}\n.app{min-height:100vh;display:flex;flex-direction:column}\n.app-header{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:40px}\nh1{font-size:2.5rem;font-weight:700}\n"); zip.file(folder+"src/App.css",""); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/pages/.gitkeep",""); zip.file(folder+"src/hooks/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\n## Open in IDE\nOpen the project folder in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Vue (Vite + Composition API + TypeScript) --- */ function buildVue(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "vue-tsc -b && vite build",\n "preview": "vite preview"\n },\n "dependencies": {\n "vue": "^3.5.13",\n "vue-router": "^4.4.5",\n "pinia": "^2.3.0",\n "axios": "^1.7.9"\n },\n "devDependencies": {\n "@vitejs/plugin-vue": "^5.2.1",\n "typescript": "~5.7.3",\n "vite": "^6.0.5",\n "vue-tsc": "^2.2.0"\n }\n}\n'); zip.file(folder+"vite.config.ts","import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { resolve } from 'path'\n\nexport default defineConfig({\n plugins: [vue()],\n resolve: { alias: { '@': resolve(__dirname,'src') } }\n})\n"); zip.file(folder+"tsconfig.json",'{"files":[],"references":[{"path":"./tsconfig.app.json"},{"path":"./tsconfig.node.json"}]}\n'); zip.file(folder+"tsconfig.app.json",'{\n "compilerOptions":{\n "target":"ES2020","useDefineForClassFields":true,"module":"ESNext","lib":["ES2020","DOM","DOM.Iterable"],\n "skipLibCheck":true,"moduleResolution":"bundler","allowImportingTsExtensions":true,\n "isolatedModules":true,"moduleDetection":"force","noEmit":true,"jsxImportSource":"vue",\n "strict":true,"paths":{"@/*":["./src/*"]}\n },\n "include":["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"]\n}\n'); zip.file(folder+"env.d.ts","/// \n"); zip.file(folder+"index.html","\n\n\n \n \n "+slugTitle(pn)+"\n\n\n
\n \n\n\n"); var hasMain=Object.keys(extracted).some(function(k){return k==="src/main.ts"||k==="main.ts";}); if(!hasMain) zip.file(folder+"src/main.ts","import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\nimport './assets/main.css'\n\nconst app = createApp(App)\napp.use(createPinia())\napp.mount('#app')\n"); var hasApp=Object.keys(extracted).some(function(k){return k.indexOf("App.vue")>=0;}); if(!hasApp) zip.file(folder+"src/App.vue","\n\n\n\n\n"); zip.file(folder+"src/assets/main.css","*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,sans-serif;background:#fff;color:#213547}\n"); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/views/.gitkeep",""); zip.file(folder+"src/stores/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\nOpen in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Angular (v19 standalone) --- */ function buildAngular(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var sel=pn.replace(/_/g,"-"); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "scripts": {\n "ng": "ng",\n "start": "ng serve",\n "build": "ng build",\n "test": "ng test"\n },\n "dependencies": {\n "@angular/animations": "^19.0.0",\n "@angular/common": "^19.0.0",\n "@angular/compiler": "^19.0.0",\n "@angular/core": "^19.0.0",\n "@angular/forms": "^19.0.0",\n "@angular/platform-browser": "^19.0.0",\n "@angular/platform-browser-dynamic": "^19.0.0",\n "@angular/router": "^19.0.0",\n "rxjs": "~7.8.0",\n "tslib": "^2.3.0",\n "zone.js": "~0.15.0"\n },\n "devDependencies": {\n "@angular-devkit/build-angular": "^19.0.0",\n "@angular/cli": "^19.0.0",\n "@angular/compiler-cli": "^19.0.0",\n "typescript": "~5.6.0"\n }\n}\n'); zip.file(folder+"angular.json",'{\n "$schema": "./node_modules/@angular/cli/lib/config/schema.json",\n "version": 1,\n "newProjectRoot": "projects",\n "projects": {\n "'+pn+'": {\n "projectType": "application",\n "root": "",\n "sourceRoot": "src",\n "prefix": "app",\n "architect": {\n "build": {\n "builder": "@angular-devkit/build-angular:application",\n "options": {\n "outputPath": "dist/'+pn+'",\n "index": "src/index.html",\n "browser": "src/main.ts",\n "tsConfig": "tsconfig.app.json",\n "styles": ["src/styles.css"],\n "scripts": []\n }\n },\n "serve": {"builder":"@angular-devkit/build-angular:dev-server","configurations":{"production":{"buildTarget":"'+pn+':build:production"},"development":{"buildTarget":"'+pn+':build:development"}},"defaultConfiguration":"development"}\n }\n }\n }\n}\n'); zip.file(folder+"tsconfig.json",'{\n "compileOnSave": false,\n "compilerOptions": {"baseUrl":"./","outDir":"./dist/out-tsc","forceConsistentCasingInFileNames":true,"strict":true,"noImplicitOverride":true,"noPropertyAccessFromIndexSignature":true,"noImplicitReturns":true,"noFallthroughCasesInSwitch":true,"paths":{"@/*":["src/*"]},"skipLibCheck":true,"esModuleInterop":true,"sourceMap":true,"declaration":false,"experimentalDecorators":true,"moduleResolution":"bundler","importHelpers":true,"target":"ES2022","module":"ES2022","useDefineForClassFields":false,"lib":["ES2022","dom"]},\n "references":[{"path":"./tsconfig.app.json"}]\n}\n'); zip.file(folder+"tsconfig.app.json",'{\n "extends":"./tsconfig.json",\n "compilerOptions":{"outDir":"./dist/out-tsc","types":[]},\n "files":["src/main.ts"],\n "include":["src/**/*.d.ts"]\n}\n'); zip.file(folder+"src/index.html","\n\n\n \n "+slugTitle(pn)+"\n \n \n \n\n\n \n\n\n"); zip.file(folder+"src/main.ts","import { bootstrapApplication } from '@angular/platform-browser';\nimport { appConfig } from './app/app.config';\nimport { AppComponent } from './app/app.component';\n\nbootstrapApplication(AppComponent, appConfig)\n .catch(err => console.error(err));\n"); zip.file(folder+"src/styles.css","* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { font-family: system-ui, -apple-system, sans-serif; background: #f9fafb; color: #111827; }\n"); var hasComp=Object.keys(extracted).some(function(k){return k.indexOf("app.component")>=0;}); if(!hasComp){ zip.file(folder+"src/app/app.component.ts","import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\n@Component({\n selector: 'app-root',\n standalone: true,\n imports: [RouterOutlet],\n templateUrl: './app.component.html',\n styleUrl: './app.component.css'\n})\nexport class AppComponent {\n title = '"+pn+"';\n}\n"); zip.file(folder+"src/app/app.component.html","
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n \n
\n"); zip.file(folder+"src/app/app.component.css",".app-header{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;gap:16px}h1{font-size:2.5rem;font-weight:700;color:#6366f1}\n"); } zip.file(folder+"src/app/app.config.ts","import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';\nimport { provideRouter } from '@angular/router';\nimport { routes } from './app.routes';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n provideZoneChangeDetection({ eventCoalescing: true }),\n provideRouter(routes)\n ]\n};\n"); zip.file(folder+"src/app/app.routes.ts","import { Routes } from '@angular/router';\n\nexport const routes: Routes = [];\n"); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nng serve\n# or: npm start\n\`\`\`\n\n## Build\n\`\`\`bash\nng build\n\`\`\`\n\nOpen in VS Code with Angular Language Service extension.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n.angular/\n"); } /* --- Python --- */ function buildPython(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var reqMap={"numpy":"numpy","pandas":"pandas","sklearn":"scikit-learn","tensorflow":"tensorflow","torch":"torch","flask":"flask","fastapi":"fastapi","uvicorn":"uvicorn","requests":"requests","sqlalchemy":"sqlalchemy","pydantic":"pydantic","dotenv":"python-dotenv","PIL":"Pillow","cv2":"opencv-python","matplotlib":"matplotlib","seaborn":"seaborn","scipy":"scipy"}; var reqs=[]; Object.keys(reqMap).forEach(function(k){if(src.indexOf("import "+k)>=0||src.indexOf("from "+k)>=0)reqs.push(reqMap[k]);}); var reqsTxt=reqs.length?reqs.join("\n"):"# add dependencies here\n"; zip.file(folder+"main.py",src||"# "+title+"\n# Generated by PantheraHive BOS\n\nprint(title+\" loaded\")\n"); zip.file(folder+"requirements.txt",reqsTxt); zip.file(folder+".env.example","# Environment variables\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n\`\`\`\n\n## Run\n\`\`\`bash\npython main.py\n\`\`\`\n"); zip.file(folder+".gitignore",".venv/\n__pycache__/\n*.pyc\n.env\n.DS_Store\n"); } /* --- Node.js --- */ function buildNode(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var depMap={"mongoose":"^8.0.0","dotenv":"^16.4.5","axios":"^1.7.9","cors":"^2.8.5","bcryptjs":"^2.4.3","jsonwebtoken":"^9.0.2","socket.io":"^4.7.4","uuid":"^9.0.1","zod":"^3.22.4","express":"^4.18.2"}; var deps={}; Object.keys(depMap).forEach(function(k){if(src.indexOf(k)>=0)deps[k]=depMap[k];}); if(!deps["express"])deps["express"]="^4.18.2"; var pkgJson=JSON.stringify({"name":pn,"version":"1.0.0","main":"src/index.js","scripts":{"start":"node src/index.js","dev":"nodemon src/index.js"},"dependencies":deps,"devDependencies":{"nodemon":"^3.0.3"}},null,2)+"\n"; zip.file(folder+"package.json",pkgJson); var fallback="const express=require(\"express\");\nconst app=express();\napp.use(express.json());\n\napp.get(\"/\",(req,res)=>{\n res.json({message:\""+title+" API\"});\n});\n\nconst PORT=process.env.PORT||3000;\napp.listen(PORT,()=>console.log(\"Server on port \"+PORT));\n"; zip.file(folder+"src/index.js",src||fallback); zip.file(folder+".env.example","PORT=3000\n"); zip.file(folder+".gitignore","node_modules/\n.env\n.DS_Store\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\n\`\`\`\n\n## Run\n\`\`\`bash\nnpm run dev\n\`\`\`\n"); } /* --- Vanilla HTML --- */ function buildVanillaHtml(zip,folder,app,code){ var title=slugTitle(app); var isFullDoc=code.trim().toLowerCase().indexOf("=0||code.trim().toLowerCase().indexOf("=0; var indexHtml=isFullDoc?code:"\n\n\n\n\n"+title+"\n\n\n\n"+code+"\n\n\n\n"; zip.file(folder+"index.html",indexHtml); zip.file(folder+"style.css","/* "+title+" — styles */\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#fff;color:#1a1a2e}\n"); zip.file(folder+"script.js","/* "+title+" — scripts */\n"); zip.file(folder+"assets/.gitkeep",""); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Open\nDouble-click \`index.html\` in your browser.\n\nOr serve locally:\n\`\`\`bash\nnpx serve .\n# or\npython3 -m http.server 3000\n\`\`\`\n"); zip.file(folder+".gitignore",".DS_Store\nnode_modules/\n.env\n"); } /* ===== MAIN ===== */ var sc=document.createElement("script"); sc.src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"; sc.onerror=function(){ if(lbl)lbl.textContent="Download ZIP"; alert("JSZip load failed — check connection."); }; sc.onload=function(){ var zip=new JSZip(); var base=(_phFname||"output").replace(/\.[^.]+$/,""); var app=base.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"my_app"; var folder=app+"/"; var vc=document.getElementById("panel-content"); var panelTxt=vc?(vc.innerText||vc.textContent||""):""; var lang=detectLang(_phCode,panelTxt); if(_phIsHtml){ buildVanillaHtml(zip,folder,app,_phCode); } else if(lang==="flutter"){ buildFlutter(zip,folder,app,_phCode,panelTxt); } else if(lang==="react-native"){ buildReactNative(zip,folder,app,_phCode,panelTxt); } else if(lang==="swift"){ buildSwift(zip,folder,app,_phCode,panelTxt); } else if(lang==="kotlin"){ buildKotlin(zip,folder,app,_phCode,panelTxt); } else if(lang==="react"){ buildReact(zip,folder,app,_phCode,panelTxt); } else if(lang==="vue"){ buildVue(zip,folder,app,_phCode,panelTxt); } else if(lang==="angular"){ buildAngular(zip,folder,app,_phCode,panelTxt); } else if(lang==="python"){ buildPython(zip,folder,app,_phCode); } else if(lang==="node"){ buildNode(zip,folder,app,_phCode); } else { /* Document/content workflow */ var title=app.replace(/_/g," "); var md=_phAll||_phCode||panelTxt||"No content"; zip.file(folder+app+".md",md); var h=""+title+""; h+="

"+title+"

"; var hc=md.replace(/&/g,"&").replace(//g,">"); hc=hc.replace(/^### (.+)$/gm,"

$1

"); hc=hc.replace(/^## (.+)$/gm,"

$1

"); hc=hc.replace(/^# (.+)$/gm,"

$1

"); hc=hc.replace(/\*\*(.+?)\*\*/g,"$1"); hc=hc.replace(/\n{2,}/g,"

"); h+="

"+hc+"

Generated by PantheraHive BOS
"; zip.file(folder+app+".html",h); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\nFiles:\n- "+app+".md (Markdown)\n- "+app+".html (styled HTML)\n"); } zip.generateAsync({type:"blob"}).then(function(blob){ var a=document.createElement("a"); a.href=URL.createObjectURL(blob); a.download=app+".zip"; a.click(); URL.revokeObjectURL(a.href); if(lbl)lbl.textContent="Download ZIP"; }); }; document.head.appendChild(sc); } function phShare(){navigator.clipboard.writeText(window.location.href).then(function(){var el=document.getElementById("ph-share-lbl");if(el){el.textContent="Link copied!";setTimeout(function(){el.textContent="Copy share link";},2500);}});}function phEmbed(){var runId=window.location.pathname.split("/").pop().replace(".html","");var embedUrl="https://pantherahive.com/embed/"+runId;var code='';navigator.clipboard.writeText(code).then(function(){var el=document.getElementById("ph-embed-lbl");if(el){el.textContent="Embed code copied!";setTimeout(function(){el.textContent="Get Embed Code";},2500);}});}