feat(web): add Agentation feedback overlay for UI annotation
Mounts the Agentation React component (v3) as a thin overlay on the vanilla TS app via a React root shim. The toolbar appears bottom-right and lets users click any element, annotate it, and generate structured markdown for AI agent feedback. Submissions are persisted in localStorage and POSTed to /api/ui-feedback when the API is available. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
dcdca5147f
commit
72addb2089
4 changed files with 133 additions and 1 deletions
74
web/package-lock.json
generated
74
web/package-lock.json
generated
|
|
@ -8,6 +8,11 @@
|
|||
"name": "acb-web",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"agentation": "^3.0.2",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
|
|
@ -711,6 +716,48 @@
|
|||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agentation": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agentation/-/agentation-3.0.2.tgz",
|
||||
"integrity": "sha512-iGzBxFVTuZEIKzLY6AExSLAQH6i6SwxV4pAu7v7m3X6bInZ7qlZXAwrEqyc4+EfP4gM7z2RXBF6SF4DeH0f2lA==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
||||
|
|
@ -815,6 +862,27 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
|
||||
"integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz",
|
||||
"integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.2.5"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.60.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz",
|
||||
|
|
@ -859,6 +927,12 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"agentation": "^3.0.2",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
|
|
|
|||
51
web/src/agentation-overlay.ts
Normal file
51
web/src/agentation-overlay.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// agentation-overlay.ts
|
||||
// Mounts the Agentation feedback toolbar as a React overlay on the vanilla TS app.
|
||||
// React is only used for this thin shim — the rest of the app remains vanilla TS.
|
||||
//
|
||||
// Agentation lets users click any element, annotate it, and generate structured
|
||||
// markdown output that describes the UI change in terms an AI agent can act on.
|
||||
// Submissions are stored in localStorage and optionally POSTed to /api/feedback
|
||||
// when the backend API is available.
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { Agentation } from 'agentation'
|
||||
import type { Annotation } from 'agentation'
|
||||
|
||||
const STORAGE_KEY = 'acb:agentation:feedback'
|
||||
const MAX_STORED = 50
|
||||
|
||||
function handleSubmit(markdown: string, annotations: Annotation[]): void {
|
||||
console.log('[agentation] Feedback submitted')
|
||||
|
||||
// Persist locally
|
||||
const existing: Array<{ markdown: string; annotations: Annotation[]; submittedAt: number }> =
|
||||
JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '[]')
|
||||
existing.push({ markdown, annotations, submittedAt: Date.now() })
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(existing.slice(-MAX_STORED)))
|
||||
|
||||
// POST to the API if available (non-blocking, best-effort)
|
||||
const apiBase = (window as unknown as Record<string, string>)['ACB_API_BASE'] ?? '/api'
|
||||
fetch(`${apiBase}/ui-feedback`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ markdown, annotations, submitted_at: new Date().toISOString() }),
|
||||
}).catch(() => {
|
||||
// API not available yet — localStorage fallback is sufficient
|
||||
})
|
||||
}
|
||||
|
||||
export function initAgentation(): void {
|
||||
const container = document.createElement('div')
|
||||
container.id = 'agentation-root'
|
||||
document.body.appendChild(container)
|
||||
|
||||
const root = ReactDOM.createRoot(container)
|
||||
// Cast through ElementType so createElement accepts the props without JSX support
|
||||
root.render(
|
||||
React.createElement(Agentation as React.ElementType, {
|
||||
onSubmit: handleSubmit,
|
||||
copyToClipboard: true,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// Main SPA entry point with routing
|
||||
import { router } from './router';
|
||||
import { initAgentation } from './agentation-overlay';
|
||||
import { renderHomePage } from './pages/home';
|
||||
import { renderLeaderboardPage } from './pages/leaderboard';
|
||||
import { renderMatchesPage } from './pages/matches';
|
||||
|
|
@ -781,10 +782,11 @@ function renderNotFoundPage(): void {
|
|||
`;
|
||||
}
|
||||
|
||||
// Start the router
|
||||
// Start the router and mount the Agentation feedback overlay
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
updateActiveNavLink();
|
||||
router.start();
|
||||
initAgentation();
|
||||
});
|
||||
|
||||
// Update nav on initial load
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue