@agent-native/core 0.43.0 → 0.44.1

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.
Files changed (124) hide show
  1. package/dist/chat-threads/store.d.ts.map +1 -1
  2. package/dist/chat-threads/store.js +71 -10
  3. package/dist/chat-threads/store.js.map +1 -1
  4. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  5. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  6. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  7. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  8. package/dist/cli/recap.d.ts +23 -0
  9. package/dist/cli/recap.d.ts.map +1 -1
  10. package/dist/cli/recap.js +177 -13
  11. package/dist/cli/recap.js.map +1 -1
  12. package/dist/cli/skills.d.ts +3 -3
  13. package/dist/cli/skills.d.ts.map +1 -1
  14. package/dist/cli/skills.js +67 -20
  15. package/dist/cli/skills.js.map +1 -1
  16. package/dist/client/AssistantChat.d.ts.map +1 -1
  17. package/dist/client/AssistantChat.js +76 -18
  18. package/dist/client/AssistantChat.js.map +1 -1
  19. package/dist/client/blocks/index.d.ts +0 -2
  20. package/dist/client/blocks/index.d.ts.map +1 -1
  21. package/dist/client/blocks/index.js +0 -2
  22. package/dist/client/blocks/index.js.map +1 -1
  23. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  24. package/dist/client/blocks/library/AnnotatedCodeBlock.js +22 -9
  25. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  26. package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
  27. package/dist/client/blocks/library/ApiEndpointBlock.js +113 -13
  28. package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
  29. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
  30. package/dist/client/blocks/library/DiffBlock.js +63 -35
  31. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  32. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
  33. package/dist/client/blocks/library/FileTreeBlock.js +4 -0
  34. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
  35. package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -1
  36. package/dist/client/blocks/library/JsonExplorerBlock.js +1 -1
  37. package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
  38. package/dist/client/blocks/library/MermaidBlock.d.ts.map +1 -1
  39. package/dist/client/blocks/library/MermaidBlock.js +22 -3
  40. package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
  41. package/dist/client/blocks/library/annotation-rail.d.ts +85 -19
  42. package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
  43. package/dist/client/blocks/library/annotation-rail.js +149 -27
  44. package/dist/client/blocks/library/annotation-rail.js.map +1 -1
  45. package/dist/client/blocks/library/code-tabs.js +1 -1
  46. package/dist/client/blocks/library/code-tabs.js.map +1 -1
  47. package/dist/client/blocks/library/diagram.d.ts +17 -0
  48. package/dist/client/blocks/library/diagram.d.ts.map +1 -1
  49. package/dist/client/blocks/library/diagram.js +47 -2
  50. package/dist/client/blocks/library/diagram.js.map +1 -1
  51. package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
  52. package/dist/client/blocks/library/server-specs.js +0 -10
  53. package/dist/client/blocks/library/server-specs.js.map +1 -1
  54. package/dist/client/blocks/library/specs.d.ts.map +1 -1
  55. package/dist/client/blocks/library/specs.js +0 -2
  56. package/dist/client/blocks/library/specs.js.map +1 -1
  57. package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -1
  58. package/dist/client/blocks/library/wireframe.config.js +19 -2
  59. package/dist/client/blocks/library/wireframe.config.js.map +1 -1
  60. package/dist/client/blocks/mdx.d.ts.map +1 -1
  61. package/dist/client/blocks/mdx.js +11 -0
  62. package/dist/client/blocks/mdx.js.map +1 -1
  63. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  64. package/dist/client/composer/TiptapComposer.js +13 -8
  65. package/dist/client/composer/TiptapComposer.js.map +1 -1
  66. package/dist/client/composer/pasted-text.d.ts +25 -0
  67. package/dist/client/composer/pasted-text.d.ts.map +1 -1
  68. package/dist/client/composer/pasted-text.js +86 -4
  69. package/dist/client/composer/pasted-text.js.map +1 -1
  70. package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
  71. package/dist/client/rich-markdown-editor/DragHandle.js +35 -72
  72. package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
  73. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
  74. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +1 -1
  75. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
  76. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +9 -1
  77. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  78. package/dist/client/rich-markdown-editor/SharedRichEditor.js +3 -1
  79. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  80. package/dist/client/rich-markdown-editor/extensions.d.ts +13 -1
  81. package/dist/client/rich-markdown-editor/extensions.d.ts.map +1 -1
  82. package/dist/client/rich-markdown-editor/extensions.js +4 -2
  83. package/dist/client/rich-markdown-editor/extensions.js.map +1 -1
  84. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  85. package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -1
  86. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  87. package/dist/db/migrations.d.ts +10 -0
  88. package/dist/db/migrations.d.ts.map +1 -1
  89. package/dist/db/migrations.js +32 -0
  90. package/dist/db/migrations.js.map +1 -1
  91. package/dist/server/og-fonts-data.d.ts +3 -0
  92. package/dist/server/og-fonts-data.d.ts.map +1 -0
  93. package/dist/server/og-fonts-data.js +9 -0
  94. package/dist/server/og-fonts-data.js.map +1 -0
  95. package/dist/server/og-fonts.d.ts +10 -0
  96. package/dist/server/og-fonts.d.ts.map +1 -0
  97. package/dist/server/og-fonts.js +58 -0
  98. package/dist/server/og-fonts.js.map +1 -0
  99. package/dist/server/poll.d.ts.map +1 -1
  100. package/dist/server/poll.js +30 -14
  101. package/dist/server/poll.js.map +1 -1
  102. package/dist/server/social-og-image.d.ts.map +1 -1
  103. package/dist/server/social-og-image.js +16 -5
  104. package/dist/server/social-og-image.js.map +1 -1
  105. package/dist/styles/blocks.css +121 -2
  106. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +2 -0
  107. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +141 -0
  108. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +2 -0
  109. package/dist/usage/store.d.ts +12 -0
  110. package/dist/usage/store.d.ts.map +1 -1
  111. package/dist/usage/store.js +35 -5
  112. package/dist/usage/store.js.map +1 -1
  113. package/package.json +1 -1
  114. package/src/templates/default/.agents/skills/storing-data/SKILL.md +2 -0
  115. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +141 -0
  116. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +2 -0
  117. package/dist/client/blocks/library/decision.config.d.ts +0 -37
  118. package/dist/client/blocks/library/decision.config.d.ts.map +0 -1
  119. package/dist/client/blocks/library/decision.config.js +0 -32
  120. package/dist/client/blocks/library/decision.config.js.map +0 -1
  121. package/dist/client/blocks/library/decision.d.ts +0 -19
  122. package/dist/client/blocks/library/decision.d.ts.map +0 -1
  123. package/dist/client/blocks/library/decision.js +0 -119
  124. package/dist/client/blocks/library/decision.js.map +0 -1
@@ -74,12 +74,97 @@ export declare function AnnotationGutterMarker({ marker, active, className, }: {
74
74
  active: boolean;
75
75
  className?: string;
76
76
  }): import("react/jsx-runtime").JSX.Element;
