@plannotator/ui 0.53.1 → 0.54.0

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 (135) hide show
  1. package/HANDOFF.md +217 -11
  2. package/README.md +65 -10
  3. package/components/AgentToolAnnouncementDialog.tsx +1 -1
  4. package/components/AgentsTab.tsx +1 -1
  5. package/components/AnnotationPanel.tsx +142 -24
  6. package/components/AnnotationSidebar.tsx +1 -1
  7. package/components/AnnotationToolbar.tsx +64 -9
  8. package/components/ApproveDropdown.tsx +7 -9
  9. package/components/AskSessionAnnouncementDialog.tsx +1 -1
  10. package/components/AttachmentsButton.tsx +1 -1
  11. package/components/BlockRenderer.tsx +19 -5
  12. package/components/CodeFilePopout.tsx +139 -4
  13. package/components/CommentPopover.tsx +44 -8
  14. package/components/DecisionControl.tsx +31 -5
  15. package/components/DiagramBlock.tsx +17 -12
  16. package/components/EditorAnnotationCard.tsx +1 -1
  17. package/components/ExportModal.tsx +77 -7
  18. package/components/HostTitle.tsx +0 -4
  19. package/components/ImageAnnotator/Toolbar.tsx +25 -1
  20. package/components/ImageAnnotator/index.tsx +24 -4
  21. package/components/ImageLightbox.tsx +658 -120
  22. package/components/ImagePeek.tsx +38 -4
  23. package/components/ImageRegions.tsx +369 -0
  24. package/components/ImageThumbnail.tsx +1 -1
  25. package/components/ImageZoomStage.tsx +796 -0
  26. package/components/InboxAnnouncementDialog.tsx +202 -0
  27. package/components/InlineMarkdown.tsx +152 -24
  28. package/components/KeyboardShortcuts.tsx +23 -0
  29. package/components/LazyBlockBoundary.tsx +100 -0
  30. package/components/ListMarker.tsx +3 -3
  31. package/components/LookAndFeelAnnouncementDialog.tsx +12 -2
  32. package/components/MenuVersionSection.tsx +31 -9
  33. package/components/PlanDecisionMenuExtras.tsx +129 -0
  34. package/components/PlanHeaderMenu.tsx +15 -1
  35. package/components/ProviderIcons.tsx +8 -0
  36. package/components/RefHoverCard.tsx +913 -0
  37. package/components/SendFailureToast.tsx +126 -0
  38. package/components/Settings.tsx +617 -126
  39. package/components/TerminalToolsAnnouncementDialog.tsx +1 -1
  40. package/components/ThemeProvider.tsx +14 -1
  41. package/components/ThemeTab.tsx +16 -24
  42. package/components/Tooltip.tsx +49 -18
  43. package/components/Viewer.tsx +280 -41
  44. package/components/ai/DocumentAIChatPanel.tsx +29 -3
  45. package/components/ai/NewChatButton.tsx +74 -0
  46. package/components/blocks/CodeBlock.tsx +1 -1
  47. package/components/blocks/HtmlBlock.tsx +7 -1
  48. package/components/blocks/QuestionBlock.tsx +522 -62
  49. package/components/diagram/DiagramComposer.tsx +5 -1
  50. package/components/diagram/DiagramPending.tsx +12 -2
  51. package/components/diagram/DiagramViewer.tsx +19 -3
  52. package/components/html-embed/HtmlEmbedBlock.tsx +374 -0
  53. package/components/html-embed/SvgFenceBlock.tsx +46 -0
  54. package/components/html-embed/index.ts +3 -0
  55. package/components/html-embed/languages.ts +15 -0
  56. package/components/html-embed/loadGate.ts +107 -0
  57. package/components/html-viewer/HtmlViewer.tsx +47 -6
  58. package/components/html-viewer/bridge-script.asset.js +173 -1
  59. package/components/html-viewer/bridge-script.ts +173 -1
  60. package/components/html-viewer/useHtmlAnnotation.ts +125 -0
  61. package/components/imageZoom.ts +265 -0
  62. package/components/plan-diff/PlanCleanDiffView.tsx +1 -1
  63. package/components/plan-diff/PlanDiffViewer.tsx +13 -2
  64. package/components/plan-diff/PlanRawDiffView.tsx +1 -1
  65. package/components/sidebar/FileBrowser.tsx +16 -2
  66. package/components/sidebar/SidebarTabs.tsx +3 -0
  67. package/config/index.ts +3 -0
  68. package/config/reviewView.ts +41 -1
  69. package/config/settings.ts +155 -4
  70. package/configure.ts +28 -0
  71. package/hooks/pfm/useCodeFilePopout.ts +7 -1
  72. package/hooks/useAIChat.ts +72 -25
  73. package/hooks/useAnnotationDraft.ts +71 -6
  74. package/hooks/useAnnotationHighlighter.ts +57 -3
  75. package/hooks/useCodeAnnotationDraft.ts +113 -17
  76. package/hooks/useCookieHostSetting.ts +96 -0
  77. package/hooks/useDocumentFont.ts +17 -0
  78. package/hooks/useDocumentTextSize.ts +20 -0
  79. package/hooks/useFileBrowser.ts +10 -1
  80. package/hooks/useLinkedDoc.ts +226 -13
  81. package/hooks/usePackageChangesSetting.ts +90 -0
  82. package/hooks/usePlanLookDialog.ts +67 -0
  83. package/hooks/useSharing.ts +11 -7
  84. package/hooks/useThemePaletteHalves.ts +37 -0
  85. package/hooks/useUpdateCheck.ts +22 -4
  86. package/hooks/useUpdateToast.ts +1 -1
  87. package/package.json +23 -22
  88. package/shortcuts/code-review/annotationToolbar.shortcuts.ts +1 -0
  89. package/shortcuts/code-review/explorer.shortcuts.ts +82 -0
  90. package/shortcuts/core.ts +63 -4
  91. package/shortcuts/index.ts +1 -0
  92. package/shortcuts/plan-review/annotationPanel.shortcuts.ts +1 -1
  93. package/shortcuts/plan-review/commentPopover.shortcuts.ts +1 -0
  94. package/shortcuts/runtime.ts +4 -3
  95. package/styles.css +1 -1
  96. package/theme.css +179 -1
  97. package/themes/plannotator-graphite.css +66 -0
  98. package/types.ts +26 -9
  99. package/utils/agentSwitch.ts +41 -20
  100. package/utils/agentToolSetting.ts +1 -1
  101. package/utils/aiChatFormat.ts +23 -7
  102. package/utils/annotationOwnsHighlight.ts +4 -2
  103. package/utils/asciidocParser.ts +570 -0
  104. package/utils/autoUpdateNotice.ts +24 -0
  105. package/utils/belowTitleSlot.ts +174 -0
  106. package/utils/codeLineScroll.ts +100 -0
  107. package/utils/commentSubmitKey.ts +48 -0
  108. package/utils/cssFontFamily.ts +65 -0
  109. package/utils/decisionSpec.ts +190 -2
  110. package/utils/defaultNotesApp.ts +1 -1
  111. package/utils/diffFonts.ts +4 -1
  112. package/utils/documentFont.ts +68 -0
  113. package/utils/documentTextSize.ts +54 -0
  114. package/utils/fontDetect.ts +63 -0
  115. package/utils/inboxAnnouncement.ts +78 -0
  116. package/utils/lookAndFeelAnnouncement.ts +82 -5
  117. package/utils/markdownExtensions.ts +2 -2
  118. package/utils/mermaid.ts +20 -3
  119. package/utils/mermaidTheme.ts +3 -51
  120. package/utils/notion.ts +58 -0
  121. package/utils/parser.ts +28 -1
  122. package/utils/platform.ts +46 -0
  123. package/utils/refCard.ts +82 -0
  124. package/utils/richClipboard.ts +380 -0
  125. package/utils/richClipboardLists.ts +57 -0
  126. package/utils/richCopyPolicy.ts +22 -0
  127. package/utils/scrollAnchor.ts +82 -0
  128. package/utils/sectionRefs.ts +123 -0
  129. package/utils/selectionBoundary.ts +40 -0
  130. package/utils/sendFailure.ts +85 -0
  131. package/utils/sharing.ts +5 -2
  132. package/utils/themeRegistry.ts +10 -0
  133. package/utils/themeTokens.ts +74 -0
  134. package/utils/updateNotice.ts +30 -8
  135. package/utils/vscodePanel.ts +9 -0
