@plannotator/ui 0.53.0 → 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.
- package/HANDOFF.md +284 -27
- package/README.md +84 -14
- package/components/AgentToolAnnouncementDialog.tsx +1 -1
- package/components/AgentsTab.tsx +1 -1
- package/components/AnnotationPanel.tsx +177 -34
- package/components/AnnotationSidebar.tsx +1 -1
- package/components/AnnotationToolbar.tsx +64 -9
- package/components/AnnotationToolstrip.tsx +45 -40
- package/components/ApproveDropdown.tsx +7 -9
- package/components/AskSessionAnnouncementDialog.tsx +1 -1
- package/components/AttachmentsButton.tsx +1 -1
- package/components/BlockRenderer.tsx +19 -5
- package/components/CodeFilePopout.tsx +140 -5
- package/components/CommentPopover.tsx +46 -8
- package/components/DecisionControl.tsx +31 -5
- package/components/DiagramBlock.tsx +17 -12
- package/components/EditorAnnotationCard.tsx +1 -1
- package/components/ExportModal.tsx +77 -7
- package/components/HostTitle.tsx +0 -4
- package/components/HtmlSurfaceControls.tsx +2 -1
- package/components/ImageAnnotator/Toolbar.tsx +25 -1
- package/components/ImageAnnotator/index.tsx +24 -4
- package/components/ImageLightbox.tsx +658 -120
- package/components/ImagePeek.tsx +38 -4
- package/components/ImageRegions.tsx +369 -0
- package/components/ImageThumbnail.tsx +1 -1
- package/components/ImageZoomStage.tsx +796 -0
- package/components/InboxAnnouncementDialog.tsx +202 -0
- package/components/InlineMarkdown.tsx +152 -24
- package/components/KeyboardShortcuts.tsx +23 -0
- package/components/LazyBlockBoundary.tsx +100 -0
- package/components/ListMarker.tsx +3 -3
- package/components/LookAndFeelAnnouncementDialog.tsx +12 -2
- package/components/MentionPicker.tsx +51 -0
- package/components/MenuVersionSection.tsx +31 -9
- package/components/PlanDecisionMenuExtras.tsx +129 -0
- package/components/PlanHeaderMenu.tsx +15 -1
- package/components/ProviderIcons.tsx +8 -0
- package/components/RefHoverCard.tsx +913 -0
- package/components/SendFailureToast.tsx +126 -0
- package/components/Settings.tsx +617 -126
- package/components/SkillReferenceMenu.tsx +39 -12
- package/components/TerminalToolsAnnouncementDialog.tsx +1 -1
- package/components/ThemeProvider.tsx +14 -1
- package/components/ThemeTab.tsx +16 -24
- package/components/Tooltip.tsx +49 -18
- package/components/Viewer.tsx +316 -47
- package/components/ai/DocumentAIChatPanel.tsx +29 -3
- package/components/ai/NewChatButton.tsx +74 -0
- package/components/blocks/CodeBlock.tsx +1 -1
- package/components/blocks/HtmlBlock.tsx +7 -1
- package/components/blocks/QuestionBlock.tsx +522 -62
- package/components/blocks/QuestionContext.tsx +3 -0
- package/components/diagram/DiagramComposer.tsx +5 -1
- package/components/diagram/DiagramPending.tsx +12 -2
- package/components/diagram/DiagramViewer.tsx +19 -3
- package/components/html-embed/HtmlEmbedBlock.tsx +374 -0
- package/components/html-embed/SvgFenceBlock.tsx +46 -0
- package/components/html-embed/index.ts +3 -0
- package/components/html-embed/languages.ts +15 -0
- package/components/html-embed/loadGate.ts +107 -0
- package/components/html-viewer/HtmlViewer.tsx +69 -6
- package/components/html-viewer/bridge-script.asset.js +504 -12
- package/components/html-viewer/bridge-script.lite.ts +1 -1
- package/components/html-viewer/bridge-script.ts +545 -12
- package/components/html-viewer/useHtmlAnnotation.ts +125 -0
- package/components/imageZoom.ts +265 -0
- package/components/plan-diff/PlanCleanDiffView.tsx +1 -1
- package/components/plan-diff/PlanDiffViewer.tsx +13 -2
- package/components/plan-diff/PlanRawDiffView.tsx +1 -1
- package/components/sidebar/FileBrowser.tsx +16 -2
- package/components/sidebar/SidebarTabs.tsx +3 -0
- package/config/index.ts +3 -0
- package/config/reviewView.ts +41 -1
- package/config/settings.ts +155 -4
- package/configure.ts +28 -0
- package/hooks/pfm/useCodeFilePopout.ts +7 -1
- package/hooks/useAIChat.ts +72 -25
- package/hooks/useAnnotationDraft.ts +71 -6
- package/hooks/useAnnotationHighlighter.ts +57 -3
- package/hooks/useCodeAnnotationDraft.ts +113 -17
- package/hooks/useCookieHostSetting.ts +96 -0
- package/hooks/useDocumentFont.ts +17 -0
- package/hooks/useDocumentTextSize.ts +20 -0
- package/hooks/useFileBrowser.ts +10 -1
- package/hooks/useLinkedDoc.ts +226 -13
- package/hooks/useMentionAutocomplete.ts +1 -1
- package/hooks/usePackageChangesSetting.ts +90 -0
- package/hooks/usePlanLookDialog.ts +67 -0
- package/hooks/useSharing.ts +11 -7
- package/hooks/useSkillReferenceAutocomplete.ts +7 -3
- package/hooks/useThemePaletteHalves.ts +37 -0
- package/hooks/useUpdateCheck.ts +22 -4
- package/hooks/useUpdateToast.ts +1 -1
- package/hooks/useVimSelection.ts +9 -0
- package/package.json +23 -22
- package/shortcuts/code-review/annotationToolbar.shortcuts.ts +1 -0
- package/shortcuts/code-review/explorer.shortcuts.ts +82 -0
- package/shortcuts/core.ts +63 -4
- package/shortcuts/index.ts +1 -0
- package/shortcuts/plan-review/annotationPanel.shortcuts.ts +1 -1
- package/shortcuts/plan-review/commentPopover.shortcuts.ts +1 -0
- package/shortcuts/runtime.ts +14 -6
- package/styles.css +1 -1
- package/theme.css +179 -1
- package/themes/plannotator-graphite.css +66 -0
- package/types.ts +26 -9
- package/utils/agentSwitch.ts +41 -20
- package/utils/agentToolSetting.ts +1 -1
- package/utils/aiChatFormat.ts +23 -7
- package/utils/annotationOwnsHighlight.ts +4 -2
- package/utils/asciidocParser.ts +570 -0
- package/utils/autoUpdateNotice.ts +24 -0
- package/utils/belowTitleSlot.ts +174 -0
- package/utils/codeLineScroll.ts +100 -0
- package/utils/commentSubmitKey.ts +48 -0
- package/utils/cssFontFamily.ts +65 -0
- package/utils/decisionSpec.ts +190 -2
- package/utils/defaultNotesApp.ts +1 -1
- package/utils/diffFonts.ts +4 -1
- package/utils/documentFont.ts +68 -0
- package/utils/documentTextSize.ts +54 -0
- package/utils/fontDetect.ts +63 -0
- package/utils/inboxAnnouncement.ts +78 -0
- package/utils/lookAndFeelAnnouncement.ts +82 -5
- package/utils/markdownExtensions.ts +2 -2
- package/utils/mentions.ts +34 -5
- package/utils/mermaid.ts +20 -3
- package/utils/mermaidTheme.ts +3 -51
- package/utils/notion.ts +58 -0
- package/utils/parser.ts +28 -1
- package/utils/platform.ts +46 -0
- package/utils/refCard.ts +82 -0
- package/utils/richClipboard.ts +380 -0
- package/utils/richClipboardLists.ts +57 -0
- package/utils/richCopyPolicy.ts +22 -0
- package/utils/scrollAnchor.ts +82 -0
- package/utils/sectionRefs.ts +123 -0
- package/utils/selectionBoundary.ts +40 -0
- package/utils/sendFailure.ts +85 -0
- package/utils/sharing.ts +5 -2
- package/utils/themeRegistry.ts +10 -0
- package/utils/themeTokens.ts +74 -0
- package/utils/updateNotice.ts +30 -8
- package/utils/vscodePanel.ts +9 -0
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import type { Annotation, EditorMode, HostDraft, ImageAttachment, InputMethod } from "../../types";
|
|
19
19
|
import { AnnotationType } from "../../types";
|
|
20
20
|
import { copyTextPreservingFocus } from "../../utils/clipboard";
|
|
21
|
+
import { getRichCopyEnabled } from "../../utils/richCopyPolicy";
|
|
21
22
|
import { elementIdentityForAskAI } from "../../utils/parser";
|
|
22
23
|
import { getIdentity } from "../../utils/identity";
|
|
23
24
|
import { THUMBS_UP_LABEL } from "../../utils/quickLabels";
|
|
@@ -139,6 +140,14 @@ function parseVimBridgeHelp(value: unknown): boolean | null {
|
|
|
139
140
|
|
|
140
141
|
const MAX_VIM_COPY_TEXT_LENGTH = 2 * 1024 * 1024;
|
|
141
142
|
|
|
143
|
+
/**
|
|
144
|
+
* The srcdoc frame's sandbox. `allow-modals` lets the page's alert, confirm
|
|
145
|
+
* and prompt open (#2063: people's own pages and local sites). Never
|
|
146
|
+
* `allow-same-origin`: the page stays at an opaque origin, away from the
|
|
147
|
+
* session's API. Live-app mode is unsandboxed and does not use this.
|
|
148
|
+
*/
|
|
149
|
+
export const HTML_VIEWER_SANDBOX = "allow-scripts allow-modals";
|
|
150
|
+
|
|
142
151
|
/** Default wait for the bridge's `ready` on the `bridgeScriptUrl` path. */
|
|
143
152
|
export const DEFAULT_BRIDGE_READY_TIMEOUT_MS = 5000;
|
|
144
153
|
|
|
@@ -239,6 +248,13 @@ export interface HtmlViewerProps {
|
|
|
239
248
|
* key. Honored only while the frame holds focus, and at most once per
|
|
240
249
|
* STEP_MESSAGE_MIN_INTERVAL_MS. Absent: the message is ignored, as before. */
|
|
241
250
|
onStep?: (dir: 1 | -1) => void;
|
|
251
|
+
/** Escape pressed inside the page in Interact that the page did not use
|
|
252
|
+
* itself (no handler of the page cancelled it or stopped it, and focus was
|
|
253
|
+
* not in one of its text fields): the host may close something of its own
|
|
254
|
+
* (the Inbox leaves full screen). Never sent in Annotate, whose Escape is
|
|
255
|
+
* `onAnnotateModeExit`. Honored only while the frame holds focus, and at
|
|
256
|
+
* most once per STEP_MESSAGE_MIN_INTERVAL_MS. Absent: ignored, as before. */
|
|
257
|
+
onPageEscape?: () => void;
|
|
242
258
|
/** Opt-in Vim-style keyboard selection. Default false for compatibility. */
|
|
243
259
|
vimModeEnabled?: boolean;
|
|
244
260
|
/** Replace the iframe-local compact badge with the shared live key HUD. */
|
|
@@ -384,6 +400,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
384
400
|
onAnnotateModeToggle,
|
|
385
401
|
onToolsToggle,
|
|
386
402
|
onStep,
|
|
403
|
+
onPageEscape,
|
|
387
404
|
vimModeEnabled = false,
|
|
388
405
|
vimHudEnabled = false,
|
|
389
406
|
vimHudKeyPanelEnabled = true,
|
|
@@ -452,6 +469,9 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
452
469
|
const annotateModeActiveRef = useRef(annotateModeActive);
|
|
453
470
|
annotateModeActiveRef.current = annotateModeActive;
|
|
454
471
|
const lastStepAtRef = useRef(0);
|
|
472
|
+
const onPageEscapeRef = useRef(onPageEscape);
|
|
473
|
+
onPageEscapeRef.current = onPageEscape;
|
|
474
|
+
const lastPageEscapeAtRef = useRef(0);
|
|
455
475
|
|
|
456
476
|
/** Single choke point for direct-to-bridge posts: live sessions get the
|
|
457
477
|
* token + concrete targetOrigin, srcdoc keeps "*" and no token. */
|
|
@@ -809,6 +829,17 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
809
829
|
onStepRef.current?.(dir);
|
|
810
830
|
return;
|
|
811
831
|
}
|
|
832
|
+
if (isRecord(e.data) && e.data.type === `${PREFIX}page-escape`) {
|
|
833
|
+
// Like a step, the page could post this itself: honored only for a
|
|
834
|
+
// key pressed in the frame, in Interact, at most once per interval.
|
|
835
|
+
if (annotateModeActiveRef.current) return;
|
|
836
|
+
if (document.activeElement !== iframeRef.current) return;
|
|
837
|
+
const now = Date.now();
|
|
838
|
+
if (now - lastPageEscapeAtRef.current < STEP_MESSAGE_MIN_INTERVAL_MS) return;
|
|
839
|
+
lastPageEscapeAtRef.current = now;
|
|
840
|
+
onPageEscapeRef.current?.();
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
812
843
|
const vimCopy = parseVimBridgeCopy(e.data);
|
|
813
844
|
if (vimCopy !== null) {
|
|
814
845
|
const iframe = iframeRef.current;
|
|
@@ -879,9 +910,15 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
879
910
|
return true;
|
|
880
911
|
}, [readOnly, vimModeEnabled]);
|
|
881
912
|
|
|
913
|
+
// A composer hidden with its target (a slide the page hid) is not on
|
|
914
|
+
// screen: it neither blocks Vim's document focus nor counts as an open
|
|
915
|
+
// overlay. Escape still closes it, as its own rung (below).
|
|
916
|
+
const composerShown = !!hook.commentPopover && hook.pinVisible;
|
|
917
|
+
const composerHidden = !!hook.commentPopover && !hook.pinVisible;
|
|
918
|
+
|
|
882
919
|
useVimDocumentFocus({
|
|
883
920
|
enabled: !readOnly && vimModeEnabled,
|
|
884
|
-
blocked: !!hook.toolbarState ||
|
|
921
|
+
blocked: !!hook.toolbarState || composerShown || !!hook.quickLabelPicker,
|
|
885
922
|
focusDocument: focusVimDocument,
|
|
886
923
|
});
|
|
887
924
|
|
|
@@ -983,6 +1020,27 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
983
1020
|
postToBridge({ type: `${PREFIX}set-host-navigates`, active: true });
|
|
984
1021
|
}, [iframeReadyVersion, hostNavigates]);
|
|
985
1022
|
|
|
1023
|
+
// The hidden composer's Escape rung: with focus outside the iframe,
|
|
1024
|
+
// Escape closes the hidden draft (its text stays in the draft store) and
|
|
1025
|
+
// is used, so the next press reaches the rest of the ladder. Capture
|
|
1026
|
+
// phase, so it runs before the Vim document-focus rung. Inside the
|
|
1027
|
+
// iframe the bridge closes the draft and the hook closes the composer.
|
|
1028
|
+
const handleCommentCloseRef = useRef(hook.handleCommentClose);
|
|
1029
|
+
handleCommentCloseRef.current = hook.handleCommentClose;
|
|
1030
|
+
useEffect(() => {
|
|
1031
|
+
if (readOnly || !composerHidden) return;
|
|
1032
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
1033
|
+
if (e.key !== 'Escape' || e.defaultPrevented || e.isComposing) return;
|
|
1034
|
+
const target = e.target as HTMLElement | null;
|
|
1035
|
+
if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)) return;
|
|
1036
|
+
if (document.querySelector('[data-plannotator-confirm-dialog="true"]')) return;
|
|
1037
|
+
e.preventDefault();
|
|
1038
|
+
handleCommentCloseRef.current();
|
|
1039
|
+
};
|
|
1040
|
+
window.addEventListener('keydown', onKeyDown, true);
|
|
1041
|
+
return () => window.removeEventListener('keydown', onKeyDown, true);
|
|
1042
|
+
}, [readOnly, composerHidden]);
|
|
1043
|
+
|
|
986
1044
|
// Parent-side Esc rung: with focus outside the iframe the bridge never
|
|
987
1045
|
// sees the keydown. Any open composer/toolbar/picker still closes first —
|
|
988
1046
|
// their state is read from this render's closure, so an Esc that closed
|
|
@@ -993,6 +1051,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
993
1051
|
!!hook.toolbarState || !!hook.commentPopover || !!hook.quickLabelPicker || !!globalCommentPopover;
|
|
994
1052
|
const onKeyDown = (e: KeyboardEvent) => {
|
|
995
1053
|
if (e.key !== 'Escape' || e.defaultPrevented) return;
|
|
1054
|
+
// A hidden composer is closed by its own rung above.
|
|
996
1055
|
if (overlayOpen) return;
|
|
997
1056
|
// A text field or dialog owns its own Escape.
|
|
998
1057
|
const target = e.target as HTMLElement | null;
|
|
@@ -1035,7 +1094,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1035
1094
|
|
|
1036
1095
|
const vimOverlayWasOpenRef = useRef(false);
|
|
1037
1096
|
useEffect(() => {
|
|
1038
|
-
const overlayOpen = !!hook.toolbarState ||
|
|
1097
|
+
const overlayOpen = !!hook.toolbarState || composerShown || !!hook.quickLabelPicker;
|
|
1039
1098
|
const wasOpen = vimOverlayWasOpenRef.current;
|
|
1040
1099
|
vimOverlayWasOpenRef.current = overlayOpen;
|
|
1041
1100
|
if (
|
|
@@ -1050,7 +1109,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1050
1109
|
{ type: `${PREFIX}focus-vim` },
|
|
1051
1110
|
);
|
|
1052
1111
|
}
|
|
1053
|
-
}, [
|
|
1112
|
+
}, [composerShown, hook.quickLabelPicker, hook.toolbarState, readOnly, vimModeEnabled]);
|
|
1054
1113
|
|
|
1055
1114
|
useEffect(() => {
|
|
1056
1115
|
if (iframeReadyVersion === 0) return;
|
|
@@ -1236,7 +1295,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1236
1295
|
same-origin XHR) and no srcdoc. Srcdoc mode is unchanged. */}
|
|
1237
1296
|
<iframe
|
|
1238
1297
|
ref={iframeRef}
|
|
1239
|
-
{...(src ? { src } : { srcDoc: srcdoc, sandbox:
|
|
1298
|
+
{...(src ? { src } : { srcDoc: srcdoc, sandbox: HTML_VIEWER_SANDBOX + (hostNavigates ? " allow-popups" : "") })}
|
|
1240
1299
|
style={{
|
|
1241
1300
|
width: "100%",
|
|
1242
1301
|
height: fullViewport ? "100%" : `${iframeHeight}px`,
|
|
@@ -1293,6 +1352,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1293
1352
|
positionMode="center-above"
|
|
1294
1353
|
element={hook.toolbarState.element}
|
|
1295
1354
|
copyText={hook.toolbarState.selectionText}
|
|
1355
|
+
getRichCopy={getRichCopyEnabled() ? hook.toolbarState.getRichCopy : undefined}
|
|
1296
1356
|
// HTML/live surfaces are comment-only: no Delete, no label
|
|
1297
1357
|
// picker, no Alt+digit labels (commentOnly). Exactly ONE label
|
|
1298
1358
|
// affordance is restored: the hardcoded 👍 "Looks good". The
|
|
@@ -1311,8 +1371,10 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1311
1371
|
document.body,
|
|
1312
1372
|
)}
|
|
1313
1373
|
|
|
1314
|
-
{/*
|
|
1315
|
-
|
|
1374
|
+
{/* Hidden with its target (a slide the page hid): CommentPopover's draft
|
|
1375
|
+
store keeps the text and images, and shown again it does not take
|
|
1376
|
+
focus from where the person is. */}
|
|
1377
|
+
{!readOnly && hook.pinVisible && hook.commentPopover &&
|
|
1316
1378
|
createPortal(
|
|
1317
1379
|
<CommentPopover
|
|
1318
1380
|
anchorEl={hook.commentPopover.anchorEl}
|
|
@@ -1340,6 +1402,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1340
1402
|
onRemoveTargetChip={targetChips ? hook.removeDraftTarget : undefined}
|
|
1341
1403
|
onHoverTargetChip={targetChips ? hook.flashDraftTarget : undefined}
|
|
1342
1404
|
refocusToken={targetChips ? hook.composerFocusToken : undefined}
|
|
1405
|
+
autoFocus={hook.composerAutoFocus}
|
|
1343
1406
|
captureStrayKeys={multiSelectActive}
|
|
1344
1407
|
yieldState={multiSelectActive ? composerYield : undefined}
|
|
1345
1408
|
/>,
|