@plannotator/ui 0.53.1 → 0.55.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 +274 -14
- package/README.md +114 -11
- package/components/AgentToolAnnouncementDialog.tsx +1 -1
- package/components/AgentsTab.tsx +1 -1
- package/components/AnnotationPanel.tsx +142 -24
- package/components/AnnotationSidebar.tsx +1 -1
- package/components/AnnotationToolbar.tsx +64 -9
- package/components/ApproveDropdown.tsx +7 -9
- package/components/AskSessionAnnouncementDialog.tsx +1 -1
- package/components/AttachmentsButton.tsx +1 -1
- package/components/BlockRenderer.tsx +22 -5
- package/components/CodeFilePopout.tsx +139 -4
- package/components/CodeSnippetPreview.tsx +174 -0
- package/components/CommentPopover.tsx +44 -8
- package/components/DecisionControl.tsx +31 -5
- package/components/DeferredMarkdownContentsRail.tsx +38 -0
- package/components/DiagramBlock.tsx +17 -12
- package/components/DiffFileTree.tsx +7 -6
- package/components/DocumentContents.tsx +74 -0
- package/components/EditorAnnotationCard.tsx +1 -1
- package/components/ExportModal.tsx +77 -7
- package/components/HostTitle.tsx +0 -4
- package/components/ImageAnnotator/Toolbar.tsx +25 -1
- package/components/ImageAnnotator/index.tsx +24 -4
- package/components/ImageLightbox.tsx +722 -126
- 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 +167 -174
- 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/MarkdownContentsRail.tsx +777 -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/TableOfContents.tsx +20 -6
- 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 +288 -43
- 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 +532 -62
- 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 +82 -6
- package/components/html-viewer/bridge-script.asset.js +173 -1
- package/components/html-viewer/bridge-script.ts +173 -1
- package/components/html-viewer/useHtmlAnnotation.ts +125 -0
- package/components/imageZoom.ts +276 -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/contents-rail.css +337 -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/useHeadingScrollSpy.ts +77 -0
- package/hooks/useHoverPreview.ts +94 -0
- package/hooks/useLinkedDoc.ts +226 -13
- package/hooks/usePackageChangesSetting.ts +90 -0
- package/hooks/usePlanLookDialog.ts +67 -0
- package/hooks/usePrefersReducedMotion.ts +23 -0
- package/hooks/useSharing.ts +11 -7
- package/hooks/useSwipeStep.ts +597 -0
- package/hooks/useThemePaletteHalves.ts +37 -0
- package/hooks/useUpdateCheck.ts +22 -4
- package/hooks/useUpdateToast.ts +1 -1
- package/package.json +26 -23
- 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 +4 -3
- 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/annotationHelpers.ts +32 -4
- 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/blockParse.ts +45 -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/headingScrollSpy.ts +92 -0
- package/utils/inboxAnnouncement.ts +78 -0
- package/utils/lookAndFeelAnnouncement.ts +82 -5
- package/utils/markdownExtensions.ts +2 -2
- 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
|
@@ -2,7 +2,11 @@ import React from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
formatShortcutBindingTokens,
|
|
4
4
|
listScopeShortcuts,
|
|
5
|
+
resolveShortcutBindings,
|
|
6
|
+
reviewExplorerShortcuts,
|
|
7
|
+
reviewMarkdownPreviewShortcuts,
|
|
5
8
|
vimSelectionShortcuts,
|
|
9
|
+
type ShortcutScopeDefinition,
|
|
6
10
|
} from '../shortcuts';
|
|
7
11
|
import { isMac, modKey, altKey } from '../utils/platform';
|
|
8
12
|
|
|
@@ -141,6 +145,23 @@ const vimShortcuts: ShortcutSection[] = (() => {
|
|
|
141
145
|
return Array.from(sections, ([title, shortcuts]) => ({ title, shortcuts }));
|
|
142
146
|
})();
|
|
143
147
|
|
|
148
|
+
/** Sections read from registry scopes (this system's keys), so they cannot drift. */
|
|
149
|
+
function scopeSections(scope: ShortcutScopeDefinition<any>): ShortcutSection[] {
|
|
150
|
+
const sections = new Map<string, Shortcut[]>();
|
|
151
|
+
const entries = listScopeShortcuts(scope)
|
|
152
|
+
.sort((left, right) => (left.displayOrder ?? 0) - (right.displayOrder ?? 0));
|
|
153
|
+
for (const entry of entries) {
|
|
154
|
+
const shortcuts = sections.get(entry.section) ?? [];
|
|
155
|
+
shortcuts.push({
|
|
156
|
+
keys: formatShortcutBindingTokens(resolveShortcutBindings(entry)[0] ?? ''),
|
|
157
|
+
desc: entry.description,
|
|
158
|
+
hint: entry.hint,
|
|
159
|
+
});
|
|
160
|
+
sections.set(entry.section, shortcuts);
|
|
161
|
+
}
|
|
162
|
+
return Array.from(sections, ([title, shortcuts]) => ({ title, shortcuts }));
|
|
163
|
+
}
|
|
164
|
+
|
|
144
165
|
const planActionShortcuts: ShortcutSection = {
|
|
145
166
|
title: 'Actions',
|
|
146
167
|
shortcuts: [
|
|
@@ -213,6 +234,8 @@ const reviewShortcuts: ShortcutSection[] = [
|
|
|
213
234
|
{ keys: ['Z'], desc: 'Undo collapse', hint: 'Reopens the last collapsed file and scrolls to it' },
|
|
214
235
|
],
|
|
215
236
|
},
|
|
237
|
+
...scopeSections(reviewExplorerShortcuts),
|
|
238
|
+
...scopeSections(reviewMarkdownPreviewShortcuts),
|
|
216
239
|
{
|
|
217
240
|
title: 'Annotations',
|
|
218
241
|
shortcuts: [
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A part of the page that a code-split build loads as its own chunk (a
|
|
5
|
+
* diagram, an HTML or SVG embed, the diagram popout, the Markdown preview)
|
|
6
|
+
* can fail to arrive: a flaky link, a server that has stopped, or a tab older
|
|
7
|
+
* than the server's build, whose chunk names no longer exist. React.lazy then
|
|
8
|
+
* throws, and without a boundary React unmounts the whole app, taking the
|
|
9
|
+
* user's unsent comments and edits off the screen with it.
|
|
10
|
+
*
|
|
11
|
+
* This boundary keeps the failure in place of that one part. The caller's
|
|
12
|
+
* `fallback` gets a small notice with a Reload action (`LazyLoadNotice`) and
|
|
13
|
+
* draws it beside whatever it can still show, usually the block's source.
|
|
14
|
+
* The rest of the page stays mounted. Nothing reloads by itself.
|
|
15
|
+
*
|
|
16
|
+
* With no error it renders its children and nothing else.
|
|
17
|
+
*/
|
|
18
|
+
export class LazyBlockBoundary extends React.Component<
|
|
19
|
+
{ fallback: (notice: React.ReactNode) => React.ReactNode; children: React.ReactNode },
|
|
20
|
+
{ failed: boolean; error: unknown }
|
|
21
|
+
> {
|
|
22
|
+
state = { failed: false, error: null as unknown };
|
|
23
|
+
|
|
24
|
+
static getDerivedStateFromError(error: unknown) {
|
|
25
|
+
return { failed: true, error };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
componentDidCatch(error: unknown) {
|
|
29
|
+
console.error('[plannotator] A part of the page could not load:', error);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
render() {
|
|
33
|
+
if (this.state.failed) return this.props.fallback(<LazyLoadNotice error={this.state.error} />);
|
|
34
|
+
return this.props.children;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The URL of the chunk a failed dynamic import asked for, when the error names
|
|
40
|
+
* one on this page's origin. Chromium and Firefox put it in the message
|
|
41
|
+
* ("Failed to fetch dynamically imported module: http://…/assets/x.js");
|
|
42
|
+
* Safari does not, and then the notice says the general thing.
|
|
43
|
+
*/
|
|
44
|
+
export function failedChunkUrl(error: unknown): string | null {
|
|
45
|
+
if (typeof window === 'undefined') return null;
|
|
46
|
+
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : '';
|
|
47
|
+
const match = /(https?:\/\/[^\s'"]+|\/assets\/[^\s'"]+)/.exec(message);
|
|
48
|
+
if (!match) return null;
|
|
49
|
+
try {
|
|
50
|
+
const url = new URL(match[1], window.location.href);
|
|
51
|
+
return url.origin === window.location.origin ? url.href : null;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* One quiet line: why the part is missing, and Reload. A chunk the server
|
|
59
|
+
* answers with 404 is a tab older than the server's build (the server was
|
|
60
|
+
* updated on the same port), so the line says that instead. Finding out costs
|
|
61
|
+
* one HEAD request, made only after a failure.
|
|
62
|
+
*/
|
|
63
|
+
export const LazyLoadNotice: React.FC<{ error: unknown }> = ({ error }) => {
|
|
64
|
+
const [updated, setUpdated] = useState(false);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const url = failedChunkUrl(error);
|
|
67
|
+
if (url === null || typeof fetch !== 'function') return;
|
|
68
|
+
let live = true;
|
|
69
|
+
fetch(url, { method: 'HEAD', cache: 'no-store' }).then(
|
|
70
|
+
(response) => {
|
|
71
|
+
if (live && response.status === 404) setUpdated(true);
|
|
72
|
+
},
|
|
73
|
+
() => {},
|
|
74
|
+
);
|
|
75
|
+
return () => {
|
|
76
|
+
live = false;
|
|
77
|
+
};
|
|
78
|
+
}, [error]);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
role="status"
|
|
83
|
+
data-lazy-load-failed={updated ? 'updated' : ''}
|
|
84
|
+
className="mb-1.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted-foreground"
|
|
85
|
+
>
|
|
86
|
+
<span>{updated ? 'This page was updated.' : "Couldn't load this view."}</span>
|
|
87
|
+
{/* The hit area is padding cancelled by a negative margin, so the line
|
|
88
|
+
looks the same: 24px tall everywhere, 44px on a touch screen. On
|
|
89
|
+
touch it grows upward into the block's top margin, never down over
|
|
90
|
+
the source under it. */}
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
onClick={() => window.location.reload()}
|
|
94
|
+
className="relative -mx-1 -my-1 px-1 py-1 font-medium text-foreground underline-offset-2 hover:underline [@media(pointer:coarse)]:-mb-1.5 [@media(pointer:coarse)]:-mt-[22px] [@media(pointer:coarse)]:pb-1.5 [@media(pointer:coarse)]:pt-[22px]"
|
|
95
|
+
>
|
|
96
|
+
{updated ? 'Reload to see this' : 'Reload'}
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
@@ -60,17 +60,17 @@ export const ListMarker: React.FC<ListMarkerProps> = ({
|
|
|
60
60
|
aria-checked={interactive ? checked : undefined}
|
|
61
61
|
>
|
|
62
62
|
{showNumeral && (
|
|
63
|
-
<span className="text-primary/60 tabular-nums text-right" style={{ minWidth: '1.5rem' }}>
|
|
63
|
+
<span className="text-primary/60 tabular-nums text-right" style={{ minWidth: '1.5rem' }} data-list-numeral="">
|
|
64
64
|
{orderedIndex}.
|
|
65
65
|
</span>
|
|
66
66
|
)}
|
|
67
67
|
{isCheckbox ? (
|
|
68
68
|
checked ? (
|
|
69
|
-
<svg className="w-4 h-4 text-success mt-[3px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
|
69
|
+
<svg data-list-check="" className="w-4 h-4 text-success mt-[3px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
|
70
70
|
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
71
71
|
</svg>
|
|
72
72
|
) : (
|
|
73
|
-
<svg className={`w-4 h-4 text-muted-foreground/50 mt-[3px]${interactive ? ' hover:text-muted-foreground transition-colors' : ''}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
73
|
+
<svg data-list-check="" className={`w-4 h-4 text-muted-foreground/50 mt-[3px]${interactive ? ' hover:text-muted-foreground transition-colors' : ''}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
74
74
|
<circle cx="12" cy="12" r="9" />
|
|
75
75
|
</svg>
|
|
76
76
|
)
|
|
@@ -12,6 +12,12 @@ interface LookAndFeelAnnouncementDialogProps {
|
|
|
12
12
|
onToggleGrid: (value: boolean) => void;
|
|
13
13
|
/** Persists the current choice and closes the dialog. */
|
|
14
14
|
onDismiss: () => void;
|
|
15
|
+
/**
|
|
16
|
+
* `first-run` (default): the first-use choice. `moved`: the one-time notice
|
|
17
|
+
* for someone moved from Grid to Clean when Clean became the default; the
|
|
18
|
+
* same chooser, with a line saying what changed.
|
|
19
|
+
*/
|
|
20
|
+
variant?: 'first-run' | 'moved';
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
const LOOK_OPTIONS: {
|
|
@@ -48,6 +54,7 @@ export const LookAndFeelAnnouncementDialog: React.FC<LookAndFeelAnnouncementDial
|
|
|
48
54
|
gridEnabled,
|
|
49
55
|
onToggleGrid,
|
|
50
56
|
onDismiss,
|
|
57
|
+
variant = 'first-run',
|
|
51
58
|
}) => {
|
|
52
59
|
const dialogRef = useRef<HTMLDivElement>(null);
|
|
53
60
|
const continueRef = useRef<HTMLButtonElement>(null);
|
|
@@ -115,6 +122,7 @@ export const LookAndFeelAnnouncementDialog: React.FC<LookAndFeelAnnouncementDial
|
|
|
115
122
|
<div
|
|
116
123
|
ref={dialogRef}
|
|
117
124
|
role="dialog"
|
|
125
|
+
data-plan-look-prompt={variant}
|
|
118
126
|
aria-modal="true"
|
|
119
127
|
aria-labelledby="plan-look-choice-title"
|
|
120
128
|
aria-describedby="plan-look-choice-description"
|
|
@@ -129,13 +137,15 @@ export const LookAndFeelAnnouncementDialog: React.FC<LookAndFeelAnnouncementDial
|
|
|
129
137
|
id="plan-look-choice-title"
|
|
130
138
|
className="text-balance text-xl font-semibold tracking-tight sm:text-2xl"
|
|
131
139
|
>
|
|
132
|
-
Choose how plans look
|
|
140
|
+
{variant === 'moved' ? 'Plans now open in Clean' : 'Choose how plans look'}
|
|
133
141
|
</h2>
|
|
134
142
|
<p
|
|
135
143
|
id="plan-look-choice-description"
|
|
136
144
|
className="mt-1 text-sm leading-relaxed text-muted-foreground"
|
|
137
145
|
>
|
|
138
|
-
|
|
146
|
+
{variant === 'moved'
|
|
147
|
+
? "Grid is one switch away in Settings, or pick it here."
|
|
148
|
+
: "Pick a starting view. You can change it anytime in Settings."}
|
|
139
149
|
</p>
|
|
140
150
|
</header>
|
|
141
151
|
|