@@ -0,0 +1,74 @@
1
+ import React, { useId } from 'react';
2
+
3
+ interface NewChatButtonProps {
4
+ onNewChat: () => void;
5
+ /** A question is in flight: a new chat would cut it off, so it waits. */
6
+ busy?: boolean;
7
+ /**
8
+ * A busy "Ask this session" answer still offers "Ask when it finishes" /
9
+ * "Interrupt and ask now": clearing would drop that question unasked.
10
+ */
11
+ pendingChoice?: boolean;
12
+ /**
13
+ * "Ask this session": the agent session that opened Plannotator answers, and
14
+ * it keeps its own memory, so the button only clears the panel. The label
15
+ * says so instead of promising fresh context.
16
+ */
17
+ sessionBridge?: boolean;
18
+ }
19
+
20
+ export function newChatLabels(sessionBridge: boolean): { label: string; title: string } {
21
+ return sessionBridge
22
+ ? {
23
+ label: 'Clear chat',
24
+ title: 'Clear this panel. The session that opened Plannotator still remembers earlier questions.',
25
+ }
26
+ : {
27
+ label: 'New chat',
28
+ title: 'Clear this chat and start a fresh AI session',
29
+ };
30
+ }
31
+
32
+ /** Why the button is unavailable right now; null when it can be used. */
33
+ export function newChatBlockedReason(options: { busy?: boolean; pendingChoice?: boolean }): string | null {
34
+ if (options.busy) return 'Wait for the answer, or stop it, before starting a new chat.';
35
+ if (options.pendingChoice) return 'First choose "Ask when it finishes" or "Interrupt and ask now" for the waiting question.';
36
+ return null;
37
+ }
38
+
39
+ /**
40
+ * The Ask AI panel's "New chat" row (#1981). While unavailable it stays
41
+ * focusable (`aria-disabled`, clicks ignored) and its reason is its
42
+ * description, so keyboard and screen-reader users learn why.
43
+ */
44
+ export const NewChatButton: React.FC<NewChatButtonProps> = ({
45
+ onNewChat,
46
+ busy = false,
47
+ pendingChoice = false,
48
+ sessionBridge = false,
49
+ }) => {
50
+ const { label, title } = newChatLabels(sessionBridge);
51
+ const blocked = newChatBlockedReason({ busy, pendingChoice });
52
+ const descriptionId = useId();
53
+ return (
54
+ <div className="flex items-center justify-end px-2 pt-1.5 pb-0.5" data-ai-new-chat-row>
55
+ <button
56
+ type="button"
57
+ onClick={() => { if (!blocked) onNewChat(); }}
58
+ aria-disabled={blocked ? true : undefined}
59
+ aria-describedby={descriptionId}
60
+ title={blocked ?? title}
61
+ data-ai-new-chat
62
+ className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md text-[11px] text-muted-foreground hover:text-foreground hover:bg-muted aria-disabled:opacity-40 aria-disabled:cursor-not-allowed aria-disabled:hover:bg-transparent aria-disabled:hover:text-muted-foreground transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50"
63
+ >
64
+ <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} aria-hidden="true">
65
+ <path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
66
+ </svg>
67
+ {label}
68
+ </button>
69
+ <span id={descriptionId} className="sr-only" data-ai-new-chat-description>
70
+ {blocked ?? title}
71
+ </span>
72
+ </div>
73
+ );
74
+ };
@@ -54,7 +54,7 @@ export const CodeBlock: React.FC<CodeBlockProps> = ({ block, onHover, onLeave })
54
54
  >
