@plannotator/ui 0.28.0 → 0.29.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 (105) hide show
  1. package/README.md +6 -0
  2. package/components/AISettingsTab.tsx +5 -4
  3. package/components/ActionMenu.tsx +5 -1
  4. package/components/AgentsTab.tsx +10 -23
  5. package/components/AnnotationPanel.tsx +9 -5
  6. package/components/AnnotationToolbar.tsx +5 -13
  7. package/components/AnnotationToolstrip.tsx +2 -2
  8. package/components/ApproveDropdown.tsx +1 -1
  9. package/components/BlockRenderer.tsx +1 -1
  10. package/components/CodeFilePopout.tsx +5 -4
  11. package/components/CommentPopover.tsx +391 -65
  12. package/components/DocBadges.tsx +29 -11
  13. package/components/ExportModal.tsx +16 -8
  14. package/components/GraphvizBlock.tsx +1 -1
  15. package/components/InlineMarkdown.tsx +30 -13
  16. package/components/KeyboardShortcuts.tsx +37 -2
  17. package/components/Landing.tsx +7 -7
  18. package/components/MenuVersionSection.tsx +4 -4
  19. package/components/MermaidBlock.tsx +1 -1
  20. package/components/ModeToggle.tsx +7 -6
  21. package/components/OpenInAppButton.tsx +2 -5
  22. package/components/PinpointOverlay.tsx +9 -7
  23. package/components/PlanHeaderMenu.tsx +8 -8
  24. package/components/PopoutDialog.tsx +6 -1
  25. package/components/ResizeHandle.tsx +1 -0
  26. package/components/Settings.tsx +172 -12
  27. package/components/SkillReferenceMenu.tsx +260 -0
  28. package/components/StickyHeaderLane.tsx +7 -0
  29. package/components/ThemeProvider.tsx +131 -32
  30. package/components/ThemeTab.tsx +123 -77
  31. package/components/ToolbarButtons.tsx +29 -8
  32. package/components/Viewer.tsx +396 -130
  33. package/components/VimKeyHud.tsx +695 -0
  34. package/components/VimModeAnnouncementDialog.tsx +557 -0
  35. package/components/VimModeOverlay.tsx +235 -0
  36. package/components/VimTargetReticle.tsx +284 -0
  37. package/components/ai/DocumentAIChatPanel.tsx +1 -1
  38. package/components/blocks/CodeBlock.tsx +18 -18
  39. package/components/blocks/TablePopout.tsx +7 -8
  40. package/components/blocks/TableToolbar.tsx +7 -8
  41. package/components/goal-setup/GoalSetupSurface.tsx +16 -3
  42. package/components/html-viewer/HtmlViewer.tsx +450 -47
  43. package/components/html-viewer/annotationNumbering.ts +37 -0
  44. package/components/html-viewer/bridge-script.ts +4051 -298
  45. package/components/html-viewer/composerYield.ts +51 -0
  46. package/components/html-viewer/srcdoc.ts +18 -3
  47. package/components/html-viewer/useHtmlAnnotation.ts +457 -32
  48. package/components/icons/themeIcons.tsx +1 -1
  49. package/components/plan-diff/PlanCleanDiffView.tsx +9 -9
  50. package/components/plan-diff/PlanDiffBadge.tsx +22 -1
  51. package/components/settings/HooksTab.tsx +12 -8
  52. package/components/sidebar/FileBrowser.tsx +4 -1
  53. package/components/themeModes.tsx +28 -0
  54. package/config/configStore.ts +76 -1
  55. package/config/settings.ts +152 -0
  56. package/configure.ts +9 -0
  57. package/globals.d.ts +7 -1
  58. package/hooks/useAIChat.ts +5 -2
  59. package/hooks/useAIProviderActivation.ts +47 -0
  60. package/hooks/useAIProviderConfig.ts +5 -1
  61. package/hooks/useAgentSettings.ts +64 -23
  62. package/hooks/useAgents.ts +4 -4
  63. package/hooks/useAnnotationHighlighter.ts +100 -3
  64. package/hooks/useArchive.ts +2 -1
  65. package/hooks/useFenceTheme.ts +17 -0
  66. package/hooks/useLinkedDoc.ts +68 -1
  67. package/hooks/usePinpoint.ts +76 -75
  68. package/hooks/usePlanDiff.ts +73 -2
  69. package/hooks/useSkillReferenceAutocomplete.ts +239 -0
  70. package/hooks/useUpdateCheck.ts +1 -2
  71. package/hooks/useVimDocumentFocus.ts +116 -0
  72. package/hooks/useVimSelection.ts +1063 -0
  73. package/package.json +4 -4
  74. package/print.css +14 -13
  75. package/shortcuts/core.ts +38 -13
  76. package/shortcuts/index.ts +10 -0
  77. package/shortcuts/plan-review/commentPopover.shortcuts.ts +7 -0
  78. package/shortcuts/plan-review/vimSelection.shortcuts.ts +251 -0
  79. package/shortcuts/runtime.ts +111 -12
  80. package/styles.css +1 -1
  81. package/theme.css +504 -0
  82. package/themes/colorblind.css +89 -0
  83. package/themes/plannotator.css +2 -2
  84. package/types.ts +93 -10
  85. package/utils/agentSwitch.ts +33 -7
  86. package/utils/blockTargeting.ts +462 -178
  87. package/utils/clipboard.ts +110 -0
  88. package/utils/codeBlockMark.ts +50 -0
  89. package/utils/codeHighlight.ts +293 -0
  90. package/utils/codexModels.ts +79 -0
  91. package/utils/domSelection.ts +84 -0
  92. package/utils/htmlChrome.ts +73 -0
  93. package/utils/inputMethod.ts +79 -6
  94. package/utils/parser.ts +517 -21
  95. package/utils/preferenceTtl.ts +15 -0
  96. package/utils/sharing.ts +0 -1
  97. package/utils/skillCatalog.ts +269 -0
  98. package/utils/skillReferences.ts +475 -0
  99. package/utils/syntaxTheme.ts +83 -0
  100. package/utils/themeRegistry.ts +154 -0
  101. package/utils/vimHud.ts +263 -0
  102. package/utils/vimModeAnnouncement.ts +23 -0
  103. package/utils/vimNavigation.ts +417 -0
  104. package/utils/vimReticle.ts +88 -0
  105. package/utils/vimScroll.ts +162 -0
