Files
inter-hub/docs/annotation-launcher.md
Bernd Worsch 04eb4643b0 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>
2026-03-29 21:17:43 +00:00

2.2 KiB

IHF Annotation Launcher

static/js/ihf-annotation-launcher.js is a self-contained vanilla JS module that injects an Annotate button adjacent to every element that carries a data-widget-id attribute.

It works in IHP server-rendered pages and in React/Vue pages where IHP does not own the DOM — the launcher relies solely on the presence of data-widget-id, not on any framework-specific structure.

Activation

Set the environment variable before starting the server:

IHP_ANNOTATION_LAUNCHER=true devenv up

This causes IHP to emit the following tag in every page's <head>:

<script src="/js/ihf-annotation-launcher.js"></script>

When IHP_ANNOTATION_LAUNCHER is absent or not "true", the script is not loaded.

Data attributes

Attribute Required Description
data-widget-id Yes UUID of the widget (from the widget registry)
data-hub-id No UUID of the hub — read from element or nearest ancestor
data-view-context No Logical UI location (informational)

Usage in React

Because the launcher uses MutationObserver, it will pick up React-rendered elements after mount:

function MyWidget({ widgetId, hubId }) {
  return (
    <div data-widget-id={widgetId} data-hub-id={hubId}>
      <button>Do something</button>
    </div>
  );
}

No other integration is required. The launcher injects the Annotate button and handles POST submission to /widgets/:widgetId/annotations.

Programmatic re-scan

If your SPA does client-side navigation without full page reloads, call:

window.IHFAnnotationLauncher.scan();

Submission

The launcher POSTs application/x-www-form-urlencoded to:

POST /widgets/:widgetId/annotations

Fields submitted: body, category, severity (fixed low), CSRF token.

Responses:

  • 200 or redirect → success (form closes with confirmation)
  • Any non-OK response → inline error message

CSRF

The launcher reads the CSRF token from <meta name="csrf-token" content="...">. IHP emits this tag automatically in authenticated layouts.