77
+ /**
78
+ * One line-anchored note card: marker pip (when `showMarker`), the resolved line
79
+ * span ("Line 8"), an optional label, and the markdown `note` (via
80
+ * `ctx.renderMarkdown`). This is the single source of card markup, rendered both
81
+ * by the visually-hidden a11y/test stack and by the on-hover portal popover.
82
+ */
83
+ export declare function AnnotationCard<A extends RailAnnotation>({ item, ctx, active, showMarker, className, onMouseEnter, onMouseLeave, }: {
84
+ item: ResolvedAnnotation<A>;
85
+ ctx: BlockRenderContext;
86
+ active?: boolean;
87
+ showMarker?: boolean;
88
+ className?: string;
89
+ onMouseEnter?: () => void;
90
+ onMouseLeave?: () => void;
91
+ }): import("react/jsx-runtime").JSX.Element;
92
+ /**
93
+ * Visually-hidden stack of every resolved note. It is NOT a visible column — it
94
+ * sits in the flow but clipped to a 1px box (the `sr-only` pattern) so the note
95
+ * text and the per-card marker pips stay in the accessibility tree and in the
96
+ * DOM (assistive tech can reach them, and tests that read `textContent`/count
97
+ * pips still see them) WITHOUT painting a persistent rail beside the code. The
98
+ * visible card appears only on hover via {@link AnnotationHoverCard}.
99
+ */
100
+ export declare function AnnotationHiddenStack<A extends RailAnnotation>({ items, ctx, showMarker, }: {
101
+ items: ResolvedAnnotation<A>[];
102
+ ctx: BlockRenderContext;
103
+ showMarker?: boolean;
104
+ }): import("react/jsx-runtime").JSX.Element;
105
+ /** The geometry the hover card anchors to (in viewport coordinates). */
106
+ export interface AnnotationAnchor {
107
+ /** Right edge of the code block (where the card should start). */
108
+ codeRight: number;
109
+ /** Left edge of the code block (for the below-fallback alignment). */
110
+ codeLeft: number;
111
+ /** Vertical center of the hovered line. */
112
+ lineCenter: number;
113
+ /** Bottom of the hovered line (for the below-fallback placement). */
114
+ lineBottom: number;
115
+ }
116
+ /**
117
+ * The single on-hover note card, portaled to `document.body` and positioned
118
+ * `fixed` so it escapes the code block's `overflow` and never reflows the code.
119
+ *
120
+ * Placement: by default it sits to the RIGHT of the code block's right edge,
121
+ * vertically centered on the hovered line — so it never overlaps the code text.
122
+ * If there isn't room to the right (it would overflow the viewport), it clamps
123
+ * within the viewport, and if the right gutter is too narrow for the card it
124
+ * falls back to BELOW the hovered line (left-aligned to the code block). The card
125
+ * keeps itself open while hovered (`onMouseEnter`/`onMouseLeave` forwarded) so it
126
+ * stays readable; the caller adds the small hover-intent close delay.
127
+ */
128
+ export declare function AnnotationHoverCard<A extends RailAnnotation>({ item, anchor, ctx, showMarker, onMouseEnter, onMouseLeave, }: {
129
+ item: ResolvedAnnotation<A>;
130
+ anchor: AnnotationAnchor;
131
+ ctx: BlockRenderContext;
132
+ showMarker?: boolean;
133
+ onMouseEnter?: () => void;
134
+ onMouseLeave?: () => void;
135
+ }): any;
136
+ /**
137
+ * Hover-intent controller for the on-hover note card. Exposes `activeIndex` +
138
+ * the captured `anchor`, plus `open`/`scheduleClose`/`cancelClose` handlers.
139
+ *
140
+ * - `open(index, anchor)` shows a card immediately (cancels any pending close).
141
+ * - `scheduleClose()` hides after a short delay, so moving the pointer from the
142
+ * code line across the gap into the card itself keeps it open.
143
+ * - `cancelClose()` (call on card mouse-enter) keeps it open while reading.
144
+ */
145
+ export declare function useAnnotationHover(delay?: number): {
146
+ readonly activeIndex: number;
147
+ readonly anchor: AnnotationAnchor;
148
+ readonly open: (index: number, anchor: AnnotationAnchor) => void;
149
+ readonly scheduleClose: () => void;
150
+ readonly cancelClose: () => void;
151
+ };
152
+ /**
153
+ * Build an {@link AnnotationAnchor} from the code block element and the hovered
154
+ * row element. The card anchors to the code block's RIGHT edge and the row's
155
+ * vertical center, both in viewport coordinates (so a `fixed` portal lines up).
156
+ */
157
+ export declare function anchorFromElements(codeEl: HTMLElement | null, rowEl: HTMLElement | null): AnnotationAnchor | null;
77
158
  /**
78
159
  * The responsive list of line-anchored note cards. Each card shows its marker
79
160
  * pip, the resolved line span ("Line 8"), an optional label, and the markdown
80
161
  * `note` (via `ctx.renderMarkdown`). Hovering a card sets the active index;
81
162
  * `activeIndex` driven from outside lets a hovered code row light its card and
82
163
  * vice-versa. Only annotations whose `range` resolved are listed.
164
+ *
165
+ * @deprecated Superseded by the on-hover {@link AnnotationHoverCard}; kept for
166
+ * back-compat with any external importer. Both block read renderers now use the
167
+ * hover popover anchored to the right of the code instead of a persistent rail.
83
168
  */