55
55
  <button
56
56
  onClick={handleCopy}
57
- className="absolute top-2 right-2 p-1.5 rounded-md bg-muted/80 hover:bg-muted text-muted-foreground hover:text-foreground opacity-0 group-hover:opacity-100 transition-opacity z-10"
57
+ className="absolute top-2 right-2 p-1.5 rounded-md bg-muted/80 hover:bg-muted text-muted-foreground hover:text-foreground opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 transition-opacity z-10"
58
58
  title={copied ? 'Copied!' : 'Copy code'}
59
59
  >
60
60
  {copied ? (
@@ -86,7 +86,13 @@ function rewriteRelativeRefs(
86
86
  // Media was already decided by the host before insertion (see
87
87
  // resolveHostMedia); only the load-failure fallback is wired here.
88
88
  root.querySelectorAll<HTMLImageElement>('img[data-host-image]').forEach((img) => {
89
- const onError = () => img.replaceWith(unavailableMedia(img.ownerDocument, img.getAttribute('alt') || 'Image', IMAGE_NOT_FOUND_TITLE));
89
+ // The raw HTML is not React's: replacing the <img> here cannot trip React's insertBefore.
90
+ // The placeholder's alt text is never quoted by a text comment (annotation-exclude).
91
+ const onError = () => {
92
+ const placeholder = unavailableMedia(img.ownerDocument, img.getAttribute('alt') || 'Image', IMAGE_NOT_FOUND_TITLE);
93
+ placeholder.classList.add('annotation-exclude');
94
+ img.replaceWith(placeholder);
95
+ };
90
96
  img.addEventListener('error', onError);
91
97
  cleanups.push(() => img.removeEventListener('error', onError));
92
98
  });