Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85965e1a00 | ||
|
|
b9ab4608fd | ||
|
|
213aa5c128 | ||
|
|
fd42eeade9 |
@@ -0,0 +1 @@
|
||||
/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e
|
||||
+151
-193
@@ -1,253 +1,211 @@
|
||||
# Graph Report - /home/agent/ai-agent/backend/workspace/73f46153-e8cc-447a-82ad-2eeefa00689f (2026-04-14)
|
||||
# Graph Report - 2e709500-79ab-403f-9ca0-3f027ac9f36e (2026-06-17)
|
||||
|
||||
## Corpus Check
|
||||
- 43 files · ~211,373 words
|
||||
- 56 files · ~324,046 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 163 nodes · 187 edges · 42 communities detected
|
||||
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS
|
||||
- 518 nodes · 563 edges · 91 communities (84 shown, 7 thin omitted)
|
||||
- Extraction: 99% EXTRACTED · 1% INFERRED · 0% AMBIGUOUS · INFERRED: 4 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Graph Freshness
|
||||
- Built from commit: `41a21312`
|
||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||
- Run `graphify update .` after code changes (no API cost).
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
- [[_COMMUNITY_Community 0|Community 0]]
|
||||
- [[_COMMUNITY_Community 1|Community 1]]
|
||||
- [[_COMMUNITY_Community 2|Community 2]]
|
||||
- [[_COMMUNITY_Community 3|Community 3]]
|
||||
- [[_COMMUNITY_Community 4|Community 4]]
|
||||
- [[_COMMUNITY_Community 5|Community 5]]
|
||||
- [[_COMMUNITY_Community 6|Community 6]]
|
||||
- [[_COMMUNITY_Community 7|Community 7]]
|
||||
- [[_COMMUNITY_Community 9|Community 9]]
|
||||
- [[_COMMUNITY_Community 10|Community 10]]
|
||||
- [[_COMMUNITY_Community 12|Community 12]]
|
||||
- [[_COMMUNITY_Community 13|Community 13]]
|
||||
- [[_COMMUNITY_Community 14|Community 14]]
|
||||
- [[_COMMUNITY_Community 15|Community 15]]
|
||||
- [[_COMMUNITY_Community 16|Community 16]]
|
||||
- [[_COMMUNITY_Community 17|Community 17]]
|
||||
- [[_COMMUNITY_Community 18|Community 18]]
|
||||
- [[_COMMUNITY_Community 19|Community 19]]
|
||||
- [[_COMMUNITY_Community 20|Community 20]]
|
||||
- [[_COMMUNITY_Community 21|Community 21]]
|
||||
- [[_COMMUNITY_Community 22|Community 22]]
|
||||
- [[_COMMUNITY_Community 23|Community 23]]
|
||||
- [[_COMMUNITY_Community 24|Community 24]]
|
||||
- [[_COMMUNITY_Community 25|Community 25]]
|
||||
- [[_COMMUNITY_Community 26|Community 26]]
|
||||
- [[_COMMUNITY_Community 27|Community 27]]
|
||||
- [[_COMMUNITY_Community 28|Community 28]]
|
||||
- [[_COMMUNITY_Community 29|Community 29]]
|
||||
- [[_COMMUNITY_Community 30|Community 30]]
|
||||
- [[_COMMUNITY_Community 31|Community 31]]
|
||||
- [[_COMMUNITY_Community 32|Community 32]]
|
||||
- [[_COMMUNITY_Community 33|Community 33]]
|
||||
- [[_COMMUNITY_Community 34|Community 34]]
|
||||
- [[_COMMUNITY_Community 35|Community 35]]
|
||||
- [[_COMMUNITY_Community 40|Community 40]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `ContactPage` - 10 edges
|
||||
2. `NavigationPage` - 8 edges
|
||||
3. `main()` - 7 edges
|
||||
4. `trackEvent()` - 6 edges
|
||||
5. `captureException()` - 6 edges
|
||||
6. `captureMessage()` - 6 edges
|
||||
7. `POST()` - 5 edges
|
||||
8. `POST()` - 5 edges
|
||||
9. `initAllAnimations()` - 5 edges
|
||||
10. `log()` - 5 edges
|
||||
1. `Performance Testing Scripts` - 12 edges
|
||||
2. `captureException()` - 11 edges
|
||||
3. `ContactPage` - 10 edges
|
||||
4. `ContactPage` - 10 edges
|
||||
5. `Astro SSR Standalone Server` - 9 edges
|
||||
6. `Backup Scripts` - 9 edges
|
||||
7. `NavigationPage` - 8 edges
|
||||
8. `Changed - Domain Migration (2026-03-21)` - 8 edges
|
||||
9. `NavigationPage` - 8 edges
|
||||
10. `main()` - 7 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- None detected - all connections are within the same source files.
|
||||
- `sendContactEmail()` --calls--> `captureException()` [INFERRED]
|
||||
src/pages/api/contact.ts → src/utils/sentry.ts
|
||||
- `POST()` --calls--> `logApiRequest()` [INFERRED]
|
||||
src/pages/api/contact.ts → src/utils/logger.ts
|
||||
- `subscribeToNewsletter()` --calls--> `captureException()` [INFERRED]
|
||||
src/pages/api/newsletter.ts → src/utils/sentry.ts
|
||||
- `POST()` --calls--> `logApiRequest()` [INFERRED]
|
||||
src/pages/api/newsletter.ts → src/utils/logger.ts
|
||||
|
||||
## Communities
|
||||
## Communities (91 total, 7 thin omitted)
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.17
|
||||
Nodes (0):
|
||||
Cohesion: 0.05
|
||||
Nodes (37): Architecture, Astro SSR Standalone Server, Available Scripts, Build Failures, Build Process, code:bash (npm install), code:dockerfile (FROM node:18-alpine), code:block11 (.) (+29 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.22
|
||||
Nodes (1): ContactPage
|
||||
Cohesion: 0.05
|
||||
Nodes (37): 1. `performance_test.py`, 2. `quick-perf-check.sh`, 3. `lighthouse_audit.py`, Available Scripts, Basic Response Time, Best Practices, CI/CD Integration, code:bash (# Quick check (recommended)) (+29 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.56
|
||||
Nodes (8): captureException(), captureMessage(), generateEventId(), getEnvironment(), getRelease(), getSentryDsn(), parseDsn(), sendToSentry()
|
||||
Cohesion: 0.14
|
||||
Nodes (27): checkRateLimit(), corsHeaders(), escapeHtml(), OPTIONS(), POST(), sendContactEmail(), validateContactForm(), checkRateLimit() (+19 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.36
|
||||
Nodes (6): formatEntry(), getMinLevel(), log(), logApiRequest(), output(), shouldLog()
|
||||
Cohesion: 0.16
|
||||
Nodes (20): formatEntry(), getMinLevel(), log(), logApiRequest(), output(), shouldLog(), checkRateLimit(), corsHeaders() (+12 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.22
|
||||
Nodes (1): NavigationPage
|
||||
Cohesion: 0.09
|
||||
Nodes (22): [1.0.0] - Initial Release, Added, Added, API Routes, Changed, Changed - Domain Migration (2026-03-21), Changelog, Configuration (+14 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): checkRateLimit(), corsHeaders(), escapeHtml(), OPTIONS(), POST(), sendContactEmail(), validateContactForm()
|
||||
Cohesion: 0.09
|
||||
Nodes (21): Automation Setup, Available Commands, Backup fails, Backup Scripts, code:bash (# First-time setup), code:bash (# Edit crontab), code:powershell (# Daily backup), code:bash (# List available backups) (+13 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): 1. Modular Monolith Over Premature Microservices, 2. Role-Based Access Control (RBAC) from Day One, 3. Offline-Resilient Design, Architecture Decisions That Scale, Blue-Green Deployments, code:block1 (src/), code:typescript (// Example: Contextual data filtering), code:sql (-- Row-level tenant isolation) (+7 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.14
|
||||
Nodes (13): 1. Multi-District Deployment Complexity, 2. Security and Compliance, 3. Reliability Under Constraint, Architecture Decisions That Paid Off, code:java (@Configuration), code:java (@Service), code:java (@Component), code:yaml (# District-specific feature configuration) (+5 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.17
|
||||
Nodes (11): 1. Customer Service Automation, 2. Predictive Analytics, 3. Process Automation, 4. Intelligent Document Processing, code:python (from transformers import pipeline), code:python (import pandas as pd), code:python (# Example: Extracting structured data from forms), Implementation Considerations (+3 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.17
|
||||
Nodes (11): Code Example, code:astro (---), code:javascript (// Traditional SPA: Everything loads upfront), code:typescript (// src/content/config.ts), code:bash (npm create astro@latest), Content Collections: Type-Safe Content, Getting Started, How Astro Differs from Traditional Frameworks (+3 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.18
|
||||
Nodes (10): code:hcl (# Define AWS EC2 instance), code:dockerfile (# Multi-stage build for a Go application), code:yaml (# AWS Cost allocation tags), Common Pitfalls, Conclusion, Containerization as a Migration Strategy, Cost Optimization Strategies, Infrastructure as Code (+2 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.43
|
||||
Nodes (6): trackCaseStudyView(), trackEvent(), trackExternalLink(), trackFormSubmit(), trackNewsletterSignup(), trackPortfolioFilter()
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): extract_scores(), get_issues(), main(), Run Lighthouse audit on a URL, Extract key scores from Lighthouse data, Extract issues from Lighthouse audit, run_lighthouse()
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): captureScreenshot(), ensureDir(), generateIndex(), main(), screenshotPath(), setTheme(), waitForPageReady()
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.36
|
||||
Nodes (4): deleteFromDB(), getAllFromDB(), openDB(), processSyncQueue()
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.52
|
||||
Nodes (6): checkRateLimit(), corsHeaders(), OPTIONS(), POST(), subscribeToNewsletter(), validateEmail()
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): checkRateLimit(), corsHeaders(), escapeHtml(), OPTIONS(), POST(), sendContactEmail(), validateContactForm()
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.43
|
||||
Nodes (6): trackCaseStudyView(), trackEvent(), trackExternalLink(), trackFormSubmit(), trackNewsletterSignup(), trackPortfolioFilter()
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): extract_scores(), get_issues(), main(), Run Lighthouse audit on a URL, Extract key scores from Lighthouse data, Extract issues from Lighthouse audit, run_lighthouse()
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): captureScreenshot(), ensureDir(), generateIndex(), main(), screenshotPath(), setTheme(), waitForPageReady()
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.36
|
||||
Nodes (4): deleteFromDB(), getAllFromDB(), openDB(), processSyncQueue()
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.48
|
||||
Nodes (5): initAllAnimations(), initButtonRipple(), initCounters(), initProgressBars(), initScrollReveal()
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.48
|
||||
Nodes (5): extractJsonLd(), main(), validateOpenGraph(), validatePage(), validateTwitterCard()
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.48
|
||||
Nodes (5): initAllAnimations(), initButtonRipple(), initCounters(), initProgressBars(), initScrollReveal()
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.48
|
||||
Nodes (5): extractJsonLd(), main(), validateOpenGraph(), validatePage(), validateTwitterCard()
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.47
|
||||
Nodes (5): main(), measure_response_time(), print_stats(), Measure response time for a URL over multiple iterations, Print statistics for response times
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.4
|
||||
Nodes (2): loadWithTheme(), setStoredTheme()
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.47
|
||||
Nodes (5): main(), measure_response_time(), print_stats(), Measure response time for a URL over multiple iterations, Print statistics for response times
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.6
|
||||
Nodes (3): formatUptime(), GET(), percentile()
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.5
|
||||
Nodes (0):
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.5
|
||||
Nodes (0):
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 37 - "Community 37"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 38 - "Community 38"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 40 - "Community 40"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
Cohesion: 0.6
|
||||
Nodes (3): formatUptime(), GET(), percentile()
|
||||
|
||||
## Knowledge Gaps
|
||||
- **5 isolated node(s):** `Run Lighthouse audit on a URL`, `Extract key scores from Lighthouse data`, `Extract issues from Lighthouse audit`, `Measure response time for a URL over multiple iterations`, `Print statistics for response times`
|
||||
- **111 isolated node(s):** `Run Lighthouse audit on a URL`, `Extract key scores from Lighthouse data`, `Extract issues from Lighthouse audit`, `Measure response time for a URL over multiple iterations`, `Print statistics for response times` (+106 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **Thin community `Community 18`** (2 nodes): `sitemap.xml.ts`, `GET()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 19`** (2 nodes): `sitemap-index.xml.ts`, `GET()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 20`** (2 nodes): `health.json.ts`, `GET()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 21`** (1 nodes): `playwright.config.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 22`** (1 nodes): `env.d.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 23`** (1 nodes): `config.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 24`** (1 nodes): `windows-tasks.ps1`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 25`** (1 nodes): `e2e-blog-navigation.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 26`** (1 nodes): `e2e-critical-paths.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 27`** (1 nodes): `navigation.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 28`** (1 nodes): `contact-form.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 29`** (1 nodes): `e2e-form-interactions.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 30`** (1 nodes): `destructive-chaos.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 31`** (1 nodes): `security-headers.test.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 32`** (1 nodes): `bug-fix-verification.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 33`** (1 nodes): `pages.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 34`** (1 nodes): `api-integration.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 35`** (1 nodes): `static-assets.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 36`** (1 nodes): `accessibility.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 37`** (1 nodes): `cross-browser.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 38`** (1 nodes): `newsletter-subscription.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 39`** (1 nodes): `portfolio.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 40`** (1 nodes): `blog.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 41`** (1 nodes): `e2e-smoke-suite.spec.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **7 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Are the 2 inferred relationships involving `captureException()` (e.g. with `sendContactEmail()` and `subscribeToNewsletter()`) actually correct?**
|
||||
_`captureException()` has 2 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **What connects `Run Lighthouse audit on a URL`, `Extract key scores from Lighthouse data`, `Extract issues from Lighthouse audit` to the rest of the system?**
|
||||
_5 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
_111 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
- **Should `Community 0` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.05 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 1` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.05 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 2` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.14 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 4` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.09 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 5` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.09 - nodes in this community are weakly interconnected._
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_navigation_spec_ts", "label": "navigation.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/navigation.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_navigation_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/navigation.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_server_mjs", "label": "server.mjs", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/server.mjs", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_server_mjs", "target": "dotenv", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/server.mjs", "source_location": "L1", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_server_mjs", "target": "url", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/server.mjs", "source_location": "L2", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_server_mjs", "target": "path", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/server.mjs", "source_location": "L3", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_server_mjs", "target": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_dist_server_entry_mjs", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/server.mjs", "source_location": "L14", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_server_mjs", "target": "server_entry_handler", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/server.mjs", "source_location": "L14", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_server_mjs", "target": "express", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/server.mjs", "source_location": "L15", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_server_mjs", "target": "compression", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/server.mjs", "source_location": "L16", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_test_results_newsletter_subscription_ne_c1de9_isible_and_usable_on_mobile_chromium_error_context_md", "label": "error-context.md", "file_type": "document", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/test-results/newsletter-subscription-Ne-c1de9-isible-and-usable-on-mobile-chromium/error-context.md", "source_location": "L1"}, {"id": "newsletter_subscription_ne_c1de9_isible_and_usable_on_mobile_chromium_error_context_page_snapshot", "label": "Page snapshot", "file_type": "document", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/test-results/newsletter-subscription-Ne-c1de9-isible-and-usable-on-mobile-chromium/error-context.md", "source_location": "L1"}, {"id": "newsletter_subscription_ne_c1de9_isible_and_usable_on_mobile_chromium_error_context_codeblock_1", "label": "code:yaml (- generic [active] [ref=e1]:)", "file_type": "document", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/test-results/newsletter-subscription-Ne-c1de9-isible-and-usable-on-mobile-chromium/error-context.md", "source_location": "L3"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_test_results_newsletter_subscription_ne_c1de9_isible_and_usable_on_mobile_chromium_error_context_md", "target": "newsletter_subscription_ne_c1de9_isible_and_usable_on_mobile_chromium_error_context_page_snapshot", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/test-results/newsletter-subscription-Ne-c1de9-isible-and-usable-on-mobile-chromium/error-context.md", "source_location": "L1", "weight": 1.0}, {"source": "newsletter_subscription_ne_c1de9_isible_and_usable_on_mobile_chromium_error_context_page_snapshot", "target": "newsletter_subscription_ne_c1de9_isible_and_usable_on_mobile_chromium_error_context_codeblock_1", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/test-results/newsletter-subscription-Ne-c1de9-isible-and-usable-on-mobile-chromium/error-context.md", "source_location": "L3", "weight": 1.0}], "input_tokens": 0, "output_tokens": 0}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_e2e_form_interactions_spec_ts", "label": "e2e-form-interactions.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/e2e-form-interactions.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_e2e_form_interactions_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/e2e-form-interactions.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_api_integration_spec_ts", "label": "api-integration.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/api-integration.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_api_integration_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/api-integration.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_bug_fix_verification_spec_ts", "label": "bug-fix-verification.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/bug-fix-verification.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_bug_fix_verification_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/bug-fix-verification.spec.ts", "source_location": "L13", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_xml_ts", "label": "sitemap.xml.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L1"}, {"id": "pages_sitemap_xml_get", "label": "GET()", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L13"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_xml_ts", "target": "astro", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L1", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_xml_ts", "target": "astro_content", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L2", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_xml_ts", "target": "pages_sitemap_xml_get", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L13", "weight": 1.0}], "raw_calls": [{"caller_nid": "pages_sitemap_xml_get", "callee": "getCollection", "is_member_call": false, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L15"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "getCollection", "is_member_call": false, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L20"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "map", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L37"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "map", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L46"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "join", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L63"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "map", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L63"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_static_assets_spec_ts", "label": "static-assets.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/static-assets.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_static_assets_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/static-assets.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_e2e_critical_paths_spec_ts", "label": "e2e-critical-paths.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/e2e-critical-paths.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_e2e_critical_paths_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/e2e-critical-paths.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_playwright_config_ts", "label": "playwright.config.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/playwright.config.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_playwright_config_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/playwright.config.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_cross_browser_spec_ts", "label": "cross-browser.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/cross-browser.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_cross_browser_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/cross-browser.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tailwind_config_mjs", "label": "tailwind.config.mjs", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tailwind.config.mjs", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tailwind_config_mjs", "target": "typography", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tailwind.config.mjs", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_pages_spec_ts", "label": "pages.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/pages.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_pages_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/pages.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_portfolio_spec_ts", "label": "portfolio.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/portfolio.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_portfolio_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/portfolio.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_api_health_json_ts", "label": "health.json.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L1"}, {"id": "api_health_json_get", "label": "GET()", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L5"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_api_health_json_ts", "target": "astro", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L1", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_api_health_json_ts", "target": "api_health_json_get", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L5", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_health_json_get", "callee": "floor", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L6"}, {"caller_nid": "api_health_json_get", "callee": "now", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L6"}, {"caller_nid": "api_health_json_get", "callee": "memoryUsage", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L7"}, {"caller_nid": "api_health_json_get", "callee": "toISOString", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L11"}, {"caller_nid": "api_health_json_get", "callee": "round", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L18"}, {"caller_nid": "api_health_json_get", "callee": "round", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L19"}, {"caller_nid": "api_health_json_get", "callee": "round", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L20"}, {"caller_nid": "api_health_json_get", "callee": "stringify", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/api/health.json.ts", "source_location": "L27"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_blog_spec_ts", "label": "blog.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/blog.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_blog_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/blog.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "label": "middleware.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L1"}, {"id": "src_middleware_validatecsrforigin", "label": "validateCsrfOrigin()", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L19"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "astro_middleware", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L1", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_utils_logger_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L2", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "utils_logger_logger", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L2", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_utils_sentry_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L3", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "utils_sentry_captureexception", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L3", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "src_middleware_validatecsrforigin", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L19", "weight": 1.0}], "raw_calls": [{"caller_nid": "src_middleware_validatecsrforigin", "callee": "includes", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L24"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "startsWith", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L25"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "get", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L36"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "get", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L37"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "startsWith", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L52"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "startsWith", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L52"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_destructive_chaos_spec_ts", "label": "destructive-chaos.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/destructive-chaos.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_destructive_chaos_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/destructive-chaos.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_e2e_blog_navigation_spec_ts", "label": "e2e-blog-navigation.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/e2e-blog-navigation.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_e2e_blog_navigation_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/e2e-blog-navigation.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_contact_form_spec_ts", "label": "contact-form.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/contact-form.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_contact_form_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/contact-form.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_astro_config_mjs", "label": "astro.config.mjs", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/astro.config.mjs", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_astro_config_mjs", "target": "config", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/astro.config.mjs", "source_location": "L1", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_astro_config_mjs", "target": "tailwind", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/astro.config.mjs", "source_location": "L2", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_astro_config_mjs", "target": "node", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/astro.config.mjs", "source_location": "L3", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_content_config_ts", "label": "config.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/content/config.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_content_config_ts", "target": "astro_content", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/content/config.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_index_xml_ts", "label": "sitemap-index.xml.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap-index.xml.ts", "source_location": "L1"}, {"id": "pages_sitemap_index_xml_get", "label": "GET()", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap-index.xml.ts", "source_location": "L10"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_index_xml_ts", "target": "astro", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap-index.xml.ts", "source_location": "L1", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_index_xml_ts", "target": "pages_sitemap_index_xml_get", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap-index.xml.ts", "source_location": "L10", "weight": 1.0}], "raw_calls": [{"caller_nid": "pages_sitemap_index_xml_get", "callee": "toISOString", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap-index.xml.ts", "source_location": "L11"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_e2e_smoke_suite_spec_ts", "label": "e2e-smoke-suite.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/e2e-smoke-suite.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_e2e_smoke_suite_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/e2e-smoke-suite.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_scripts_backup_windows_tasks_ps1", "label": "windows-tasks.ps1", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/scripts/backup/windows-tasks.ps1", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_env_d_ts", "label": "env.d.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/env.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_security_headers_test_ts", "label": "security-headers.test.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/security-headers.test.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_security_headers_test_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/security-headers.test.ts", "source_location": "L6", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_newsletter_subscription_spec_ts", "label": "newsletter-subscription.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/newsletter-subscription.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_newsletter_subscription_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/newsletter-subscription.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_accessibility_spec_ts", "label": "accessibility.spec.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/accessibility.spec.ts", "source_location": "L1"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_tests_accessibility_spec_ts", "target": "test", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/tests/accessibility.spec.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7734
-912
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,187 @@
|
||||
---
|
||||
// MobileStickyCtaBar — Sticky bottom action bar for mobile/tablet (<768px)
|
||||
// 3 equal columns: Call, WhatsApp, Quote
|
||||
// - Fixed to bottom of viewport
|
||||
// - Respects safe-area-inset-bottom on iOS
|
||||
// - Hidden at md+ breakpoints (>=768px)
|
||||
// - Hidden on print
|
||||
// - Light + dark theme aware
|
||||
---
|
||||
|
||||
<nav
|
||||
aria-label="Quick contact actions"
|
||||
data-testid="mobile-sticky-cta-bar"
|
||||
class="mobile-sticky-cta md:hidden"
|
||||
>
|
||||
<a
|
||||
href="tel:+919561417403"
|
||||
aria-label="Call WorkRoot"
|
||||
data-testid="mobile-cta-call"
|
||||
class="mobile-sticky-cta__btn"
|
||||
>
|
||||
<svg
|
||||
class="mobile-sticky-cta__icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.86 19.86 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.86 19.86 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.37 1.9.72 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.35 1.85.59 2.81.72A2 2 0 0 1 22 16.92z"/>
|
||||
</svg>
|
||||
<span class="mobile-sticky-cta__label">Call</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://wa.me/919561417403?text=Hi%20WorkRoot%2C%20I%27d%20like%20to%20discuss%20a%20project."
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
aria-label="Chat on WhatsApp"
|
||||
data-testid="mobile-cta-whatsapp"
|
||||
class="mobile-sticky-cta__btn mobile-sticky-cta__btn--whatsapp"
|
||||
>
|
||||
<svg
|
||||
class="mobile-sticky-cta__icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.464 3.488"/>
|
||||
</svg>
|
||||
<span class="mobile-sticky-cta__label">WhatsApp</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/contact"
|
||||
aria-label="Request a quote"
|
||||
data-testid="mobile-cta-quote"
|
||||
class="mobile-sticky-cta__btn mobile-sticky-cta__btn--accent"
|
||||
>
|
||||
<svg
|
||||
class="mobile-sticky-cta__icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
|
||||
<polyline points="22,6 12,13 2,6"/>
|
||||
</svg>
|
||||
<span class="mobile-sticky-cta__label">Quote</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.mobile-sticky-cta {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 50;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0;
|
||||
background-color: #ffffff;
|
||||
border-top: 1px solid rgba(15, 23, 42, 0.08);
|
||||
box-shadow: 0 -8px 20px -8px rgba(15, 23, 42, 0.18);
|
||||
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||
/* Ensure no horizontal overflow on very narrow viewports */
|
||||
max-width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(html.dark) .mobile-sticky-cta {
|
||||
background-color: #0f172a;
|
||||
border-top-color: rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 -8px 20px -8px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
min-height: 56px;
|
||||
padding: 8px 6px;
|
||||
text-decoration: none;
|
||||
color: #0f172a;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.1;
|
||||
background-color: transparent;
|
||||
transition: background-color 150ms ease, color 150ms ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
:global(html.dark) .mobile-sticky-cta__btn {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn:active {
|
||||
background-color: rgba(8, 145, 178, 0.08);
|
||||
}
|
||||
|
||||
:global(html.dark) .mobile-sticky-cta__btn:active {
|
||||
background-color: rgba(34, 211, 238, 0.12);
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn:focus-visible {
|
||||
outline: 2px solid #0891b2;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn--whatsapp {
|
||||
color: #128c4f;
|
||||
}
|
||||
:global(html.dark) .mobile-sticky-cta__btn--whatsapp {
|
||||
color: #25D366;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__btn--accent {
|
||||
color: #b45309;
|
||||
}
|
||||
:global(html.dark) .mobile-sticky-cta__btn--accent {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mobile-sticky-cta__label {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
/* Hide on tablets/desktops (>=768px = Tailwind md breakpoint).
|
||||
Note: the `md:hidden` utility on the element loses the cascade against
|
||||
this Astro-scoped class selector (`[data-astro-cid-*]` raises specificity),
|
||||
so we enforce the breakpoint here too. */
|
||||
@media (min-width: 768px) {
|
||||
.mobile-sticky-cta {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide on print */
|
||||
@media print {
|
||||
.mobile-sticky-cta {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
// WhatsAppFab — Global floating WhatsApp contact button
|
||||
// - Fixed bottom-right, 56x56, brand green (#25D366)
|
||||
// - Visible on every page across desktop and mobile
|
||||
// - Mobile: lifted above the MobileStickyCtaBar via safe CSS variable offset
|
||||
// - Respects iOS safe-area-inset-bottom
|
||||
// - Hidden when printing
|
||||
---
|
||||
|
||||
<a
|
||||
href="https://wa.me/919561417403?text=Hi%20WorkRoot%2C%20I%27d%20like%20to%20discuss%20a%20project."
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
aria-label="Chat on WhatsApp"
|
||||
data-testid="whatsapp-fab"
|
||||
class="whatsapp-fab"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="whatsapp-fab__icon"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.464 3.488"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.whatsapp-fab {
|
||||
/* Mobile: lifted above the sticky CTA bar so they don't overlap. */
|
||||
--wa-fab-bottom: calc(72px + env(safe-area-inset-bottom, 0px) + 16px);
|
||||
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
right: max(16px, env(safe-area-inset-right, 16px));
|
||||
bottom: var(--wa-fab-bottom);
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 9999px;
|
||||
background-color: #25D366;
|
||||
color: #ffffff;
|
||||
box-shadow:
|
||||
0 6px 16px rgba(37, 211, 102, 0.35),
|
||||
0 2px 6px rgba(0, 0, 0, 0.18);
|
||||
z-index: 60;
|
||||
text-decoration: none;
|
||||
transition: transform 200ms ease, box-shadow 200ms ease, background-color 200ms ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.whatsapp-fab:hover {
|
||||
background-color: #1ebe5c;
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 10px 20px rgba(37, 211, 102, 0.45),
|
||||
0 4px 8px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
.whatsapp-fab:focus-visible {
|
||||
outline: 2px solid #ffffff;
|
||||
outline-offset: 3px;
|
||||
box-shadow:
|
||||
0 0 0 4px rgba(37, 211, 102, 0.55),
|
||||
0 6px 16px rgba(37, 211, 102, 0.4);
|
||||
}
|
||||
|
||||
.whatsapp-fab:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.whatsapp-fab__icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
/* Desktop / tablet: no sticky CTA bar present, sit at standard offset. */
|
||||
@media (min-width: 768px) {
|
||||
.whatsapp-fab {
|
||||
--wa-fab-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduce motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.whatsapp-fab {
|
||||
transition: none;
|
||||
}
|
||||
.whatsapp-fab:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide on print */
|
||||
@media print {
|
||||
.whatsapp-fab {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -6,6 +6,8 @@ import Header from '../components/Header.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Analytics from '../components/Analytics.astro';
|
||||
import PWAInstallPrompt from '../components/PWAInstallPrompt.astro';
|
||||
import WhatsAppFab from '../components/WhatsAppFab.astro';
|
||||
import MobileStickyCtaBar from '../components/MobileStickyCtaBar.astro';
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props {
|
||||
@@ -290,6 +292,15 @@ const fullTitle = title === 'Home' ? siteName : `${title} | ${siteName}`;
|
||||
<!-- Footer -->
|
||||
<Footer />
|
||||
|
||||
<!-- Spacer to ensure footer/content clears the mobile sticky CTA bar (<md only) -->
|
||||
<div aria-hidden="true" class="md:hidden h-[calc(64px+env(safe-area-inset-bottom,0px))] w-full"></div>
|
||||
|
||||
<!-- Mobile sticky bottom CTA bar (visible <md) -->
|
||||
<MobileStickyCtaBar />
|
||||
|
||||
<!-- Floating WhatsApp action (global) -->
|
||||
<WhatsAppFab />
|
||||
|
||||
<!-- Scripts slot for page-specific scripts -->
|
||||
<slot name="scripts" />
|
||||
|
||||
|
||||
+88
-15
@@ -122,6 +122,43 @@ const stats = [
|
||||
{ value: 3, suffix: '', label: 'Team Members', icon: 'clock' },
|
||||
];
|
||||
|
||||
// Technology stack data — relocated below the fold so social proof leads the
|
||||
// hero. Each entry pairs the framework name with a short context line and a
|
||||
// neutral monochrome SVG icon (uses currentColor so the grayscale →
|
||||
// brand-color hover treatment is driven entirely by Tailwind utility classes).
|
||||
const techStack = [
|
||||
{
|
||||
name: 'Angular',
|
||||
context: 'Enterprise front-ends',
|
||||
icon: '<svg viewBox="0 0 24 24" class="w-8 h-8" fill="currentColor" aria-hidden="true"><path d="M12 2.5 2.5 5.7l1.6 12 7.9 3.8 7.9-3.8 1.6-12L12 2.5Zm0 2.3 6.3 11.3h-1.9l-1.3-3.1H9l-1.3 3.1H5.7L12 4.8Zm-2.3 6.5h4.6L12 7.3l-2.3 4Z"/></svg>',
|
||||
},
|
||||
{
|
||||
name: 'Spring Boot',
|
||||
context: 'Backend APIs',
|
||||
icon: '<svg viewBox="0 0 24 24" class="w-8 h-8" fill="currentColor" aria-hidden="true"><path d="M21.6 4.2c-.4 1-1 1.9-1.5 2.7C18.4 4.5 15.4 3 12 3 6.5 3 2 7.5 2 13s4.5 10 10 10c5 0 9.1-3.7 9.9-8.5.5-2.4.1-5-1.3-7.4l1-2.9Zm-9.6 17c-4.6 0-8.4-3.8-8.4-8.4 0-3.4 2.1-6.4 5.1-7.7-1 1.6-1.7 3.4-2.1 5.3l-.1.6 1.1.4c2.4.8 4.8 1.8 7 3.6 1.4 1.1 2.4 2.6 2.8 4.3-1.4 1.2-3.3 1.9-5.4 1.9Zm6.3-2.5c-.8-2.5-3-4.8-6.5-6.5-1.2-.6-2.5-1.1-3.7-1.4.5-2.1 1.5-4 2.9-5.5 1.1-.1 2.2 0 3.3.4 1.8.6 3.4 1.8 4.6 3.4l-.2.3-.1.1c-.8 1.4-1.4 3-1.7 4.6-.5 1.7-.7 3.4-.5 4.6h1.9Z"/></svg>',
|
||||
},
|
||||
{
|
||||
name: 'Next.js',
|
||||
context: 'React SSR & ISR',
|
||||
icon: '<svg viewBox="0 0 24 24" class="w-8 h-8" fill="currentColor" aria-hidden="true"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2Zm-.8 4.5h1.6v9.2H11.3l-3.5-5v5H6.2V6.5h1.5l3.5 5v-5Zm5.9 0h1.5v11h-1.5v-11Z"/></svg>',
|
||||
},
|
||||
{
|
||||
name: 'Astro',
|
||||
context: 'Marketing & docs sites',
|
||||
icon: '<svg viewBox="0 0 24 24" class="w-8 h-8" fill="currentColor" aria-hidden="true"><path d="M8.4 17.8c-.4-1.4-.5-2.7 0-3.4l.1-.1c.4 1.1 1.4 1.6 2.7 2 2 .6 2.6 1.4 1.8 3.1l-.1.2c-.6 1.1-1.8 2-2.6 2-.7 0-1.3-.5-1.6-1.4 0-.3-.1-.6-.1-1l-.2-1.4ZM14.8 2 18 13H6L9.2 2h5.6Zm-2.8 2.6L9.7 12h4.6L12 4.6Z"/></svg>',
|
||||
},
|
||||
{
|
||||
name: 'Golang',
|
||||
context: 'High-throughput services',
|
||||
icon: '<svg viewBox="0 0 24 24" class="w-8 h-8" fill="currentColor" aria-hidden="true"><path d="M2.4 11.6c-.1 0-.1 0 0 0-.1-.1-.1-.1 0-.2l.5-.6c.1-.1.2-.1.3-.1H7c.1 0 .1.1.1.2l-.4.5c-.1.1-.2.2-.3.2l-4 .1v-.1Zm-1.9 1.2c-.1 0-.1-.1 0-.2l.5-.6c.1-.1.2-.1.3-.1h5.4c.1 0 .2.1.1.2l-.2.5c-.1.1-.2.2-.3.2l-5.8 0Zm3.1 1.1c-.1 0-.1-.1 0-.2l.4-.5c.1-.1.2-.1.3-.1h2.4c.1 0 .2.1.2.2l-.1.5c0 .1-.1.2-.2.2l-3-.1Zm12.6-2.6c-.8.2-1.4.4-2.2.6-.2 0-.2 0-.4-.1-.2-.2-.3-.4-.6-.5-.8-.4-1.6-.3-2.4.2-.9.6-1.4 1.4-1.4 2.5 0 1 .7 1.9 1.7 2 .9.1 1.6-.2 2.2-.9.1-.2.2-.3.4-.5h-3.1c-.3 0-.4-.2-.3-.5l.4-.7c.1-.2.3-.3.5-.3h5.8c0 .1 0 .1 0 .2-.2 1.2-.7 2.2-1.5 3.1-1 1-2.3 1.7-3.7 1.8-2.5.3-4.7-.7-5.7-3-.8-1.9-.5-3.7.6-5.4 1.2-1.8 3-2.8 5.2-3.2 1.8-.3 3.5-.1 5 1 1.1.8 1.7 1.9 2 3.2 0 .2 0 .3-.3.3Zm3.5 5.4c-1.6 0-3-.5-4.2-1.7-1-1-1.6-2.2-1.7-3.6-.2-2.1.4-3.9 1.8-5.4 1.5-1.6 3.3-2.5 5.4-2.5 2 0 3.5.7 4.6 2.2.9 1.4 1.2 2.9 1 4.5-.4 3.7-3.2 6.2-6.9 6.5Zm3.9-6.6c0-.3-.1-.5-.1-.8-.2-1.4-1.4-2-2.7-1.7-1.3.3-2.1 1.1-2.4 2.4-.2 1.1.3 2.2 1.3 2.6.8.4 1.6.3 2.3-.2 1.1-.6 1.6-1.5 1.6-2.3Z"/></svg>',
|
||||
},
|
||||
{
|
||||
name: 'PostgreSQL',
|
||||
context: 'Production data layer',
|
||||
icon: '<svg viewBox="0 0 24 24" class="w-8 h-8" fill="currentColor" aria-hidden="true"><path d="M12 3c-3.3 0-6 1-6 2.3v13.5c0 1.3 2.7 2.3 6 2.3s6-1 6-2.3V5.3C18 4 15.3 3 12 3Zm0 1.5c2.9 0 5 .8 5 1.5s-2.1 1.5-5 1.5-5-.8-5-1.5 2.1-1.5 5-1.5Zm5 14.3c0 .7-2.1 1.5-5 1.5s-5-.8-5-1.5v-2.4c1.2.7 3 1.1 5 1.1s3.8-.4 5-1.1v2.4Zm0-4.6c0 .7-2.1 1.5-5 1.5s-5-.8-5-1.5v-2.4c1.2.7 3 1.1 5 1.1s3.8-.4 5-1.1v2.4Zm0-4.6c0 .7-2.1 1.5-5 1.5s-5-.8-5-1.5V7.7c1.2.7 3 1.1 5 1.1s3.8-.4 5-1.1v2.4Z"/></svg>',
|
||||
},
|
||||
];
|
||||
|
||||
// FAQ data
|
||||
const faqs = [
|
||||
{
|
||||
@@ -233,24 +270,14 @@ const faqs = [
|
||||
<span>No commitment</span>
|
||||
</p>
|
||||
|
||||
<!-- Tech stack proof strip -->
|
||||
<div class="mt-16 opacity-0 animate-slide-up" style="animation-delay: 1s;">
|
||||
<p class="text-sm text-secondary-400 uppercase tracking-wider mb-8">Powered By Industry Leaders</p>
|
||||
<div class="flex flex-wrap justify-center items-center gap-12 opacity-60" role="list" aria-label="Technologies we use">
|
||||
<div class="text-2xl font-bold text-white" role="listitem">Angular</div>
|
||||
<div class="text-2xl font-bold text-white" role="listitem">Spring Boot</div>
|
||||
<div class="text-2xl font-bold text-white" role="listitem">Next.js</div>
|
||||
<div class="text-2xl font-bold text-white" role="listitem">Astro</div>
|
||||
<div class="text-2xl font-bold text-white" role="listitem">Golang</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scroll indicator -->
|
||||
<div class="absolute bottom-8 left-1/2 -translate-x-1/2 opacity-0 animate-fade-in" style="animation-delay: 1.2s;">
|
||||
<a href="#client-logos" class="flex flex-col items-center text-white/60 hover:text-white transition-colors group" aria-label="Scroll to clients">
|
||||
<span class="text-sm mb-2">Discover More</span>
|
||||
<!-- Scroll indicator — points at the client-logo social-proof strip just
|
||||
below the hero so the first interaction surface is trust, not stack. -->
|
||||
<div class="absolute bottom-8 left-1/2 -translate-x-1/2 opacity-0 animate-fade-in" style="animation-delay: 1s;">
|
||||
<a href="#client-logos" class="flex flex-col items-center text-white/60 hover:text-white transition-colors group" aria-label="Scroll to client logos">
|
||||
<span class="text-sm mb-2">See who we work with</span>
|
||||
<svg class="w-6 h-6 animate-bounce" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
|
||||
</svg>
|
||||
@@ -635,6 +662,52 @@ const faqs = [
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Technologies We Work With — relocated from the hero so social proof
|
||||
leads above the fold. Kept intentionally subdued (border-only cards,
|
||||
neutral grayscale icons) so it reads as supporting context rather than
|
||||
competing with the client-logo trust strip up top. -->
|
||||
<section id="tech-stack" class="py-20 sm:py-24 bg-secondary-50 dark:bg-secondary-900/60 border-y border-secondary-100 dark:border-secondary-800">
|
||||
<div class="container-wrapper">
|
||||
<div class="text-center max-w-3xl mx-auto mb-12 sm:mb-16" data-animate="fade-up">
|
||||
<span class="inline-block px-4 py-2 rounded-full bg-secondary-100 dark:bg-secondary-800 text-secondary-700 dark:text-secondary-300 text-xs sm:text-sm font-bold uppercase tracking-wider mb-4">Tech Stack</span>
|
||||
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold text-secondary-900 dark:text-secondary-100 mb-4 sm:mb-6">Technologies we work with</h2>
|
||||
<p class="text-base sm:text-xl text-secondary-600 dark:text-secondary-400">
|
||||
Battle-tested frameworks and runtimes we ship to production across enterprise, university, and government deployments.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul
|
||||
class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3 sm:gap-4 max-w-6xl mx-auto"
|
||||
role="list"
|
||||
aria-label="Technologies we work with"
|
||||
>
|
||||
{techStack.map((tech, index) => (
|
||||
<li
|
||||
class="group flex flex-col items-center justify-center text-center gap-2 sm:gap-3 rounded-2xl border border-secondary-200 dark:border-secondary-700 bg-white dark:bg-secondary-800/70 px-3 py-5 sm:px-4 sm:py-6 min-w-0 transition-all duration-300 hover:-translate-y-1 hover:border-primary-300 hover:shadow-lg dark:hover:border-primary-500/60 focus-within:ring-2 focus-within:ring-primary-400"
|
||||
data-animate="fade-up"
|
||||
data-delay={String(index * 60)}
|
||||
>
|
||||
<span
|
||||
class="text-secondary-400 dark:text-secondary-500 group-hover:text-primary-500 dark:group-hover:text-primary-400 transition-colors"
|
||||
aria-hidden="true"
|
||||
set:html={tech.icon}
|
||||
/>
|
||||
<span class="text-sm sm:text-base font-semibold text-secondary-800 dark:text-secondary-100 leading-tight break-words">
|
||||
{tech.name}
|
||||
</span>
|
||||
<span class="text-[11px] sm:text-xs uppercase tracking-wider text-secondary-500 dark:text-secondary-400 leading-tight">
|
||||
{tech.context}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<p class="text-center text-sm text-secondary-500 dark:text-secondary-400 mt-10 sm:mt-12">
|
||||
Also working with <span class="font-semibold text-secondary-700 dark:text-secondary-200">React Native</span>, <span class="font-semibold text-secondary-700 dark:text-secondary-200">MySQL</span>, <span class="font-semibold text-secondary-700 dark:text-secondary-200">Docker</span>, and <span class="font-semibold text-secondary-700 dark:text-secondary-200">GitHub Actions</span> — <a href="/services" class="text-primary-600 dark:text-primary-400 font-semibold hover:underline">see the full stack →</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Final CTA Section - Enhanced -->
|
||||
<section class="py-24 bg-gradient-to-br from-secondary-900 via-primary-900 to-primary-800 relative overflow-hidden">
|
||||
<!-- Background decorations -->
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
const WA_HREF =
|
||||
'https://wa.me/919561417403?text=Hi%20WorkRoot%2C%20I%27d%20like%20to%20discuss%20a%20project.';
|
||||
|
||||
/**
|
||||
* Verifies the global WhatsApp FAB and the Mobile Sticky CTA bar render
|
||||
* correctly across viewports, expose working contact links, and never
|
||||
* cause horizontal overflow on a 360px viewport.
|
||||
*/
|
||||
|
||||
test.describe('WhatsApp FAB (global)', () => {
|
||||
test('renders on every page at desktop width', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 800 });
|
||||
|
||||
for (const path of ['/', '/about', '/services', '/contact']) {
|
||||
await page.goto(path);
|
||||
const fab = page.getByTestId('whatsapp-fab');
|
||||
await expect(fab, `FAB missing on ${path}`).toBeVisible();
|
||||
await expect(fab).toHaveAttribute('href', WA_HREF);
|
||||
await expect(fab).toHaveAttribute('target', '_blank');
|
||||
await expect(fab).toHaveAttribute('rel', /noopener/);
|
||||
await expect(fab).toHaveAttribute('aria-label', 'Chat on WhatsApp');
|
||||
}
|
||||
});
|
||||
|
||||
test('renders on home at 360px mobile width', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 360, height: 800 });
|
||||
await page.goto('/');
|
||||
const fab = page.getByTestId('whatsapp-fab');
|
||||
await expect(fab).toBeVisible();
|
||||
|
||||
// FAB is 56x56
|
||||
const box = await fab.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
expect(box!.width).toBeGreaterThanOrEqual(48);
|
||||
expect(box!.height).toBeGreaterThanOrEqual(48);
|
||||
|
||||
// FAB does not horizontally overflow the viewport
|
||||
expect(box!.x + box!.width).toBeLessThanOrEqual(360);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Mobile Sticky CTA Bar', () => {
|
||||
test('visible at 360px and exposes 3 working actions', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 360, height: 800 });
|
||||
await page.goto('/');
|
||||
|
||||
const bar = page.getByTestId('mobile-sticky-cta-bar');
|
||||
await expect(bar).toBeVisible();
|
||||
|
||||
const callBtn = page.getByTestId('mobile-cta-call');
|
||||
const waBtn = page.getByTestId('mobile-cta-whatsapp');
|
||||
const quoteBtn = page.getByTestId('mobile-cta-quote');
|
||||
|
||||
await expect(callBtn).toBeVisible();
|
||||
await expect(waBtn).toBeVisible();
|
||||
await expect(quoteBtn).toBeVisible();
|
||||
|
||||
await expect(callBtn).toHaveAttribute('href', 'tel:+919561417403');
|
||||
await expect(waBtn).toHaveAttribute('href', WA_HREF);
|
||||
await expect(quoteBtn).toHaveAttribute('href', '/contact');
|
||||
|
||||
// Each action is at least a 48px tall touch target
|
||||
for (const btn of [callBtn, waBtn, quoteBtn]) {
|
||||
const box = await btn.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
expect(box!.height).toBeGreaterThanOrEqual(48);
|
||||
}
|
||||
|
||||
// The bar spans the full viewport width and is pinned to the bottom
|
||||
const barBox = await bar.boundingBox();
|
||||
expect(barBox).not.toBeNull();
|
||||
expect(barBox!.x).toBeLessThanOrEqual(0.5);
|
||||
expect(barBox!.width).toBeGreaterThanOrEqual(355);
|
||||
expect(barBox!.width).toBeLessThanOrEqual(360);
|
||||
|
||||
// No horizontal overflow at 360px (match existing site tolerance: viewport + 20px)
|
||||
const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
|
||||
expect(bodyWidth).toBeLessThanOrEqual(360 + 20);
|
||||
|
||||
// The page must not be horizontally scrolled — confirms fixed elements fit.
|
||||
const scrollX = await page.evaluate(() => window.scrollX);
|
||||
expect(scrollX).toBe(0);
|
||||
});
|
||||
|
||||
test('hidden at >=768px (desktop / tablet landscape)', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 800 });
|
||||
await page.goto('/');
|
||||
|
||||
const bar = page.getByTestId('mobile-sticky-cta-bar');
|
||||
// Either not visible, or has md:hidden applied (display:none via media query)
|
||||
await expect(bar).toBeHidden();
|
||||
});
|
||||
|
||||
test('renders on every key page on mobile', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 360, height: 800 });
|
||||
for (const path of ['/', '/about', '/services', '/portfolio', '/blog', '/contact']) {
|
||||
await page.goto(path);
|
||||
await expect(
|
||||
page.getByTestId('mobile-sticky-cta-bar'),
|
||||
`Sticky CTA bar missing on ${path}`,
|
||||
).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('FAB sits above the sticky bar on mobile (no overlap)', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 360, height: 800 });
|
||||
await page.goto('/');
|
||||
|
||||
const fab = page.getByTestId('whatsapp-fab');
|
||||
const bar = page.getByTestId('mobile-sticky-cta-bar');
|
||||
|
||||
const fabBox = await fab.boundingBox();
|
||||
const barBox = await bar.boundingBox();
|
||||
expect(fabBox).not.toBeNull();
|
||||
expect(barBox).not.toBeNull();
|
||||
|
||||
// The bottom of the FAB sits above the top of the sticky bar.
|
||||
expect(fabBox!.y + fabBox!.height).toBeLessThanOrEqual(barBox!.y);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user