feat(P6/T05): cross-framework annotation launcher JS widget

ihf-annotation-launcher.js: vanilla JS, no framework dependency. Scans DOM for
data-widget-id elements, injects Annotate trigger, opens inline form, POSTs to
/widgets/:widgetId/annotations. Works in React/Vue-rendered pages via
MutationObserver. Feature-gated by IHP_ANNOTATION_LAUNCHER=true env var
(Config.hs AnnotationLauncherEnabled, FrontController layout conditional).
Docs: docs/annotation-launcher.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:17:43 +00:00
parent 32bb003f3b
commit 04eb4643b0
5 changed files with 357 additions and 4 deletions

View File

@@ -2,9 +2,11 @@ module Web.FrontController where
import IHP.RouterPrelude
import IHP.LoginSupport.Middleware
import IHP.ControllerPrelude (getAppConfig)
import Generated.Types
import Web.Types
import Web.Routes ()
import Config (AnnotationLauncherEnabled (..))
-- Controllers
import Web.Controller.Hubs ()
@@ -47,6 +49,13 @@ instance InitControllerContext WebApplication where
setLayout defaultLayout
initAuthentication @User
annotationLauncherScript :: (?context :: ControllerContext) => Html
annotationLauncherScript =
let AnnotationLauncherEnabled enabled = getAppConfig @AnnotationLauncherEnabled
in if enabled
then [hsx|<script src="/js/ihf-annotation-launcher.js"></script>|]
else mempty
defaultLayout :: Layout
defaultLayout inner = [hsx|
<!DOCTYPE html>
@@ -59,6 +68,7 @@ defaultLayout inner = [hsx|
<link rel="stylesheet" href="/app.css" />
<script src="/vendor/morphdom.js"></script>
<script src="/vendor/ihp-auto-refresh.js"></script>
{annotationLauncherScript}
</head>
<body class="bg-gray-50 text-gray-900">
<nav class="bg-white border-b border-gray-200 px-6 py-3 flex items-center gap-6">