userflow-mcp 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/analysis/clarity.d.ts +7 -0
- package/dist/analysis/clarity.d.ts.map +1 -0
- package/dist/analysis/clarity.js +114 -0
- package/dist/analysis/clarity.js.map +1 -0
- package/dist/analysis/cognitive-load.d.ts +7 -0
- package/dist/analysis/cognitive-load.d.ts.map +1 -0
- package/dist/analysis/cognitive-load.js +45 -0
- package/dist/analysis/cognitive-load.js.map +1 -0
- package/dist/analysis/emotional-arc.d.ts +10 -0
- package/dist/analysis/emotional-arc.d.ts.map +1 -0
- package/dist/analysis/emotional-arc.js +95 -0
- package/dist/analysis/emotional-arc.js.map +1 -0
- package/dist/analysis/friction.d.ts +23 -0
- package/dist/analysis/friction.d.ts.map +1 -0
- package/dist/analysis/friction.js +61 -0
- package/dist/analysis/friction.js.map +1 -0
- package/dist/feedback/comparison.d.ts +11 -0
- package/dist/feedback/comparison.d.ts.map +1 -0
- package/dist/feedback/comparison.js +156 -0
- package/dist/feedback/comparison.js.map +1 -0
- package/dist/feedback/generator.d.ts +19 -0
- package/dist/feedback/generator.d.ts.map +1 -0
- package/dist/feedback/generator.js +139 -0
- package/dist/feedback/generator.js.map +1 -0
- package/dist/feedback/report.d.ts +10 -0
- package/dist/feedback/report.d.ts.map +1 -0
- package/dist/feedback/report.js +123 -0
- package/dist/feedback/report.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/personas/engine.d.ts +39 -0
- package/dist/personas/engine.d.ts.map +1 -0
- package/dist/personas/engine.js +58 -0
- package/dist/personas/engine.js.map +1 -0
- package/dist/personas/presets.d.ts +11 -0
- package/dist/personas/presets.d.ts.map +1 -0
- package/dist/personas/presets.js +251 -0
- package/dist/personas/presets.js.map +1 -0
- package/dist/personas/types.d.ts +11 -0
- package/dist/personas/types.d.ts.map +1 -0
- package/dist/personas/types.js +23 -0
- package/dist/personas/types.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +288 -0
- package/dist/server.js.map +1 -0
- package/dist/session/session-manager.d.ts +54 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +228 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +35 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/types.d.ts +284 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/actions.d.ts +17 -0
- package/dist/utils/actions.d.ts.map +1 -0
- package/dist/utils/actions.js +89 -0
- package/dist/utils/actions.js.map +1 -0
- package/dist/utils/browser.d.ts +22 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +122 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/page-snapshot.d.ts +11 -0
- package/dist/utils/page-snapshot.d.ts.map +1 -0
- package/dist/utils/page-snapshot.js +102 -0
- package/dist/utils/page-snapshot.js.map +1 -0
- package/dist/walker/action-planner.d.ts +15 -0
- package/dist/walker/action-planner.d.ts.map +1 -0
- package/dist/walker/action-planner.js +236 -0
- package/dist/walker/action-planner.js.map +1 -0
- package/dist/walker/flow-walker.d.ts +10 -0
- package/dist/walker/flow-walker.d.ts.map +1 -0
- package/dist/walker/flow-walker.js +107 -0
- package/dist/walker/flow-walker.js.map +1 -0
- package/dist/walker/session-recorder.d.ts +28 -0
- package/dist/walker/session-recorder.d.ts.map +1 -0
- package/dist/walker/session-recorder.js +90 -0
- package/dist/walker/session-recorder.js.map +1 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ARISTONE
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# UserFlow MCP
|
|
2
|
+
|
|
3
|
+
**Simulates real users navigating your app and delivers qualitative UX feedback.** Built as an MCP server for Claude Code.
|
|
4
|
+
|
|
5
|
+
UserFlow doesn't run Lighthouse scores or check WCAG compliance — it puts itself in your user's shoes. It clicks through your app as different personas (a first-time user, a busy executive, a senior citizen, an accessibility-dependent user) and tells you where they'd get confused, frustrated, or give up.
|
|
6
|
+
|
|
7
|
+
> **Free for Claude Pro users.** No API keys, no external services. Just install and go.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install globally
|
|
13
|
+
npm install -g userflow-mcp
|
|
14
|
+
|
|
15
|
+
# Or use directly with npx
|
|
16
|
+
npx -y userflow-mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Add to Claude Code
|
|
20
|
+
|
|
21
|
+
In your Claude Code MCP settings:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"userflow": {
|
|
27
|
+
"command": "npx",
|
|
28
|
+
"args": ["-y", "userflow-mcp"]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then in Claude Code:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
→ simulate_user url:"https://myapp.com" persona:"Alex"
|
|
38
|
+
→ quick_impression url:"https://myapp.com"
|
|
39
|
+
→ compare_personas url:"https://myapp.com" personas:["Alex", "Morgan", "Patricia"]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## What You Get
|
|
43
|
+
|
|
44
|
+
Instead of scores and violations, you get feedback like this:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
Step 1: Landing Page (3.2s)
|
|
48
|
+
🔍 curious
|
|
49
|
+
> "Hmm, 'Supercharge your workflow' — but what does this product actually do?"
|
|
50
|
+
⚠️ MEDIUM: Value prop unclear — heading doesn't explain the product
|
|
51
|
+
→ Rewrite heading to describe what the product does, not how it makes you feel
|
|
52
|
+
|
|
53
|
+
Step 2: Signup (12.1s)
|
|
54
|
+
😐 neutral
|
|
55
|
+
> "Alright, let me sign up and see..."
|
|
56
|
+
🛑 HIGH: Form asks for company size during signup — feels invasive
|
|
57
|
+
→ Remove non-essential fields from signup, ask later during onboarding
|
|
58
|
+
|
|
59
|
+
Step 3: Dashboard (8.4s)
|
|
60
|
+
😤 frustrated
|
|
61
|
+
> "Okay I'm in... now what? There's nothing here."
|
|
62
|
+
🚨 CRITICAL: Empty state with no guidance
|
|
63
|
+
→ Add a getting-started checklist or demo project
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 11 Tools
|
|
67
|
+
|
|
68
|
+
| Tool | Description |
|
|
69
|
+
|------|-------------|
|
|
70
|
+
| **simulate_user** | Full persona simulation — walks the entire flow, records every step |
|
|
71
|
+
| **quick_impression** | 30-second first impression — would this user stay or bounce? |
|
|
72
|
+
| **test_onboarding** | Test signup/onboarding as a first-time user |
|
|
73
|
+
| **test_checkout** | Test purchase/conversion flow |
|
|
74
|
+
| **compare_personas** | Run 2-5 personas on the same flow, compare experiences |
|
|
75
|
+
| **find_dead_ends** | Discover pages with no exit, empty states, broken flows |
|
|
76
|
+
| **rate_clarity** | Evaluate value prop, CTAs, cognitive load — with scores |
|
|
77
|
+
| **session_transcript** | Detailed step-by-step journey log with thoughts |
|
|
78
|
+
| **custom_persona** | Define your own persona with specific traits and goals |
|
|
79
|
+
| **export_report** | Generate standalone HTML report |
|
|
80
|
+
| **list_personas** | See all built-in personas with descriptions |
|
|
81
|
+
|
|
82
|
+
## 8 Built-in Personas
|
|
83
|
+
|
|
84
|
+
| Name | Description | Tech | Patience | Device |
|
|
85
|
+
|------|-------------|------|----------|--------|
|
|
86
|
+
| **Alex** | The First-Timer — never used SaaS before | Novice | Moderate | Mobile |
|
|
87
|
+
| **Morgan** | The Power User — developer, expects excellence | Expert | Low | Desktop |
|
|
88
|
+
| **Patricia** | The Senior Explorer — 68, low vision | Basic | High | Desktop |
|
|
89
|
+
| **Jordan** | The Busy Executive — 10 seconds to impress | Intermediate | Very Low | Mobile |
|
|
90
|
+
| **Sam** | The Accessibility Tester — screen reader user | Advanced | Moderate | Desktop |
|
|
91
|
+
| **Riley** | The Skeptical Evaluator — looking for red flags | Intermediate | Moderate | Desktop |
|
|
92
|
+
| **Casey** | The International User — potential language barriers | Basic | High | Mobile |
|
|
93
|
+
| **Taylor** | The Return Visitor — knows the app, wants efficiency | Advanced | Moderate | Desktop |
|
|
94
|
+
|
|
95
|
+
## How It Works
|
|
96
|
+
|
|
97
|
+
1. **Puppeteer** opens your URL in a real browser
|
|
98
|
+
2. The **persona engine** creates a user with specific traits (tech literacy, patience, goals, device)
|
|
99
|
+
3. The **flow walker** autonomously navigates — clicking buttons, filling forms, scrolling — based on what the persona would actually do
|
|
100
|
+
4. At each step, the **analysis engine** checks for:
|
|
101
|
+
- **Friction points** — confusing CTAs, too many form fields, empty states, slow loads
|
|
102
|
+
- **Cognitive load** — how overwhelming the page is
|
|
103
|
+
- **Clarity** — is the value prop clear? Can the user find the CTA?
|
|
104
|
+
- **Emotional state** — curious → confident → confused → frustrated
|
|
105
|
+
5. The **feedback generator** compiles everything into a structured report
|
|
106
|
+
|
|
107
|
+
No LLM calls during simulation — all analysis uses deterministic heuristics. This means it's **fast**, **free**, and **consistent**.
|
|
108
|
+
|
|
109
|
+
## Custom Personas
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
→ custom_persona url:"https://myapp.com"
|
|
113
|
+
name:"Startup Founder"
|
|
114
|
+
description:"Technical founder evaluating tools for their team"
|
|
115
|
+
goals:["understand pricing", "evaluate team features", "check integrations"]
|
|
116
|
+
tech_literacy:"advanced"
|
|
117
|
+
patience:"low"
|
|
118
|
+
device:"desktop"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Multi-Persona Comparison
|
|
122
|
+
|
|
123
|
+
The `compare_personas` tool reveals how different users experience the same flow:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
→ compare_personas url:"https://myapp.com" personas:["Alex", "Jordan", "Patricia"]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Output includes:
|
|
130
|
+
- **Shared friction** — issues ALL users hit (fix these first)
|
|
131
|
+
- **Divergence points** — moments where different users interpret the UI differently
|
|
132
|
+
- **Per-persona summaries** — friction scores, emotional trends, goal achievement
|
|
133
|
+
|
|
134
|
+
## Architecture
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
src/
|
|
138
|
+
├── server.ts # MCP tool registrations (11 tools)
|
|
139
|
+
├── personas/
|
|
140
|
+
│ ├── presets.ts # 8 built-in personas
|
|
141
|
+
│ └── engine.ts # Persona creation + utilities
|
|
142
|
+
├── walker/
|
|
143
|
+
│ ├── flow-walker.ts # Autonomous page traversal
|
|
144
|
+
│ ├─�� action-planner.ts # Decides what a persona would do
|
|
145
|
+
│ └── session-recorder.ts # Journey tracking
|
|
146
|
+
├── analysis/
|
|
147
|
+
│ ├── friction.ts # Friction detection + scoring
|
|
148
|
+
│ ├── cognitive-load.ts # Page complexity assessment
|
|
149
|
+
│ ��── clarity.ts # CTA + value prop evaluation
|
|
150
|
+
│ └── emotional-arc.ts # Sentiment tracking
|
|
151
|
+
├── feedback/
|
|
152
|
+
│ ├── generator.ts # Markdown report generation
|
|
153
|
+
│ ├── comparison.ts # Multi-persona comparison
|
|
154
|
+
│ └── report.ts # HTML report generation
|
|
155
|
+
└── utils/
|
|
156
|
+
└── browser.ts # Puppeteer browser management
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Requirements
|
|
160
|
+
|
|
161
|
+
- **Node.js** >= 18.0.0
|
|
162
|
+
- **Google Chrome** or Chromium installed
|
|
163
|
+
- **Claude Code** with MCP support
|
|
164
|
+
|
|
165
|
+
## Development
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
git clone https://github.com/prembobby39-gif/userflow-mcp.git
|
|
169
|
+
cd userflow-mcp
|
|
170
|
+
npm install
|
|
171
|
+
npm run build
|
|
172
|
+
npm test
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT — ARISTONE
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PageSnapshot, ClarityAssessment } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Assess how clear a page's purpose, CTAs, and navigation are.
|
|
4
|
+
* Returns a score from 0 (completely unclear) to 10 (crystal clear).
|
|
5
|
+
*/
|
|
6
|
+
export declare function assessClarity(page: PageSnapshot): ClarityAssessment;
|
|
7
|
+
//# sourceMappingURL=clarity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clarity.d.ts","sourceRoot":"","sources":["../../src/analysis/clarity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,iBAAiB,CAsGnE"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assess how clear a page's purpose, CTAs, and navigation are.
|
|
3
|
+
* Returns a score from 0 (completely unclear) to 10 (crystal clear).
|
|
4
|
+
*/
|
|
5
|
+
export function assessClarity(page) {
|
|
6
|
+
let score = 10; // Start perfect, deduct for issues
|
|
7
|
+
const issues = [];
|
|
8
|
+
// Value proposition (from headings)
|
|
9
|
+
let valueProposition;
|
|
10
|
+
if (page.headings.length > 0) {
|
|
11
|
+
const h1 = page.headings[0];
|
|
12
|
+
const hasAction = /\b(get|start|build|create|manage|track|discover|learn|grow|save|boost)\b/i.test(h1);
|
|
13
|
+
const isVague = /\b(welcome|hello|home|dashboard)\b/i.test(h1) && h1.split(" ").length < 5;
|
|
14
|
+
if (isVague) {
|
|
15
|
+
valueProposition = `Vague heading: "${h1}" — doesn't explain what the product does`;
|
|
16
|
+
score -= 2;
|
|
17
|
+
}
|
|
18
|
+
else if (hasAction) {
|
|
19
|
+
valueProposition = `Clear action-oriented heading: "${h1}"`;
|
|
20
|
+
}
|
|
21
|
+
else if (h1.length > 10) {
|
|
22
|
+
valueProposition = `Descriptive heading: "${h1}"`;
|
|
23
|
+
score -= 0.5;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
valueProposition = `Short heading: "${h1}" — may not communicate enough`;
|
|
27
|
+
score -= 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
valueProposition = "No heading found — users won't know what this page is about";
|
|
32
|
+
score -= 3;
|
|
33
|
+
}
|
|
34
|
+
// CTA clarity
|
|
35
|
+
let ctaClarity;
|
|
36
|
+
const visibleButtons = page.buttons.filter((b) => b.isVisible);
|
|
37
|
+
if (visibleButtons.length === 0) {
|
|
38
|
+
ctaClarity = "No visible buttons — unclear what action to take";
|
|
39
|
+
score -= 2;
|
|
40
|
+
}
|
|
41
|
+
else if (visibleButtons.length === 1) {
|
|
42
|
+
ctaClarity = `Single clear CTA: "${visibleButtons[0].text}"`;
|
|
43
|
+
score += 0.5;
|
|
44
|
+
}
|
|
45
|
+
else if (visibleButtons.length <= 3) {
|
|
46
|
+
ctaClarity = `${visibleButtons.length} buttons — clear hierarchy if primary CTA is visually distinct`;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
ctaClarity = `${visibleButtons.length} buttons — too many competing CTAs, consider reducing`;
|
|
50
|
+
score -= 1;
|
|
51
|
+
}
|
|
52
|
+
// Navigation logic
|
|
53
|
+
let navigationLogic;
|
|
54
|
+
const navLinks = page.links.filter((l) => l.isVisible);
|
|
55
|
+
if (navLinks.length === 0 && page.buttons.length === 0) {
|
|
56
|
+
navigationLogic = "No navigation options — user is stuck";
|
|
57
|
+
score -= 3;
|
|
58
|
+
}
|
|
59
|
+
else if (navLinks.length > 20) {
|
|
60
|
+
navigationLogic = `${navLinks.length} links — navigation is cluttered and overwhelming`;
|
|
61
|
+
score -= 1.5;
|
|
62
|
+
}
|
|
63
|
+
else if (navLinks.length > 0) {
|
|
64
|
+
navigationLogic = `${navLinks.length} links available — reasonable navigation density`;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
navigationLogic = "Navigation relies only on buttons, no standard links";
|
|
68
|
+
score -= 0.5;
|
|
69
|
+
}
|
|
70
|
+
// Heading structure
|
|
71
|
+
let headingStructure;
|
|
72
|
+
if (page.headings.length >= 3) {
|
|
73
|
+
headingStructure = `Good structure: ${page.headings.length} headings organize the content`;
|
|
74
|
+
}
|
|
75
|
+
else if (page.headings.length > 0) {
|
|
76
|
+
headingStructure = `Minimal structure: only ${page.headings.length} heading(s)`;
|
|
77
|
+
score -= 0.5;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
headingStructure = "No heading structure — content is unorganized";
|
|
81
|
+
score -= 1;
|
|
82
|
+
}
|
|
83
|
+
// Error state
|
|
84
|
+
if (page.errorMessages.length > 0) {
|
|
85
|
+
score -= 2;
|
|
86
|
+
issues.push(`${page.errorMessages.length} error messages visible`);
|
|
87
|
+
}
|
|
88
|
+
// Empty page
|
|
89
|
+
if (page.mainText.trim().length < 30) {
|
|
90
|
+
score -= 2;
|
|
91
|
+
issues.push("Extremely sparse content — page appears empty or broken");
|
|
92
|
+
}
|
|
93
|
+
const clampedScore = Math.max(0, Math.min(10, Math.round(score * 10) / 10));
|
|
94
|
+
const assessmentParts = [];
|
|
95
|
+
if (clampedScore >= 8)
|
|
96
|
+
assessmentParts.push("Page purpose and next steps are clear.");
|
|
97
|
+
else if (clampedScore >= 6)
|
|
98
|
+
assessmentParts.push("Page is mostly clear but has some ambiguity.");
|
|
99
|
+
else if (clampedScore >= 4)
|
|
100
|
+
assessmentParts.push("Page clarity needs improvement — users may be confused.");
|
|
101
|
+
else
|
|
102
|
+
assessmentParts.push("Page is unclear — users won't know what to do.");
|
|
103
|
+
if (issues.length > 0)
|
|
104
|
+
assessmentParts.push(`Issues: ${issues.join("; ")}.`);
|
|
105
|
+
return {
|
|
106
|
+
score: clampedScore,
|
|
107
|
+
valueProposition,
|
|
108
|
+
ctaClarity,
|
|
109
|
+
navigationLogic,
|
|
110
|
+
headingStructure,
|
|
111
|
+
assessment: assessmentParts.join(" "),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=clarity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clarity.js","sourceRoot":"","sources":["../../src/analysis/clarity.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAkB;IAC9C,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC,mCAAmC;IACnD,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,oCAAoC;IACpC,IAAI,gBAAwB,CAAC;IAC7B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,SAAS,GAAG,2EAA2E,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvG,MAAM,OAAO,GAAG,qCAAqC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAE3F,IAAI,OAAO,EAAE,CAAC;YACZ,gBAAgB,GAAG,mBAAmB,EAAE,2CAA2C,CAAC;YACpF,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACrB,gBAAgB,GAAG,mCAAmC,EAAE,GAAG,CAAC;QAC9D,CAAC;aAAM,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC1B,gBAAgB,GAAG,yBAAyB,EAAE,GAAG,CAAC;YAClD,KAAK,IAAI,GAAG,CAAC;QACf,CAAC;aAAM,CAAC;YACN,gBAAgB,GAAG,mBAAmB,EAAE,gCAAgC,CAAC;YACzE,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;SAAM,CAAC;QACN,gBAAgB,GAAG,6DAA6D,CAAC;QACjF,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,cAAc;IACd,IAAI,UAAkB,CAAC;IACvB,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC/D,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,UAAU,GAAG,kDAAkD,CAAC;QAChE,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;SAAM,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,UAAU,GAAG,sBAAsB,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QAC7D,KAAK,IAAI,GAAG,CAAC;IACf,CAAC;SAAM,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACtC,UAAU,GAAG,GAAG,cAAc,CAAC,MAAM,gEAAgE,CAAC;IACxG,CAAC;SAAM,CAAC;QACN,UAAU,GAAG,GAAG,cAAc,CAAC,MAAM,uDAAuD,CAAC;QAC7F,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,mBAAmB;IACnB,IAAI,eAAuB,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvD,eAAe,GAAG,uCAAuC,CAAC;QAC1D,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;SAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAChC,eAAe,GAAG,GAAG,QAAQ,CAAC,MAAM,mDAAmD,CAAC;QACxF,KAAK,IAAI,GAAG,CAAC;IACf,CAAC;SAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,eAAe,GAAG,GAAG,QAAQ,CAAC,MAAM,kDAAkD,CAAC;IACzF,CAAC;SAAM,CAAC;QACN,eAAe,GAAG,sDAAsD,CAAC;QACzE,KAAK,IAAI,GAAG,CAAC;IACf,CAAC;IAED,oBAAoB;IACpB,IAAI,gBAAwB,CAAC;IAC7B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC9B,gBAAgB,GAAG,mBAAmB,IAAI,CAAC,QAAQ,CAAC,MAAM,gCAAgC,CAAC;IAC7F,CAAC;SAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,gBAAgB,GAAG,2BAA2B,IAAI,CAAC,QAAQ,CAAC,MAAM,aAAa,CAAC;QAChF,KAAK,IAAI,GAAG,CAAC;IACf,CAAC;SAAM,CAAC;QACN,gBAAgB,GAAG,+CAA+C,CAAC;QACnE,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,cAAc;IACd,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,KAAK,IAAI,CAAC,CAAC;QACX,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,yBAAyB,CAAC,CAAC;IACrE,CAAC;IAED,aAAa;IACb,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACrC,KAAK,IAAI,CAAC,CAAC;QACX,MAAM,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAE5E,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,IAAI,YAAY,IAAI,CAAC;QAAE,eAAe,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;SACjF,IAAI,YAAY,IAAI,CAAC;QAAE,eAAe,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;SAC5F,IAAI,YAAY,IAAI,CAAC;QAAE,eAAe,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;;QACvG,eAAe,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;IAE5E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,eAAe,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE7E,OAAO;QACL,KAAK,EAAE,YAAY;QACnB,gBAAgB;QAChB,UAAU;QACV,eAAe;QACf,gBAAgB;QAChB,UAAU,EAAE,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;KACtC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PageSnapshot, CognitiveLoadAssessment } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Assess the cognitive load of a page snapshot.
|
|
4
|
+
* Returns a score from 0 (minimal) to 10 (overwhelming).
|
|
5
|
+
*/
|
|
6
|
+
export declare function assessCognitiveLoad(page: PageSnapshot): CognitiveLoadAssessment;
|
|
7
|
+
//# sourceMappingURL=cognitive-load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognitive-load.d.ts","sourceRoot":"","sources":["../../src/analysis/cognitive-load.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,uBAAuB,CAsC/E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assess the cognitive load of a page snapshot.
|
|
3
|
+
* Returns a score from 0 (minimal) to 10 (overwhelming).
|
|
4
|
+
*/
|
|
5
|
+
export function assessCognitiveLoad(page) {
|
|
6
|
+
const elementCount = page.interactiveElements.length + page.headings.length;
|
|
7
|
+
const interactiveCount = page.interactiveElements.length;
|
|
8
|
+
const textLength = page.mainText.length;
|
|
9
|
+
const textDensity = textLength / Math.max(1, page.headings.length + 1); // chars per section
|
|
10
|
+
const decisionPoints = page.buttons.length + page.links.filter((l) => l.isVisible).length;
|
|
11
|
+
// Score components (each 0-2, sum to 0-10)
|
|
12
|
+
const elementScore = elementCount > 50 ? 2 : elementCount > 30 ? 1.5 : elementCount > 15 ? 1 : 0.5;
|
|
13
|
+
const interactiveScore = interactiveCount > 20 ? 2 : interactiveCount > 12 ? 1.5 : interactiveCount > 6 ? 1 : 0.5;
|
|
14
|
+
const textScore = textDensity > 500 ? 2 : textDensity > 300 ? 1.5 : textDensity > 150 ? 1 : 0.5;
|
|
15
|
+
const decisionScore = decisionPoints > 15 ? 2 : decisionPoints > 8 ? 1.5 : decisionPoints > 4 ? 1 : 0.5;
|
|
16
|
+
const formScore = page.formFields.length > 6 ? 2 : page.formFields.length > 3 ? 1.5 : page.formFields.length > 0 ? 1 : 0;
|
|
17
|
+
const rawScore = elementScore + interactiveScore + textScore + decisionScore + formScore;
|
|
18
|
+
const score = Math.min(10, Math.round(rawScore * 10) / 10);
|
|
19
|
+
const visualComplexity = score > 7 ? "Very High" : score > 5 ? "High" : score > 3 ? "Moderate" : "Low";
|
|
20
|
+
const assessmentParts = [];
|
|
21
|
+
if (score > 7)
|
|
22
|
+
assessmentParts.push("This page is overwhelming — too many elements competing for attention.");
|
|
23
|
+
else if (score > 5)
|
|
24
|
+
assessmentParts.push("This page has high cognitive load — users may feel decision fatigue.");
|
|
25
|
+
else if (score > 3)
|
|
26
|
+
assessmentParts.push("This page has moderate cognitive load — manageable for most users.");
|
|
27
|
+
else
|
|
28
|
+
assessmentParts.push("This page has low cognitive load — clean and focused.");
|
|
29
|
+
if (decisionPoints > 10)
|
|
30
|
+
assessmentParts.push(`${decisionPoints} clickable elements create decision paralysis.`);
|
|
31
|
+
if (page.formFields.length > 5)
|
|
32
|
+
assessmentParts.push(`Form with ${page.formFields.length} fields adds significant friction.`);
|
|
33
|
+
if (page.headings.length === 0)
|
|
34
|
+
assessmentParts.push("No headings to organize content — users lack visual anchors.");
|
|
35
|
+
return {
|
|
36
|
+
score,
|
|
37
|
+
elementCount,
|
|
38
|
+
interactiveCount,
|
|
39
|
+
textDensity: Math.round(textDensity),
|
|
40
|
+
decisionPoints,
|
|
41
|
+
visualComplexity,
|
|
42
|
+
assessment: assessmentParts.join(" "),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cognitive-load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognitive-load.js","sourceRoot":"","sources":["../../src/analysis/cognitive-load.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EmotionalArc, SessionStep } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Analyze the emotional arc across a session's steps.
|
|
4
|
+
*/
|
|
5
|
+
export declare function analyzeEmotionalArc(steps: readonly SessionStep[]): EmotionalArc;
|
|
6
|
+
/**
|
|
7
|
+
* Get a human-readable summary of the emotional journey.
|
|
8
|
+
*/
|
|
9
|
+
export declare function summarizeEmotionalArc(arc: EmotionalArc): string;
|
|
10
|
+
//# sourceMappingURL=emotional-arc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emotional-arc.d.ts","sourceRoot":"","sources":["../../src/analysis/emotional-arc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAa7E;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,YAAY,CAuC/E;AA+BD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAsB/D"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const EMOTION_VALENCE = {
|
|
2
|
+
delighted: 5,
|
|
3
|
+
confident: 4,
|
|
4
|
+
curious: 3,
|
|
5
|
+
neutral: 2,
|
|
6
|
+
anxious: 1,
|
|
7
|
+
confused: 0,
|
|
8
|
+
bored: -1,
|
|
9
|
+
frustrated: -2,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Analyze the emotional arc across a session's steps.
|
|
13
|
+
*/
|
|
14
|
+
export function analyzeEmotionalArc(steps) {
|
|
15
|
+
if (steps.length === 0) {
|
|
16
|
+
return {
|
|
17
|
+
states: [],
|
|
18
|
+
trend: "stable",
|
|
19
|
+
lowestPoint: { step: 0, state: "neutral" },
|
|
20
|
+
highestPoint: { step: 0, state: "neutral" },
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const states = steps.map((s) => s.emotionalState);
|
|
24
|
+
// Find highest and lowest points
|
|
25
|
+
let lowestStep = 0;
|
|
26
|
+
let lowestValence = Infinity;
|
|
27
|
+
let highestStep = 0;
|
|
28
|
+
let highestValence = -Infinity;
|
|
29
|
+
for (let i = 0; i < states.length; i++) {
|
|
30
|
+
const valence = EMOTION_VALENCE[states[i]] ?? 2;
|
|
31
|
+
if (valence < lowestValence) {
|
|
32
|
+
lowestValence = valence;
|
|
33
|
+
lowestStep = i;
|
|
34
|
+
}
|
|
35
|
+
if (valence > highestValence) {
|
|
36
|
+
highestValence = valence;
|
|
37
|
+
highestStep = i;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Determine trend
|
|
41
|
+
const trend = determineTrend(states);
|
|
42
|
+
return {
|
|
43
|
+
states,
|
|
44
|
+
trend,
|
|
45
|
+
lowestPoint: { step: lowestStep, state: states[lowestStep] },
|
|
46
|
+
highestPoint: { step: highestStep, state: states[highestStep] },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Determine the overall emotional trend of the session.
|
|
51
|
+
*/
|
|
52
|
+
function determineTrend(states) {
|
|
53
|
+
if (states.length < 2)
|
|
54
|
+
return "stable";
|
|
55
|
+
const valences = states.map((s) => EMOTION_VALENCE[s] ?? 2);
|
|
56
|
+
// Calculate changes between consecutive steps
|
|
57
|
+
const changes = [];
|
|
58
|
+
for (let i = 1; i < valences.length; i++) {
|
|
59
|
+
changes.push(valences[i] - valences[i - 1]);
|
|
60
|
+
}
|
|
61
|
+
const avgChange = changes.reduce((sum, c) => sum + c, 0) / changes.length;
|
|
62
|
+
const volatility = changes.reduce((sum, c) => sum + Math.abs(c), 0) / changes.length;
|
|
63
|
+
// High volatility = volatile
|
|
64
|
+
if (volatility > 2)
|
|
65
|
+
return "volatile";
|
|
66
|
+
// Consistent direction
|
|
67
|
+
if (avgChange > 0.3)
|
|
68
|
+
return "improving";
|
|
69
|
+
if (avgChange < -0.3)
|
|
70
|
+
return "declining";
|
|
71
|
+
return "stable";
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get a human-readable summary of the emotional journey.
|
|
75
|
+
*/
|
|
76
|
+
export function summarizeEmotionalArc(arc) {
|
|
77
|
+
if (arc.states.length === 0)
|
|
78
|
+
return "No emotional data recorded.";
|
|
79
|
+
const trendDescriptions = {
|
|
80
|
+
improving: "The user's experience improved over the session",
|
|
81
|
+
stable: "The user's emotional state remained relatively consistent",
|
|
82
|
+
declining: "The user became increasingly frustrated or disengaged",
|
|
83
|
+
volatile: "The user's experience was inconsistent — swinging between positive and negative",
|
|
84
|
+
};
|
|
85
|
+
const parts = [trendDescriptions[arc.trend] ?? "Unknown trend"];
|
|
86
|
+
parts.push(`Lowest point: step ${arc.lowestPoint.step} (${arc.lowestPoint.state})`);
|
|
87
|
+
parts.push(`Highest point: step ${arc.highestPoint.step} (${arc.highestPoint.state})`);
|
|
88
|
+
const startEmotion = arc.states[0];
|
|
89
|
+
const endEmotion = arc.states[arc.states.length - 1];
|
|
90
|
+
if (startEmotion !== endEmotion) {
|
|
91
|
+
parts.push(`Journey: ${startEmotion} → ${endEmotion}`);
|
|
92
|
+
}
|
|
93
|
+
return parts.join(". ") + ".";
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=emotional-arc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emotional-arc.js","sourceRoot":"","sources":["../../src/analysis/emotional-arc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FrictionPoint, SessionStep, FrictionSeverity } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Calculate an overall friction score for a set of steps.
|
|
4
|
+
* Returns 0-10 where 10 is maximum friction.
|
|
5
|
+
*/
|
|
6
|
+
export declare function calculateFrictionScore(steps: readonly SessionStep[]): number;
|
|
7
|
+
/**
|
|
8
|
+
* Get all friction points sorted by severity (critical first).
|
|
9
|
+
*/
|
|
10
|
+
export declare function getSortedFriction(steps: readonly SessionStep[]): readonly FrictionPoint[];
|
|
11
|
+
/**
|
|
12
|
+
* Identify which steps have the highest friction concentration.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getHighFrictionSteps(steps: readonly SessionStep[]): readonly SessionStep[];
|
|
15
|
+
/**
|
|
16
|
+
* Group friction points by their location (URL).
|
|
17
|
+
*/
|
|
18
|
+
export declare function groupFrictionByLocation(steps: readonly SessionStep[]): ReadonlyMap<string, readonly FrictionPoint[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Get a count summary of friction by severity level.
|
|
21
|
+
*/
|
|
22
|
+
export declare function countBySeverity(steps: readonly SessionStep[]): Readonly<Record<FrictionSeverity, number>>;
|
|
23
|
+
//# sourceMappingURL=friction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"friction.d.ts","sourceRoot":"","sources":["../../src/analysis/friction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAShF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAY5E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,aAAa,EAAE,CAKzF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,WAAW,EAAE,CAI1F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,WAAW,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC,CAU/C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,QAAQ,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAQ5C"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const SEVERITY_WEIGHTS = {
|
|
2
|
+
low: 1,
|
|
3
|
+
medium: 2,
|
|
4
|
+
high: 4,
|
|
5
|
+
critical: 7,
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Calculate an overall friction score for a set of steps.
|
|
9
|
+
* Returns 0-10 where 10 is maximum friction.
|
|
10
|
+
*/
|
|
11
|
+
export function calculateFrictionScore(steps) {
|
|
12
|
+
const allFriction = steps.flatMap((s) => s.frictionPoints);
|
|
13
|
+
if (allFriction.length === 0)
|
|
14
|
+
return 0;
|
|
15
|
+
const totalWeight = allFriction.reduce((sum, f) => sum + (SEVERITY_WEIGHTS[f.severity] ?? 1), 0);
|
|
16
|
+
// Normalize: scale by step count, cap at 10
|
|
17
|
+
const normalized = (totalWeight / Math.max(1, steps.length)) * 2;
|
|
18
|
+
return Math.min(10, Math.round(normalized * 10) / 10);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get all friction points sorted by severity (critical first).
|
|
22
|
+
*/
|
|
23
|
+
export function getSortedFriction(steps) {
|
|
24
|
+
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
25
|
+
return steps
|
|
26
|
+
.flatMap((s) => s.frictionPoints)
|
|
27
|
+
.sort((a, b) => (severityOrder[a.severity] ?? 3) - (severityOrder[b.severity] ?? 3));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Identify which steps have the highest friction concentration.
|
|
31
|
+
*/
|
|
32
|
+
export function getHighFrictionSteps(steps) {
|
|
33
|
+
return steps.filter((s) => s.frictionPoints.some((f) => f.severity === "high" || f.severity === "critical"));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Group friction points by their location (URL).
|
|
37
|
+
*/
|
|
38
|
+
export function groupFrictionByLocation(steps) {
|
|
39
|
+
const map = new Map();
|
|
40
|
+
for (const step of steps) {
|
|
41
|
+
for (const friction of step.frictionPoints) {
|
|
42
|
+
const existing = map.get(friction.location) ?? [];
|
|
43
|
+
existing.push(friction);
|
|
44
|
+
map.set(friction.location, existing);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return map;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get a count summary of friction by severity level.
|
|
51
|
+
*/
|
|
52
|
+
export function countBySeverity(steps) {
|
|
53
|
+
const counts = { low: 0, medium: 0, high: 0, critical: 0 };
|
|
54
|
+
for (const step of steps) {
|
|
55
|
+
for (const friction of step.frictionPoints) {
|
|
56
|
+
counts[friction.severity]++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return counts;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=friction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"friction.js","sourceRoot":"","sources":["../../src/analysis/friction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { UserSession, PersonaComparison } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Compare sessions from multiple personas on the same flow.
|
|
4
|
+
* Identifies shared friction points and divergence points.
|
|
5
|
+
*/
|
|
6
|
+
export declare function comparePersonaSessions(sessions: readonly UserSession[]): PersonaComparison;
|
|
7
|
+
/**
|
|
8
|
+
* Generate a markdown comparison report.
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateComparisonReport(comparison: PersonaComparison): string;
|
|
11
|
+
//# sourceMappingURL=comparison.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison.d.ts","sourceRoot":"","sources":["../../src/feedback/comparison.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAiB,MAAM,aAAa,CAAC;AAEjF;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC/B,iBAAiB,CAuHnB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,iBAAiB,GAAG,MAAM,CA0D9E"}
|