package/types.ts CHANGED
@@ -25,6 +25,38 @@ export interface ImageAttachment {
25
25
  name: string;
26
26
  }
27
27
 
28
+ /** DOM-relative text position used to restore a document annotation selection. */
29
+ export interface AnnotationTextMeta {
30
+ parentTagName: string;
31
+ parentIndex: number;
32
+ textOffset: number;
33
+ }
34
+
35
+ /** Durable, media-specific location for a note created in the PR artifact viewer. */
36
+ export type ArtifactAnnotationAnchor =
37
+ | {
38
+ kind: 'document';
39
+ originalText: string;
40
+ blockId: string;
41
+ startOffset: number;
42
+ endOffset: number;
43
+ startMeta?: AnnotationTextMeta;
44
+ endMeta?: AnnotationTextMeta;
45
+ }
46
+ | { kind: 'image'; x: number; y: number }
47
+ | { kind: 'video'; timestamp: number }
48
+ | { kind: 'page' };
49
+
50
+ /** Context shared by description and comment annotations created on a PR artifact. */
51
+ export interface ArtifactAnnotationMeta {
52
+ artifactId: string;
53
+ artifactName: string;
54
+ artifactUrl: string;
55
+ artifactKind: 'image' | 'gif' | 'video' | 'html' | 'markdown';
56
+ sourceUrl: string;
57
+ anchor: ArtifactAnnotationAnchor;
58
+ }
59
+
28
60
  export interface Annotation {
29
61
  id: string;
30
62
  blockId: string; // Legacy - not used with web-highlighter
@@ -40,23 +72,56 @@ export interface Annotation {
40
72
  isQuickLabel?: boolean; // true if created via quick label chip
41
73
  quickLabelTip?: string; // optional instruction tip from the label definition
42
74
  diffContext?: 'added' | 'removed' | 'modified'; // set when annotation created in plan diff view
75
+ artifact?: ArtifactAnnotationMeta; // code-review artifact viewer anchor + source context
43
76
  mathTargets?: Array<{
44
77
  blockId: string;
45
78
  tex: string;
46
79
  displayMode: boolean;
47
80
  }>; // math elements covered by a mixed text+formula selection
48
81
  prUrl?: string; // code-review PR mode: the PR this note belongs to, so it isn't shown/exported against another PR after an in-place switch
82
+ htmlAnchor?: HtmlElementAnchor; // raw-HTML pinpoint: serialized element anchor for reliable restoration
83
+ htmlAdditionalTargets?: HtmlAnnotationTarget[]; // raw-HTML shift-click multi-select: extra elements this one comment covers (primary stays htmlAnchor/originalText)
49
84
  // web-highlighter metadata for cross-element selections
50
- startMeta?: {
51
- parentTagName: string;
52
- parentIndex: number;
53
- textOffset: number;
54
- };
55
- endMeta?: {
56
- parentTagName: string;
57
- parentIndex: number;
58
- textOffset: number;
59
- };
85
+ startMeta?: AnnotationTextMeta;
86
+ endMeta?: AnnotationTextMeta;
87
+ }
88
+
89
+ /**
90
+ * Serialized element anchor for annotations created on the raw-HTML surface
91
+ * (pinpoint mode). Built inside the sandboxed viewer bridge: a verified-unique
92
+ * CSS selector plus a fingerprint (tag + normalized text snapshot) used to
93
+ * fail closed when a weak selector no longer matches the same content.
94
+ * Additive — annotations without one restore via document-wide text search.
95
+ */
96
+ export interface HtmlElementAnchor {
97
+ selector: string;
98
+ tagName: string;
99
+ text?: string;
100
+ /**
101
+ * The user's selected point inside the target element's rect, normalized to
102
+ * 0..1 on each axis. Placed comment markers reproject it against the
103
+ * element's CURRENT rect, so the marker follows the element through
104
+ * responsive movement instead of pinning stale pixels. Additive — anchors
105
+ * without one (older records, keyboard-driven selections) fall back to the
106
+ * target rect center.
107
+ */
108
+ point?: { x: number; y: number };
109
+ }
110
+
111
+ /**
112
+ * One additional element covered by a multi-target raw-HTML pinpoint comment
113
+ * (shift-click multi-select). Carries what the export and composer chips need
114
+ * even when anchoring failed closed: the semantic label from the pinpoint
115
+ * hover cascade and the element's text (or `[element: …]` description).
116
+ * Additive — annotations without the array behave exactly as before.
117
+ */
118
+ export interface HtmlAnnotationTarget {
119
+ /** Semantic label from the hover-label cascade (e.g. "Button", "rowchip"). */
120
+ label?: string;
121
+ /** The target element's capped text, or an element description when text-less. */
122
+ text: string;
123
+ /** Element anchor for restoration; absent when the bridge failed closed. */
124
+ anchor?: HtmlElementAnchor;
60
125
  }
61
126
 
62
127
  export type AlertKind = 'note' | 'tip' | 'warning' | 'caution' | 'important';
@@ -122,6 +187,7 @@ export interface CommentAnnotation {
122
187
  text: string; // the reviewer's note
123
188
  createdAt: number;
124
189
  prUrl?: string; // the PR this note belongs to (see Annotation.prUrl)
190
+ artifact?: ArtifactAnnotationMeta; // optional artifact anchor within this source comment
125
191
  }
126
192
 
127
193
  export interface CodeAnnotation {
@@ -139,6 +205,15 @@ export interface CodeAnnotation {
139
205
  charStart?: number; // Character offset within lineStart (token-level selection)
140
206
  charEnd?: number; // Character offset within lineEnd (token-level selection)
141
207
  tokenText?: string; // Selected token/span text (token-level selection)
208
+ /** Exact text highlighted when the comment was created inside an edit
209
+ * session (captured from the editor selection). Exported alongside the
210
+ * comment so the agent sees what was highlighted even when it differs
211
+ * from the anchored diff lines. */
212
+ selectedText?: string;
213
+ /** True when the edit-session selection overlapped in-session edits: the
214
+ * line anchor maps to the pristine lines those edits replace, so it is
215
+ * approximate and the export labels it as such. */
216
+ selectedTextFromEdits?: boolean;
142
217
  createdAt: number;
143
218
  author?: string;
144
219
  source?: string; // External tool identifier (e.g., "eslint") — set when annotation comes from external API
@@ -159,6 +234,14 @@ export interface CodeAnnotation {
159
234
  commitSha?: string;
160
235
  /** The commit's one-line subject, captured for readable export labels. */
161
236
  commitSubject?: string;
237
+ /** GitButler target that supplied this annotation's line coordinates. */
238
+ gitButlerDiffType?: string;
239
+ /** Human-readable GitButler target label captured with the annotation. */
240
+ gitButlerDiffLabel?: string;
241
+ /** GitButler merge base active when the annotation was created. */
242
+ gitButlerBase?: string;
243
+ /** Exact server snapshot that supplied the GitButler line coordinates. */
244
+ gitButlerSnapshotId?: string;
162
245
  }
163
246
 
164
247
  /** Token-level metadata passed from selection to annotation creation. */
@@ -1,8 +1,13 @@
1
1
  /**
2
2
  * Agent Switch Settings Utility
3
3
  *
4
- * Manages settings for automatic agent switching after plan approval.
5
- * Supports built-in agents (build), disabled, or custom agent names.
4
+ * Manages settings for automatic agent switching after plan approval and after
5
+ * sending code review feedback. Supports discovered agents, disabled, or custom
6
+ * agent names.
7
+ *
8
+ * The stored value is shared across surfaces, but the default when nothing is
9
+ * stored is surface-specific: plan approval keeps its historical hand-off to the
10
+ * build agent, while review feedback stays on the current agent.
6
11
  *
7
12
  * Uses cookies (not localStorage) because each hook invocation runs on a
8
13
  * random port, and localStorage is scoped by origin including port.
@@ -28,14 +33,32 @@ export const AGENT_OPTIONS: { value: string; label: string; description: string
28
33
  { value: 'disabled', label: 'Disabled', description: 'Stay on current agent after approval' },
29
34
  ];
30
35
 
31
- const DEFAULT_SETTINGS: AgentSwitchSettings = {
36
+ /** UI surface asking for the setting — only affects the unset default. */
37
+ export type AgentSwitchSurface = 'plan' | 'review';
38
+
39
+ const PLAN_DEFAULT_SETTINGS: AgentSwitchSettings = {
32
40
  switchTo: 'build',
33
41
  };
34
42
 
43
+ const REVIEW_DEFAULT_SETTINGS: AgentSwitchSettings = {
44
+ switchTo: 'disabled',
45
+ };
46
+
47
+ /**
48
+ * Default used when the user has never picked an agent switch setting.
49
+ * Plan approval hands off to the build agent (historical behavior); review
50
+ * feedback stays on the current agent.
51
+ */
52
+ export function getAgentSwitchDefaults(surface: AgentSwitchSurface = 'plan'): AgentSwitchSettings {
53
+ return surface === 'review' ? REVIEW_DEFAULT_SETTINGS : PLAN_DEFAULT_SETTINGS;
54
+ }
55
+
35
56
  /**
36
- * Get current agent switch settings from storage
57
+ * Get current agent switch settings from storage.
58
+ * An explicit user choice applies to every surface; only the unset default
59
+ * varies by surface.
37
60
  */
38
- export function getAgentSwitchSettings(): AgentSwitchSettings {
61
+ export function getAgentSwitchSettings(surface: AgentSwitchSurface = 'plan'): AgentSwitchSettings {
39
62
  const stored = storage.getItem(STORAGE_KEY);
40
63
  const customName = storage.getItem(CUSTOM_NAME_KEY) || undefined;
41
64
 
@@ -43,7 +66,7 @@ export function getAgentSwitchSettings(): AgentSwitchSettings {
43
66
  if (stored) {
44
67
  return { switchTo: stored, customName };
45
68
  }
46
- return DEFAULT_SETTINGS;
69
+ return getAgentSwitchDefaults(surface);
47
70
  }
48
71
 
49
72
  /**
@@ -67,5 +90,8 @@ export function getEffectiveAgentName(settings: AgentSwitchSettings): string | u
67
90
  if (settings.switchTo === 'custom' && settings.customName) {
68
91
  return settings.customName;
69
92
  }
70
- return settings.switchTo; // 'build' or fallback
93
+ if (settings.switchTo === 'custom') {
94
+ return undefined;
95
+ }
96
+ return settings.switchTo;
71
97
  }