84
169
  export declare function AnnotationNoteRail<A extends RailAnnotation>({ items, activeIndex, onActiveChange, ctx, className, showMarker, }: {
85
170
  items: ResolvedAnnotation<A>[];
@@ -90,25 +175,6 @@ export declare function AnnotationNoteRail<A extends RailAnnotation>({ items, ac
90
175
  /** Show a leading numbered pip on each card (diff block). */
91
176
  showMarker?: boolean;
92
177
  }): import("react/jsx-runtime").JSX.Element;
93
- /**
94
- * A single line-anchored note rendered INLINE in the diff flow — full-width,
95
- * directly under the line it annotates — instead of in a side rail (the
96
- * GitHub-review affordance). The diff block places one of these after the first
97
- * row of each annotation's range, so the numbered `①`/`②` row marker and the note
98
- * read together in one column. It carries the same marker pip, line range,
99
- * optional label, and markdown `note` as a rail card, and the same two-way hover
100
- * wiring (`active` / `onActiveChange`) so the row and its note cross-highlight.
101
- *
102
- * The outer band is `min-w-full` so the amber wash spans the diff's full scroll
103
- * width; the content is `sticky left-0` with a readable `max-w` so the note stays
104
- * pinned and legible while long code lines scroll horizontally beneath it.
105
- */
106
- export declare function InlineAnnotationNote<A extends RailAnnotation>({ item, active, onActiveChange, ctx, }: {
107
- item: ResolvedAnnotation<A>;
108
- active: boolean;
109
- onActiveChange: (index: number | null) => void;
110
- ctx: BlockRenderContext;
111
- }): import("react/jsx-runtime").JSX.Element;
112
178
  /** Whether a resolved list has at least one note worth rendering a rail for. */
113
179
  export declare function hasRailAnnotations(items: ResolvedAnnotation[]): boolean;
114
180
  export type AnnotationRailChildren = ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"annotation-rail.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/annotation-rail.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"annotation-rail.d.ts","sourceRoot":"","sources":["../../../../src/client/blocks/library/annotation-rail.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo } from "react";
2
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
3
+ import { createPortal } from "react-dom";
3
4
  import { cn } from "../../utils.js";
4
5
  /**
5
6
  * Shared line-anchored annotation UI for the `annotated-code` and `diff` blocks.
@@ -96,41 +97,162 @@ export function AnnotationGutterMarker({ marker, active, className, }) {
96
97
  ? "bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950"
97
98
  : "bg-amber-400/25 text-amber-700 dark:bg-amber-300/20 dark:text-amber-300", className), children: marker }));
98
99
  }
99
- /* ── Note rail ─────────────────────────────────────────────────────────────── */
100
+ /* ── Note card ─────────────────────────────────────────────────────────────── */
101
+ /**
102
+ * One line-anchored note card: marker pip (when `showMarker`), the resolved line
103
+ * span ("Line 8"), an optional label, and the markdown `note` (via
104
+ * `ctx.renderMarkdown`). This is the single source of card markup, rendered both
105
+ * by the visually-hidden a11y/test stack and by the on-hover portal popover.
106
+ */
107
+ export function AnnotationCard({ item, ctx, active = false, showMarker = false, className, onMouseEnter, onMouseLeave, }) {
108
+ return (_jsxs("div", { onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, className: cn("rounded-lg border px-3.5 py-2.5 transition-colors", active
109
+ ? "border-amber-400/70 bg-amber-50 dark:border-amber-300/40 dark:bg-amber-300/[0.08]"
110
+ : "border-plan-line bg-plan-block/40 hover:border-amber-400/50", className), children: [_jsxs("div", { className: cn("flex flex-wrap gap-x-2 gap-y-0.5", showMarker ? "items-center" : "items-baseline"), children: [showMarker && (_jsx(AnnotationGutterMarker, { marker: item.marker, active: active })), _jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wide text-plan-muted", children: rangeLabel(item) }), item.annotation.label && (_jsx("span", { className: "text-[13px] font-semibold text-plan-text", children: item.annotation.label }))] }), _jsx("div", { className: "plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85", children: ctx.renderMarkdown ? (ctx.renderMarkdown(item.annotation.note)) : (_jsx("p", { children: item.annotation.note })) })] }));
111
+ }
112
+ /**
113
+ * Visually-hidden stack of every resolved note. It is NOT a visible column — it
114
+ * sits in the flow but clipped to a 1px box (the `sr-only` pattern) so the note
115
+ * text and the per-card marker pips stay in the accessibility tree and in the
116
+ * DOM (assistive tech can reach them, and tests that read `textContent`/count
117
+ * pips still see them) WITHOUT painting a persistent rail beside the code. The
118
+ * visible card appears only on hover via {@link AnnotationHoverCard}.
119
+ */
120
+ export function AnnotationHiddenStack({ items, ctx, showMarker = false, }) {
121
+ const resolved = useMemo(() => items.filter((item) => item.range), [items]);
122
+ if (resolved.length === 0)
123
+ return null;
124
+ return (_jsx("div", { className: "absolute size-px overflow-hidden whitespace-nowrap border-0 p-0 [clip:rect(0,0,0,0)] [clip-path:inset(50%)]", "data-annotation-hidden-stack": true, children: resolved.map((item) => (_jsx(AnnotationCard, { item: item, ctx: ctx, showMarker: showMarker }, item.index))) }));
125
+ }
126
+ const HOVER_CARD_WIDTH = 280;
127
+ const HOVER_CARD_GAP = 12;
128
+ const VIEWPORT_MARGIN = 8;
129
+ /**
130
+ * The single on-hover note card, portaled to `document.body` and positioned
131
+ * `fixed` so it escapes the code block's `overflow` and never reflows the code.
132
+ *
133
+ * Placement: by default it sits to the RIGHT of the code block's right edge,
134
+ * vertically centered on the hovered line — so it never overlaps the code text.
135
+ * If there isn't room to the right (it would overflow the viewport), it clamps
136
+ * within the viewport, and if the right gutter is too narrow for the card it
137
+ * falls back to BELOW the hovered line (left-aligned to the code block). The card
138
+ * keeps itself open while hovered (`onMouseEnter`/`onMouseLeave` forwarded) so it
139
+ * stays readable; the caller adds the small hover-intent close delay.
140
+ */
141
+ export function AnnotationHoverCard({ item, anchor, ctx, showMarker = false, onMouseEnter, onMouseLeave, }) {
142
+ const cardRef = useRef(null);
143
+ const [pos, setPos] = useState(null);
144
+ // Measure the rendered card, then resolve a non-overlapping position: right of
145
+ // the code if it fits, else clamp into the viewport, else drop below the line.
146
+ useLayoutEffect(() => {
147
+ if (typeof window === "undefined")
148
+ return;
149
+ const el = cardRef.current;
150
+ const rect = el?.getBoundingClientRect();
151
+ const width = rect && rect.width > 0 ? rect.width : HOVER_CARD_WIDTH;
152
+ const height = rect && rect.height > 0 ? rect.height : 0;
153
+ const vw = window.innerWidth || 0;
154
+ const vh = window.innerHeight || 0;
155
+ const rightLeft = anchor.codeRight + HOVER_CARD_GAP;
156
+ const fitsRight = rightLeft + width + VIEWPORT_MARGIN <= vw;
157
+ let left;
158
+ let top;
159
+ if (fitsRight) {
160
+ // Default: to the right of the code, centered on the hovered line.
161
+ left = rightLeft;
162
+ top = anchor.lineCenter - height / 2;
163
+ }
164
+ else {
165
+ // No room to the right → drop below the line, aligned to the code's left.
166
+ left = anchor.codeLeft;
167
+ top = anchor.lineBottom + HOVER_CARD_GAP;
168
+ }
169
+ // Clamp within the viewport so the card is never cut off.
170
+ left = Math.max(VIEWPORT_MARGIN, Math.min(left, vw - width - VIEWPORT_MARGIN));
171
+ top = Math.max(VIEWPORT_MARGIN, Math.min(top, vh - height - VIEWPORT_MARGIN));
172
+ setPos({ top, left });
173
+ }, [
174
+ anchor.codeRight,
175
+ anchor.codeLeft,
176
+ anchor.lineCenter,
177
+ anchor.lineBottom,
178
+ item.index,
179
+ ]);
180
+ if (typeof document === "undefined")
181
+ return null;
182
+ return createPortal(_jsx("div", { ref: cardRef, role: "tooltip", "data-annotation-hover-card": true, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, className: "pointer-events-auto fixed z-50", style: {
183
+ top: pos?.top ?? anchor.lineCenter,
184
+ left: pos?.left ?? anchor.codeRight + HOVER_CARD_GAP,
185
+ width: HOVER_CARD_WIDTH,
186
+ // Hide until measured to avoid a one-frame jump from the fallback spot.
187
+ visibility: pos ? "visible" : "hidden",
188
+ }, children: _jsx(AnnotationCard, { item: item, ctx: ctx, active: true, showMarker: showMarker, className: "shadow-lg shadow-black/10 dark:shadow-black/40" }) }), document.body);
189
+ }
190
+ /**
191
+ * Hover-intent controller for the on-hover note card. Exposes `activeIndex` +
192
+ * the captured `anchor`, plus `open`/`scheduleClose`/`cancelClose` handlers.
193
+ *
194
+ * - `open(index, anchor)` shows a card immediately (cancels any pending close).
195
+ * - `scheduleClose()` hides after a short delay, so moving the pointer from the
196
+ * code line across the gap into the card itself keeps it open.
197
+ * - `cancelClose()` (call on card mouse-enter) keeps it open while reading.
198
+ */
199
+ export function useAnnotationHover(delay = 130) {
200
+ const [active, setActive] = useState(null);
201
+ const timer = useRef(null);
202
+ const cancelClose = () => {
203
+ if (timer.current) {
204
+ clearTimeout(timer.current);
205
+ timer.current = null;
206
+ }
207
+ };
208
+ const open = (index, anchor) => {
209
+ cancelClose();
210
+ setActive({ index, anchor });
211
+ };
212
+ const scheduleClose = () => {
213
+ cancelClose();
214
+ timer.current = setTimeout(() => setActive(null), delay);
215
+ };
216
+ useEffect(() => () => cancelClose(), []);
217
+ return {
218
+ activeIndex: active?.index ?? null,
219
+ anchor: active?.anchor ?? null,
220
+ open,
221
+ scheduleClose,
222
+ cancelClose,
223
+ };
224
+ }
225
+ /**
226
+ * Build an {@link AnnotationAnchor} from the code block element and the hovered
227
+ * row element. The card anchors to the code block's RIGHT edge and the row's
228
+ * vertical center, both in viewport coordinates (so a `fixed` portal lines up).
229
+ */
230
+ export function anchorFromElements(codeEl, rowEl) {
231
+ if (!codeEl || !rowEl)
232
+ return null;
233
+ const code = codeEl.getBoundingClientRect();
234
+ const row = rowEl.getBoundingClientRect();
235
+ return {
236
+ codeRight: code.right,
237
+ codeLeft: code.left,
238
+ lineCenter: row.top + row.height / 2,
239
+ lineBottom: row.bottom,
240
+ };
241
+ }
100
242
  /**
101
243
  * The responsive list of line-anchored note cards. Each card shows its marker
102
244
  * pip, the resolved line span ("Line 8"), an optional label, and the markdown
103
245
  * `note` (via `ctx.renderMarkdown`). Hovering a card sets the active index;
104
246
  * `activeIndex` driven from outside lets a hovered code row light its card and
105
247
  * vice-versa. Only annotations whose `range` resolved are listed.
248
+ *
249
+ * @deprecated Superseded by the on-hover {@link AnnotationHoverCard}; kept for
250
+ * back-compat with any external importer. Both block read renderers now use the
251
+ * hover popover anchored to the right of the code instead of a persistent rail.
106
252
  */
107
253
  export function AnnotationNoteRail({ items, activeIndex, onActiveChange, ctx, className, showMarker = false, }) {
108
254
  const sideAnnotations = useMemo(() => items.filter((item) => item.range), [items]);
109
- return (_jsx("div", { className: cn("flex flex-col gap-2.5", className), children: sideAnnotations.map((item) => {
110
- const isActive = activeIndex === item.index;
111
- return (_jsxs("div", { onMouseEnter: () => onActiveChange(item.index), onMouseLeave: () => onActiveChange(null), className: cn("rounded-lg border px-3.5 py-2.5 transition-colors", isActive
112
- ? "border-amber-400/70 bg-amber-50 dark:border-amber-300/40 dark:bg-amber-300/[0.08]"
113
- : "border-plan-line bg-plan-block/40 hover:border-amber-400/50"), children: [_jsxs("div", { className: cn("flex flex-wrap gap-x-2 gap-y-0.5", showMarker ? "items-center" : "items-baseline"), children: [showMarker && (_jsx(AnnotationGutterMarker, { marker: item.marker, active: isActive })), _jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wide text-plan-muted", children: rangeLabel(item) }), item.annotation.label && (_jsx("span", { className: "text-[13px] font-semibold text-plan-text", children: item.annotation.label }))] }), _jsx("div", { className: "plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85", children: ctx.renderMarkdown ? (ctx.renderMarkdown(item.annotation.note)) : (_jsx("p", { children: item.annotation.note })) })] }, item.index));
114
- }) }));
115
- }
116
- /* ── Inline note (GitHub-style) ─────────────────────────────────────────────── */
117
- /**
118
- * A single line-anchored note rendered INLINE in the diff flow — full-width,
119
- * directly under the line it annotates — instead of in a side rail (the
120
- * GitHub-review affordance). The diff block places one of these after the first
121
- * row of each annotation's range, so the numbered `①`/`②` row marker and the note
122
- * read together in one column. It carries the same marker pip, line range,
123
- * optional label, and markdown `note` as a rail card, and the same two-way hover
124
- * wiring (`active` / `onActiveChange`) so the row and its note cross-highlight.
125
- *
126
- * The outer band is `min-w-full` so the amber wash spans the diff's full scroll
127
- * width; the content is `sticky left-0` with a readable `max-w` so the note stays
128
- * pinned and legible while long code lines scroll horizontally beneath it.
129
- */
130
- export function InlineAnnotationNote({ item, active, onActiveChange, ctx, }) {
131
- return (_jsx("div", { onMouseEnter: () => onActiveChange(item.index), onMouseLeave: () => onActiveChange(null), className: cn("min-w-full border-y transition-colors", active
132
- ? "border-amber-400/60 bg-amber-100/70 dark:border-amber-300/40 dark:bg-amber-300/[0.12]"
133
- : "border-amber-400/30 bg-amber-50/70 dark:border-amber-300/20 dark:bg-amber-300/[0.06]"), children: _jsxs("div", { className: "sticky left-0 flex max-w-[44rem] gap-2.5 whitespace-normal px-3 py-2.5 font-sans", children: [_jsx(AnnotationGutterMarker, { marker: item.marker, active: active }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-0.5", children: [_jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wide text-plan-muted", children: rangeLabel(item) }), item.annotation.label && (_jsx("span", { className: "text-[13px] font-semibold text-plan-text", children: item.annotation.label }))] }), _jsx("div", { className: "plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85", children: ctx.renderMarkdown ? (ctx.renderMarkdown(item.annotation.note)) : (_jsx("p", { children: item.annotation.note })) })] })] }) }));
255
+ return (_jsx("div", { className: cn("flex flex-col gap-2.5", className), children: sideAnnotations.map((item) => (_jsx(AnnotationCard, { item: item, ctx: ctx, active: activeIndex === item.index, showMarker: showMarker, onMouseEnter: () => onActiveChange(item.index), onMouseLeave: () => onActiveChange(null) }, item.index))) }));
134
256
  }
135
257
  /** Whether a resolved list has at least one note worth rendering a rail for. */
136
258
  export function hasRailAnnotations(items) {
@@ -1 +1 @@
1
- {"version":3,"file":"annotation-rail.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/annotation-rail.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../../utils.js\";\nimport type { BlockRenderContext } from \"../types.js\";\n\n/**\n * Shared line-anchored annotation UI for the `annotated-code` and `diff` blocks.\n *\n * Both blocks render a numbered code surface plus a side \"rail\" of notes, where\n * each note targets a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) and hovering a code\n * line ↔ its note cross-highlights. This module owns the pure pieces that were\n * identical between them so neither block forks the behavior:\n *\n * - `parseLineRange` — the forgiving 1-based `lines` range parser.\n * - `resolveAnnotations` / `buildLineMarkerMap` — turn a raw annotation list\n * into stable, marker-numbered, range-resolved records and a line→markers map.\n * - `rangeLabel` — the human \"Line 8\" / \"Lines 3–6\" label.\n * - `AnnotationGutterMarker` — the numbered amber pip placed on an annotated row\n * (used by the diff grid; the annotated-code surface uses its own rail bar).\n * - `AnnotationNoteRail` — the responsive list of note cards with two-way hover.\n * `showMarker` opts the diff block into a leading numbered pip on each card so\n * a note can be matched to its `①`/`②` row marker; annotated-code omits it to\n * keep its original card chrome.\n *\n * `AnnotatedCodeBlock` annotates a single code surface; `DiffBlock` annotates a\n * before/after grid (each annotation also carries a `side`). The shared types\n * here are intentionally minimal — callers pass their own `side` handling and\n * decide which rows a marker lands on; this module only owns the parsing, the\n * resolved-record shape, and the rendered marker + rail chrome.\n */\n\n/* ── Line-ref parsing ──────────────────────────────────────────────────────── */\n\n/**\n * Parse a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) into an inclusive `[start,end]`\n * pair, clamped to `[1, lineCount]`. Returns `null` for malformed or fully\n * out-of-range refs so callers can ignore them gracefully. A reversed range\n * (`\"5-3\"`) is normalized; a partially out-of-range range is clamped.\n */\nexport function parseLineRange(\n ref: string,\n lineCount: number,\n): { start: number; end: number } | null {\n const match = /^\\s*(\\d+)\\s*(?:-\\s*(\\d+)\\s*)?$/.exec(ref);\n if (!match) return null;\n let start = Number.parseInt(match[1], 10);\n let end = match[2] != null ? Number.parseInt(match[2], 10) : start;\n if (!Number.isFinite(start) || !Number.isFinite(end)) return null;\n if (start > end) [start, end] = [end, start];\n // Fully outside the file → ignore.\n if (end < 1 || start > lineCount) return null;\n return { start: Math.max(1, start), end: Math.min(lineCount, end) };\n}\n\n/** The minimal annotation shape the rail needs (a superset works too). */\nexport interface RailAnnotation {\n lines: string;\n label?: string;\n note: string;\n}\n\nexport interface ResolvedAnnotation<A extends RailAnnotation = RailAnnotation> {\n /** Index in the original `annotations` array (stable hover key). */\n index: number;\n /** 1-based marker number (authoring order). */\n marker: number;\n annotation: A;\n range: { start: number; end: number } | null;\n}\n\n/**\n * Resolve a raw annotation list into stable, marker-numbered records, parsing\n * each `lines` ref against `lineCount`. `lineCountFor` lets the diff block pick a\n * per-annotation line count (before-side vs after-side); annotated-code passes a\n * single constant. Markers are authoring-order, 1-based, and assigned to ALL\n * annotations (even unresolved ones) so numbering is stable regardless of which\n * refs happen to match.\n */\nexport function resolveAnnotations<A extends RailAnnotation>(\n annotations: A[] | undefined,\n lineCountFor: (annotation: A) => number,\n): ResolvedAnnotation<A>[] {\n return (annotations ?? []).map((annotation, index) => ({\n index,\n marker: index + 1,\n annotation,\n range: parseLineRange(annotation.lines, lineCountFor(annotation)),\n }));\n}\n\n/** Map a 1-based line number → the resolved annotations covering it. */\nexport function buildLineMarkerMap<A extends RailAnnotation>(\n resolved: ResolvedAnnotation<A>[],\n): Map<number, ResolvedAnnotation<A>[]> {\n const map = new Map<number, ResolvedAnnotation<A>[]>();\n for (const item of resolved) {\n if (!item.range) continue;\n for (let n = item.range.start; n <= item.range.end; n += 1) {\n const list = map.get(n) ?? [];\n list.push(item);\n map.set(n, list);\n }\n }\n return map;\n}\n\n/** Human label for a resolved annotation's line span (\"Line 8\" / \"Lines 3–6\"). */\nexport function rangeLabel(item: ResolvedAnnotation): string {\n if (!item.range) return `Lines ${item.annotation.lines}`;\n return item.range.start === item.range.end\n ? `Line ${item.range.start}`\n : `Lines ${item.range.start}–${item.range.end}`;\n}\n\n/* ── Marker ────────────────────────────────────────────────────────────────── */\n\n/**\n * The numbered amber pip rendered on an annotated code row's gutter. `active`\n * brightens it when its note (or a co-located row) is hovered.\n */\nexport function AnnotationGutterMarker({\n marker,\n active,\n className,\n}: {\n marker: number;\n active: boolean;\n className?: string;\n}) {\n return (\n <span\n aria-hidden\n className={cn(\n \"inline-flex size-[15px] shrink-0 items-center justify-center rounded-full text-[9px] font-semibold leading-none tabular-nums transition-colors\",\n active\n ? \"bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950\"\n : \"bg-amber-400/25 text-amber-700 dark:bg-amber-300/20 dark:text-amber-300\",\n className,\n )}\n >\n {marker}\n </span>\n );\n}\n\n/* ── Note rail ─────────────────────────────────────────────────────────────── */\n\n/**\n * The responsive list of line-anchored note cards. Each card shows its marker\n * pip, the resolved line span (\"Line 8\"), an optional label, and the markdown\n * `note` (via `ctx.renderMarkdown`). Hovering a card sets the active index;\n * `activeIndex` driven from outside lets a hovered code row light its card and\n * vice-versa. Only annotations whose `range` resolved are listed.\n */\nexport function AnnotationNoteRail<A extends RailAnnotation>({\n items,\n activeIndex,\n onActiveChange,\n ctx,\n className,\n showMarker = false,\n}: {\n items: ResolvedAnnotation<A>[];\n activeIndex: number | null;\n onActiveChange: (index: number | null) => void;\n ctx: BlockRenderContext;\n className?: string;\n /** Show a leading numbered pip on each card (diff block). */\n showMarker?: boolean;\n}) {\n const sideAnnotations = useMemo(\n () => items.filter((item) => item.range),\n [items],\n );\n return (\n <div className={cn(\"flex flex-col gap-2.5\", className)}>\n {sideAnnotations.map((item) => {\n const isActive = activeIndex === item.index;\n return (\n <div\n key={item.index}\n onMouseEnter={() => onActiveChange(item.index)}\n onMouseLeave={() => onActiveChange(null)}\n className={cn(\n \"rounded-lg border px-3.5 py-2.5 transition-colors\",\n isActive\n ? \"border-amber-400/70 bg-amber-50 dark:border-amber-300/40 dark:bg-amber-300/[0.08]\"\n : \"border-plan-line bg-plan-block/40 hover:border-amber-400/50\",\n )}\n >\n <div\n className={cn(\n \"flex flex-wrap gap-x-2 gap-y-0.5\",\n showMarker ? \"items-center\" : \"items-baseline\",\n )}\n >\n {showMarker && (\n <AnnotationGutterMarker\n marker={item.marker}\n active={isActive}\n />\n )}\n <span className=\"text-[11px] font-semibold uppercase tracking-wide text-plan-muted\">\n {rangeLabel(item)}\n </span>\n {item.annotation.label && (\n <span className=\"text-[13px] font-semibold text-plan-text\">\n {item.annotation.label}\n </span>\n )}\n </div>\n <div className=\"plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85\">\n {ctx.renderMarkdown ? (\n ctx.renderMarkdown(item.annotation.note)\n ) : (\n <p>{item.annotation.note}</p>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n\n/* ── Inline note (GitHub-style) ─────────────────────────────────────────────── */\n\n/**\n * A single line-anchored note rendered INLINE in the diff flow — full-width,\n * directly under the line it annotates — instead of in a side rail (the\n * GitHub-review affordance). The diff block places one of these after the first\n * row of each annotation's range, so the numbered `①`/`②` row marker and the note\n * read together in one column. It carries the same marker pip, line range,\n * optional label, and markdown `note` as a rail card, and the same two-way hover\n * wiring (`active` / `onActiveChange`) so the row and its note cross-highlight.\n *\n * The outer band is `min-w-full` so the amber wash spans the diff's full scroll\n * width; the content is `sticky left-0` with a readable `max-w` so the note stays\n * pinned and legible while long code lines scroll horizontally beneath it.\n */\nexport function InlineAnnotationNote<A extends RailAnnotation>({\n item,\n active,\n onActiveChange,\n ctx,\n}: {\n item: ResolvedAnnotation<A>;\n active: boolean;\n onActiveChange: (index: number | null) => void;\n ctx: BlockRenderContext;\n}) {\n return (\n <div\n onMouseEnter={() => onActiveChange(item.index)}\n onMouseLeave={() => onActiveChange(null)}\n className={cn(\n \"min-w-full border-y transition-colors\",\n active\n ? \"border-amber-400/60 bg-amber-100/70 dark:border-amber-300/40 dark:bg-amber-300/[0.12]\"\n : \"border-amber-400/30 bg-amber-50/70 dark:border-amber-300/20 dark:bg-amber-300/[0.06]\",\n )}\n >\n <div className=\"sticky left-0 flex max-w-[44rem] gap-2.5 whitespace-normal px-3 py-2.5 font-sans\">\n <AnnotationGutterMarker marker={item.marker} active={active} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-baseline gap-x-2 gap-y-0.5\">\n <span className=\"text-[11px] font-semibold uppercase tracking-wide text-plan-muted\">\n {rangeLabel(item)}\n </span>\n {item.annotation.label && (\n <span className=\"text-[13px] font-semibold text-plan-text\">\n {item.annotation.label}\n </span>\n )}\n </div>\n <div className=\"plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85\">\n {ctx.renderMarkdown ? (\n ctx.renderMarkdown(item.annotation.note)\n ) : (\n <p>{item.annotation.note}</p>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\n/** Whether a resolved list has at least one note worth rendering a rail for. */\nexport function hasRailAnnotations(items: ResolvedAnnotation[]): boolean {\n return items.some((item) => item.range);\n}\n\nexport type AnnotationRailChildren = ReactNode;\n"]}
1
+ {"version":3,"file":"annotation-rail.js","sourceRoot":"","sources":["../../../../src/client/blocks/library/annotation-rail.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../../utils.js\";\nimport type { BlockRenderContext } from \"../types.js\";\n\n/**\n * Shared line-anchored annotation UI for the `annotated-code` and `diff` blocks.\n *\n * Both blocks render a numbered code surface plus a side \"rail\" of notes, where\n * each note targets a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) and hovering a code\n * line ↔ its note cross-highlights. This module owns the pure pieces that were\n * identical between them so neither block forks the behavior:\n *\n * - `parseLineRange` — the forgiving 1-based `lines` range parser.\n * - `resolveAnnotations` / `buildLineMarkerMap` — turn a raw annotation list\n * into stable, marker-numbered, range-resolved records and a line→markers map.\n * - `rangeLabel` — the human \"Line 8\" / \"Lines 3–6\" label.\n * - `AnnotationGutterMarker` — the numbered amber pip placed on an annotated row\n * (used by the diff grid; the annotated-code surface uses its own rail bar).\n * - `AnnotationNoteRail` — the responsive list of note cards with two-way hover.\n * `showMarker` opts the diff block into a leading numbered pip on each card so\n * a note can be matched to its `①`/`②` row marker; annotated-code omits it to\n * keep its original card chrome.\n *\n * `AnnotatedCodeBlock` annotates a single code surface; `DiffBlock` annotates a\n * before/after grid (each annotation also carries a `side`). The shared types\n * here are intentionally minimal — callers pass their own `side` handling and\n * decide which rows a marker lands on; this module only owns the parsing, the\n * resolved-record shape, and the rendered marker + rail chrome.\n */\n\n/* ── Line-ref parsing ──────────────────────────────────────────────────────── */\n\n/**\n * Parse a 1-based `lines` ref (`\"3\"` or `\"3-5\"`) into an inclusive `[start,end]`\n * pair, clamped to `[1, lineCount]`. Returns `null` for malformed or fully\n * out-of-range refs so callers can ignore them gracefully. A reversed range\n * (`\"5-3\"`) is normalized; a partially out-of-range range is clamped.\n */\nexport function parseLineRange(\n ref: string,\n lineCount: number,\n): { start: number; end: number } | null {\n const match = /^\\s*(\\d+)\\s*(?:-\\s*(\\d+)\\s*)?$/.exec(ref);\n if (!match) return null;\n let start = Number.parseInt(match[1], 10);\n let end = match[2] != null ? Number.parseInt(match[2], 10) : start;\n if (!Number.isFinite(start) || !Number.isFinite(end)) return null;\n if (start > end) [start, end] = [end, start];\n // Fully outside the file → ignore.\n if (end < 1 || start > lineCount) return null;\n return { start: Math.max(1, start), end: Math.min(lineCount, end) };\n}\n\n/** The minimal annotation shape the rail needs (a superset works too). */\nexport interface RailAnnotation {\n lines: string;\n label?: string;\n note: string;\n}\n\nexport interface ResolvedAnnotation<A extends RailAnnotation = RailAnnotation> {\n /** Index in the original `annotations` array (stable hover key). */\n index: number;\n /** 1-based marker number (authoring order). */\n marker: number;\n annotation: A;\n range: { start: number; end: number } | null;\n}\n\n/**\n * Resolve a raw annotation list into stable, marker-numbered records, parsing\n * each `lines` ref against `lineCount`. `lineCountFor` lets the diff block pick a\n * per-annotation line count (before-side vs after-side); annotated-code passes a\n * single constant. Markers are authoring-order, 1-based, and assigned to ALL\n * annotations (even unresolved ones) so numbering is stable regardless of which\n * refs happen to match.\n */\nexport function resolveAnnotations<A extends RailAnnotation>(\n annotations: A[] | undefined,\n lineCountFor: (annotation: A) => number,\n): ResolvedAnnotation<A>[] {\n return (annotations ?? []).map((annotation, index) => ({\n index,\n marker: index + 1,\n annotation,\n range: parseLineRange(annotation.lines, lineCountFor(annotation)),\n }));\n}\n\n/** Map a 1-based line number → the resolved annotations covering it. */\nexport function buildLineMarkerMap<A extends RailAnnotation>(\n resolved: ResolvedAnnotation<A>[],\n): Map<number, ResolvedAnnotation<A>[]> {\n const map = new Map<number, ResolvedAnnotation<A>[]>();\n for (const item of resolved) {\n if (!item.range) continue;\n for (let n = item.range.start; n <= item.range.end; n += 1) {\n const list = map.get(n) ?? [];\n list.push(item);\n map.set(n, list);\n }\n }\n return map;\n}\n\n/** Human label for a resolved annotation's line span (\"Line 8\" / \"Lines 3–6\"). */\nexport function rangeLabel(item: ResolvedAnnotation): string {\n if (!item.range) return `Lines ${item.annotation.lines}`;\n return item.range.start === item.range.end\n ? `Line ${item.range.start}`\n : `Lines ${item.range.start}–${item.range.end}`;\n}\n\n/* ── Marker ────────────────────────────────────────────────────────────────── */\n\n/**\n * The numbered amber pip rendered on an annotated code row's gutter. `active`\n * brightens it when its note (or a co-located row) is hovered.\n */\nexport function AnnotationGutterMarker({\n marker,\n active,\n className,\n}: {\n marker: number;\n active: boolean;\n className?: string;\n}) {\n return (\n <span\n aria-hidden\n className={cn(\n \"inline-flex size-[15px] shrink-0 items-center justify-center rounded-full text-[9px] font-semibold leading-none tabular-nums transition-colors\",\n active\n ? \"bg-amber-500 text-white dark:bg-amber-400 dark:text-amber-950\"\n : \"bg-amber-400/25 text-amber-700 dark:bg-amber-300/20 dark:text-amber-300\",\n className,\n )}\n >\n {marker}\n </span>\n );\n}\n\n/* ── Note card ─────────────────────────────────────────────────────────────── */\n\n/**\n * One line-anchored note card: marker pip (when `showMarker`), the resolved line\n * span (\"Line 8\"), an optional label, and the markdown `note` (via\n * `ctx.renderMarkdown`). This is the single source of card markup, rendered both\n * by the visually-hidden a11y/test stack and by the on-hover portal popover.\n */\nexport function AnnotationCard<A extends RailAnnotation>({\n item,\n ctx,\n active = false,\n showMarker = false,\n className,\n onMouseEnter,\n onMouseLeave,\n}: {\n item: ResolvedAnnotation<A>;\n ctx: BlockRenderContext;\n active?: boolean;\n showMarker?: boolean;\n className?: string;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}) {\n return (\n <div\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n className={cn(\n \"rounded-lg border px-3.5 py-2.5 transition-colors\",\n active\n ? \"border-amber-400/70 bg-amber-50 dark:border-amber-300/40 dark:bg-amber-300/[0.08]\"\n : \"border-plan-line bg-plan-block/40 hover:border-amber-400/50\",\n className,\n )}\n >\n <div\n className={cn(\n \"flex flex-wrap gap-x-2 gap-y-0.5\",\n showMarker ? \"items-center\" : \"items-baseline\",\n )}\n >\n {showMarker && (\n <AnnotationGutterMarker marker={item.marker} active={active} />\n )}\n <span className=\"text-[11px] font-semibold uppercase tracking-wide text-plan-muted\">\n {rangeLabel(item)}\n </span>\n {item.annotation.label && (\n <span className=\"text-[13px] font-semibold text-plan-text\">\n {item.annotation.label}\n </span>\n )}\n </div>\n <div className=\"plan-annotation-note mt-1 text-[13px] leading-relaxed text-plan-text/85\">\n {ctx.renderMarkdown ? (\n ctx.renderMarkdown(item.annotation.note)\n ) : (\n <p>{item.annotation.note}</p>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Visually-hidden stack of every resolved note. It is NOT a visible column — it\n * sits in the flow but clipped to a 1px box (the `sr-only` pattern) so the note\n * text and the per-card marker pips stay in the accessibility tree and in the\n * DOM (assistive tech can reach them, and tests that read `textContent`/count\n * pips still see them) WITHOUT painting a persistent rail beside the code. The\n * visible card appears only on hover via {@link AnnotationHoverCard}.\n */\nexport function AnnotationHiddenStack<A extends RailAnnotation>({\n items,\n ctx,\n showMarker = false,\n}: {\n items: ResolvedAnnotation<A>[];\n ctx: BlockRenderContext;\n showMarker?: boolean;\n}) {\n const resolved = useMemo(() => items.filter((item) => item.range), [items]);\n if (resolved.length === 0) return null;\n return (\n <div\n className=\"absolute size-px overflow-hidden whitespace-nowrap border-0 p-0 [clip:rect(0,0,0,0)] [clip-path:inset(50%)]\"\n data-annotation-hidden-stack\n >\n {resolved.map((item) => (\n <AnnotationCard\n key={item.index}\n item={item}\n ctx={ctx}\n showMarker={showMarker}\n />\n ))}\n </div>\n );\n}\n\n/* ── Hover popover (portal, anchored RIGHT of the code) ────────────────────── */\n\n/** The geometry the hover card anchors to (in viewport coordinates). */\nexport interface AnnotationAnchor {\n /** Right edge of the code block (where the card should start). */\n codeRight: number;\n /** Left edge of the code block (for the below-fallback alignment). */\n codeLeft: number;\n /** Vertical center of the hovered line. */\n lineCenter: number;\n /** Bottom of the hovered line (for the below-fallback placement). */\n lineBottom: number;\n}\n\nconst HOVER_CARD_WIDTH = 280;\nconst HOVER_CARD_GAP = 12;\nconst VIEWPORT_MARGIN = 8;\n\n/**\n * The single on-hover note card, portaled to `document.body` and positioned\n * `fixed` so it escapes the code block's `overflow` and never reflows the code.\n *\n * Placement: by default it sits to the RIGHT of the code block's right edge,\n * vertically centered on the hovered line — so it never overlaps the code text.\n * If there isn't room to the right (it would overflow the viewport), it clamps\n * within the viewport, and if the right gutter is too narrow for the card it\n * falls back to BELOW the hovered line (left-aligned to the code block). The card\n * keeps itself open while hovered (`onMouseEnter`/`onMouseLeave` forwarded) so it\n * stays readable; the caller adds the small hover-intent close delay.\n */\nexport function AnnotationHoverCard<A extends RailAnnotation>({\n item,\n anchor,\n ctx,\n showMarker = false,\n onMouseEnter,\n onMouseLeave,\n}: {\n item: ResolvedAnnotation<A>;\n anchor: AnnotationAnchor;\n ctx: BlockRenderContext;\n showMarker?: boolean;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}) {\n const cardRef = useRef<HTMLDivElement | null>(null);\n const [pos, setPos] = useState<{ top: number; left: number } | null>(null);\n\n // Measure the rendered card, then resolve a non-overlapping position: right of\n // the code if it fits, else clamp into the viewport, else drop below the line.\n useLayoutEffect(() => {\n if (typeof window === \"undefined\") return;\n const el = cardRef.current;\n const rect = el?.getBoundingClientRect();\n const width = rect && rect.width > 0 ? rect.width : HOVER_CARD_WIDTH;\n const height = rect && rect.height > 0 ? rect.height : 0;\n const vw = window.innerWidth || 0;\n const vh = window.innerHeight || 0;\n\n const rightLeft = anchor.codeRight + HOVER_CARD_GAP;\n const fitsRight = rightLeft + width + VIEWPORT_MARGIN <= vw;\n\n let left: number;\n let top: number;\n if (fitsRight) {\n // Default: to the right of the code, centered on the hovered line.\n left = rightLeft;\n top = anchor.lineCenter - height / 2;\n } else {\n // No room to the right → drop below the line, aligned to the code's left.\n left = anchor.codeLeft;\n top = anchor.lineBottom + HOVER_CARD_GAP;\n }\n // Clamp within the viewport so the card is never cut off.\n left = Math.max(\n VIEWPORT_MARGIN,\n Math.min(left, vw - width - VIEWPORT_MARGIN),\n );\n top = Math.max(\n VIEWPORT_MARGIN,\n Math.min(top, vh - height - VIEWPORT_MARGIN),\n );\n setPos({ top, left });\n }, [\n anchor.codeRight,\n anchor.codeLeft,\n anchor.lineCenter,\n anchor.lineBottom,\n item.index,\n ]);\n\n if (typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={cardRef}\n role=\"tooltip\"\n data-annotation-hover-card\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n className=\"pointer-events-auto fixed z-50\"\n style={{\n top: pos?.top ?? anchor.lineCenter,\n left: pos?.left ?? anchor.codeRight + HOVER_CARD_GAP,\n width: HOVER_CARD_WIDTH,\n // Hide until measured to avoid a one-frame jump from the fallback spot.\n visibility: pos ? \"visible\" : \"hidden\",\n }}\n >\n <AnnotationCard\n item={item}\n ctx={ctx}\n active\n showMarker={showMarker}\n className=\"shadow-lg shadow-black/10 dark:shadow-black/40\"\n />\n </div>,\n document.body,\n );\n}\n\n/**\n * Hover-intent controller for the on-hover note card. Exposes `activeIndex` +\n * the captured `anchor`, plus `open`/`scheduleClose`/`cancelClose` handlers.\n *\n * - `open(index, anchor)` shows a card immediately (cancels any pending close).\n * - `scheduleClose()` hides after a short delay, so moving the pointer from the\n * code line across the gap into the card itself keeps it open.\n * - `cancelClose()` (call on card mouse-enter) keeps it open while reading.\n */\nexport function useAnnotationHover(delay = 130) {\n const [active, setActive] = useState<{\n index: number;\n anchor: AnnotationAnchor;\n } | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const cancelClose = () => {\n if (timer.current) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n };\n const open = (index: number, anchor: AnnotationAnchor) => {\n cancelClose();\n setActive({ index, anchor });\n };\n const scheduleClose = () => {\n cancelClose();\n timer.current = setTimeout(() => setActive(null), delay);\n };\n\n useEffect(() => () => cancelClose(), []);\n\n return {\n activeIndex: active?.index ?? null,\n anchor: active?.anchor ?? null,\n open,\n scheduleClose,\n cancelClose,\n } as const;\n}\n\n/**\n * Build an {@link AnnotationAnchor} from the code block element and the hovered\n * row element. The card anchors to the code block's RIGHT edge and the row's\n * vertical center, both in viewport coordinates (so a `fixed` portal lines up).\n */\nexport function anchorFromElements(\n codeEl: HTMLElement | null,\n rowEl: HTMLElement | null,\n): AnnotationAnchor | null {\n if (!codeEl || !rowEl) return null;\n const code = codeEl.getBoundingClientRect();\n const row = rowEl.getBoundingClientRect();\n return {\n codeRight: code.right,\n codeLeft: code.left,\n lineCenter: row.top + row.height / 2,\n lineBottom: row.bottom,\n };\n}\n\n/**\n * The responsive list of line-anchored note cards. Each card shows its marker\n * pip, the resolved line span (\"Line 8\"), an optional label, and the markdown\n * `note` (via `ctx.renderMarkdown`). Hovering a card sets the active index;\n * `activeIndex` driven from outside lets a hovered code row light its card and\n * vice-versa. Only annotations whose `range` resolved are listed.\n *\n * @deprecated Superseded by the on-hover {@link AnnotationHoverCard}; kept for\n * back-compat with any external importer. Both block read renderers now use the\n * hover popover anchored to the right of the code instead of a persistent rail.\n */\nexport function AnnotationNoteRail<A extends RailAnnotation>({\n items,\n activeIndex,\n onActiveChange,\n ctx,\n className,\n showMarker = false,\n}: {\n items: ResolvedAnnotation<A>[];\n activeIndex: number | null;\n onActiveChange: (index: number | null) => void;\n ctx: BlockRenderContext;\n className?: string;\n /** Show a leading numbered pip on each card (diff block). */\n showMarker?: boolean;\n}) {\n const sideAnnotations = useMemo(\n () => items.filter((item) => item.range),\n [items],\n );\n return (\n <div className={cn(\"flex flex-col gap-2.5\", className)}>\n {sideAnnotations.map((item) => (\n <AnnotationCard\n key={item.index}\n item={item}\n ctx={ctx}\n active={activeIndex === item.index}\n showMarker={showMarker}\n onMouseEnter={() => onActiveChange(item.index)}\n onMouseLeave={() => onActiveChange(null)}\n />\n ))}\n </div>\n );\n}\n\n/** Whether a resolved list has at least one note worth rendering a rail for. */\nexport function hasRailAnnotations(items: ResolvedAnnotation[]): boolean {\n return items.some((item) => item.range);\n}\n\nexport type AnnotationRailChildren = ReactNode;\n"]}
@@ -190,7 +190,7 @@ function HighlightedCodeTextarea({ value, language, label, editable, onChange, }
190
190
  layer.scrollTop = event.currentTarget.scrollTop;
191
191
  layer.scrollLeft = event.currentTarget.scrollLeft;
192
192
  };
193
- return (_jsxs("div", { className: cn("relative min-h-[140px] overflow-hidden rounded-md border border-input bg-background text-foreground focus-within:ring-1 focus-within:ring-ring", !editable && "cursor-not-allowed opacity-50"), "data-code-tabs-highlighted-editor": true, children: [_jsx("pre", { ref: highlightLayerRef, "aria-hidden": "true", className: "pointer-events-none absolute inset-0 m-0 overflow-hidden whitespace-pre px-3 py-2 font-mono text-xs leading-5", "data-code-tabs-highlight-layer": true, children: _jsxs("code", { children: [highlighted, value.endsWith("\n") ? " " : null] }) }), _jsx("textarea", { "data-plan-interactive": true, spellCheck: false, wrap: "off", className: cn("relative z-10 block min-h-[140px] w-full resize-y overflow-auto rounded-md border-0 bg-transparent px-3 py-2 font-mono text-xs leading-5 caret-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed", value ? "text-transparent" : "text-muted-foreground"), value: value, disabled: !editable, onChange: onChange, onScroll: syncScroll })] }));
193
+ return (_jsxs("div", { className: cn("relative min-h-[140px] overflow-hidden rounded-md border border-input bg-background text-foreground focus-within:ring-1 focus-within:ring-ring", !editable && "cursor-not-allowed opacity-50"), "data-code-tabs-highlighted-editor": true, children: [_jsx("pre", { ref: highlightLayerRef, "aria-hidden": "true", className: "pointer-events-none absolute inset-0 m-0 overflow-hidden whitespace-pre px-3 py-2 font-mono [font-size:var(--plan-code-size)] leading-5", "data-code-tabs-highlight-layer": true, children: _jsxs("code", { children: [highlighted, value.endsWith("\n") ? " " : null] }) }), _jsx("textarea", { "data-plan-interactive": true, spellCheck: false, wrap: "off", className: cn("relative z-10 block min-h-[140px] w-full resize-y overflow-auto rounded-md border-0 bg-transparent px-3 py-2 font-mono [font-size:var(--plan-code-size)] leading-5 caret-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed", value ? "text-transparent" : "text-muted-foreground"), value: value, disabled: !editable, onChange: onChange, onScroll: syncScroll })] }));
194
194
  }
195
195
  /**
196
196
  * Editor: a file-tab strip (one tab active at a time) with the active tab's code