@plannotator/ui 0.46.1 → 0.49.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 +114 -3
- package/README.md +41 -2
- package/components/AISettingsTab.tsx +6 -0
- package/components/AgentsTab.tsx +11 -1
- package/components/AnnotationPanel.tsx +44 -3
- package/components/BlockRenderer.tsx +55 -1
- package/components/CompletionOverlay.tsx +23 -1
- package/components/MarkdownEditor.tsx +9 -0
- package/components/ModelSourceHint.tsx +49 -0
- package/components/PRPlatformIcon.tsx +22 -0
- package/components/QuestionProgressChip.tsx +66 -0
- package/components/QuestionsPanelSection.tsx +121 -0
- package/components/Settings.tsx +25 -1
- package/components/Viewer.tsx +169 -6
- package/components/ai/AIProviderBar.tsx +11 -3
- package/components/blocks/QuestionBlock.tsx +591 -0
- package/config/settings.ts +17 -0
- package/hooks/useAgentSettings.ts +8 -1
- package/hooks/useAnnotationHighlighter.ts +4 -0
- package/hooks/useCodeAnnotationDraft.ts +24 -7
- package/hooks/useModelCatalogs.ts +28 -11
- package/hooks/usePinpoint.ts +10 -0
- package/hooks/useScrollKeyRouting.ts +157 -0
- package/hooks/useUndoHistory.ts +17 -0
- package/hooks/useVimDocumentFocus.ts +2 -1
- package/package.json +4 -4
- package/styles.css +1 -1
- package/types.ts +3 -0
- package/utils/aiProvider.ts +4 -0
- package/utils/autoUpdateNotice.ts +59 -0
- package/utils/decisionSpec.ts +36 -2
- package/utils/parser.ts +125 -563
- package/utils/questionAnswers.ts +227 -0
package/types.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { DiagramAnchor } from '@plannotator/core/diagram-anchor';
|
|
2
2
|
import type { DiagramRenderKind } from '@plannotator/core/annotatable';
|
|
3
|
+
import type { QuestionAnswer } from '@plannotator/core/question-block';
|
|
3
4
|
|
|
4
5
|
export type { DiagramAnchor } from '@plannotator/core/diagram-anchor';
|
|
6
|
+
export type { QuestionAnswer } from '@plannotator/core/question-block';
|
|
5
7
|
export type { DiagramRenderKind } from '@plannotator/core/annotatable';
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -100,6 +102,7 @@ export interface Annotation {
|
|
|
100
102
|
elementContext?: HtmlElementContext; // raw-HTML / live-app pinpoint: bounded agent-facing description of the primary element (never used by restore)
|
|
101
103
|
htmlAdditionalTargets?: HtmlAnnotationTarget[]; // raw-HTML shift-click multi-select: extra elements this one comment covers (primary stays htmlAnchor/originalText)
|
|
102
104
|
diagramAnchor?: DiagramAnchor; // a comment on a rendered diagram part (Mermaid / Graphviz fence): the part's own id, label and document source line; the highlighter skips it and the diagram overlay restores it (see @plannotator/core/diagram-anchor)
|
|
105
|
+
questionAnswer?: QuestionAnswer; // the reviewer's answer to a `:::question` block (id `ann-question-<key>`): the Viewer draws the answer from it, the highlighter skips it, and the export prints it in the "Answers to your questions" section instead of as a numbered comment (see @plannotator/core/question-block)
|
|
103
106
|
// web-highlighter metadata for cross-element selections
|
|
104
107
|
startMeta?: AnnotationTextMeta;
|
|
105
108
|
endMeta?: AnnotationTextMeta;
|
package/utils/aiProvider.ts
CHANGED
|
@@ -21,6 +21,10 @@ export interface AIProviderOption {
|
|
|
21
21
|
id: string;
|
|
22
22
|
name: string;
|
|
23
23
|
models?: AIProviderModel[];
|
|
24
|
+
/** Where `models` came from, when the server says (`/api/ai/capabilities`). */
|
|
25
|
+
modelsSource?: 'fallback' | 'discovered';
|
|
26
|
+
/** The installed CLI's version, when the server reports it. */
|
|
27
|
+
toolVersion?: string;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export interface AIProviderSettings {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time notice after a background auto-update (#1634).
|
|
3
|
+
*
|
|
4
|
+
* The compiled CLI's servers attach `autoUpdateNotice` to /api/plan and
|
|
5
|
+
* /api/diff when the data dir records an install attempt: `updated` once the
|
|
6
|
+
* running binary is the new version, `failed` when the install script exited
|
|
7
|
+
* non-zero. The notice id is stable per attempt; the "seen" marker is a cookie,
|
|
8
|
+
* which is shared across the random ports sessions run on, so the toast shows
|
|
9
|
+
* once whichever app (plan, annotate, review) loads first.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { getItem, setItem } from './storage';
|
|
13
|
+
|
|
14
|
+
export interface AutoUpdateNotice {
|
|
15
|
+
id: string;
|
|
16
|
+
kind: 'updated' | 'failed';
|
|
17
|
+
version: string;
|
|
18
|
+
releaseUrl: string;
|
|
19
|
+
logPath?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const SEEN_KEY = 'plannotator-auto-update-notice-seen';
|
|
23
|
+
|
|
24
|
+
export function parseAutoUpdateNotice(value: unknown): AutoUpdateNotice | undefined {
|
|
25
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
26
|
+
const v = value as Record<string, unknown>;
|
|
27
|
+
if (typeof v.id !== 'string' || !v.id) return undefined;
|
|
28
|
+
if (v.kind !== 'updated' && v.kind !== 'failed') return undefined;
|
|
29
|
+
if (typeof v.version !== 'string' || typeof v.releaseUrl !== 'string') return undefined;
|
|
30
|
+
if (!/^https:\/\/github\.com\//.test(v.releaseUrl)) return undefined;
|
|
31
|
+
return {
|
|
32
|
+
id: v.id,
|
|
33
|
+
kind: v.kind,
|
|
34
|
+
version: v.version,
|
|
35
|
+
releaseUrl: v.releaseUrl,
|
|
36
|
+
...(typeof v.logPath === 'string' && { logPath: v.logPath }),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* True the first time a given notice is claimed in this browser, false after.
|
|
42
|
+
* Claiming marks it seen, so a caller shows the toast only when this is true.
|
|
43
|
+
*/
|
|
44
|
+
export function claimAutoUpdateNotice(notice: AutoUpdateNotice): boolean {
|
|
45
|
+
if (getItem(SEEN_KEY) === notice.id) return false;
|
|
46
|
+
setItem(SEEN_KEY, notice.id);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Title and description for the toast. */
|
|
51
|
+
export function describeAutoUpdateNotice(notice: AutoUpdateNotice): { title: string; description: string } {
|
|
52
|
+
if (notice.kind === 'updated') {
|
|
53
|
+
return { title: `Updated to v${notice.version}`, description: 'Plannotator updated itself in the background.' };
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
title: 'Auto-update failed',
|
|
57
|
+
description: notice.logPath ? `See ${notice.logPath}` : `Could not install v${notice.version}.`,
|
|
58
|
+
};
|
|
59
|
+
}
|
package/utils/decisionSpec.ts
CHANGED
|
@@ -126,6 +126,15 @@ export interface DecisionPlatformInput {
|
|
|
126
126
|
mrLabel: string;
|
|
127
127
|
/** The viewer authored this PR/MR: approve paths mute, never disappear. */
|
|
128
128
|
selfAuthored: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* #1611: whether the platform has a real request-changes review (GitHub
|
|
131
|
+
* does, GitLab does not). `false` makes the request-changes rows say they
|
|
132
|
+
* post as a comment. Absent keeps the original copy, so a host that never
|
|
133
|
+
* sets it renders exactly what it rendered before. When supported, a
|
|
134
|
+
* self-authored PR mutes Request changes too (GitHub refuses it on your own
|
|
135
|
+
* PR) and adds a live "Comment…" row so the empty state is never a dead end.
|
|
136
|
+
*/
|
|
137
|
+
requestChangesSupported?: boolean;
|
|
129
138
|
}
|
|
130
139
|
|
|
131
140
|
export const DECISION_NOTE_PLACEHOLDER = 'Add a note...';
|
|
@@ -345,6 +354,11 @@ function buildPlatformSpec(input: DecisionSpecInput, platform: DecisionPlatformI
|
|
|
345
354
|
// Frozen copy (maintainer-approved): the self-approval mute reason.
|
|
346
355
|
const selfReason = `You can't approve your own ${noun} on ${platform.label}.`;
|
|
347
356
|
const selfReasonShort = `You can't approve your own ${platform.mrLabel}`;
|
|
357
|
+
const requestChangesUnsupported = platform.requestChangesSupported === false;
|
|
358
|
+
// GitHub refuses REQUEST_CHANGES on your own PR, exactly like APPROVE.
|
|
359
|
+
const requestChangesMuted = selfAuthored && platform.requestChangesSupported === true;
|
|
360
|
+
const selfRequestChangesShort = `You can't request changes on your own ${platform.mrLabel}`;
|
|
361
|
+
const postsAsComment = `Posts as a comment; ${platform.label} has no request-changes review`;
|
|
348
362
|
|
|
349
363
|
if (!input.hasFeedback) {
|
|
350
364
|
return {
|
|
@@ -374,11 +388,27 @@ function buildPlatformSpec(input: DecisionSpecInput, platform: DecisionPlatformI
|
|
|
374
388
|
id: 'request-changes',
|
|
375
389
|
// Frozen copy (maintainer-approved): 'Request changes…'.
|
|
376
390
|
label: 'Request changes…',
|
|
377
|
-
subtitle:
|
|
391
|
+
subtitle: requestChangesMuted
|
|
392
|
+
? selfRequestChangesShort
|
|
393
|
+
: requestChangesUnsupported
|
|
394
|
+
? postsAsComment
|
|
395
|
+
: 'Overall feedback, zero line comments, via the dialog',
|
|
378
396
|
tone: 'primary',
|
|
379
397
|
icon: 'send',
|
|
380
398
|
dividerBefore: true,
|
|
399
|
+
...(requestChangesMuted ? { muted: true } : {}),
|
|
381
400
|
},
|
|
401
|
+
// With Request changes muted on your own PR, a neutral comment is the
|
|
402
|
+
// live path (the empty state is never a dead end).
|
|
403
|
+
...(requestChangesMuted
|
|
404
|
+
? [{
|
|
405
|
+
id: 'note-with-feedback' as const,
|
|
406
|
+
label: 'Comment…',
|
|
407
|
+
subtitle: 'Overall feedback as a neutral comment, via the dialog',
|
|
408
|
+
tone: 'primary' as const,
|
|
409
|
+
icon: 'send' as const,
|
|
410
|
+
}]
|
|
411
|
+
: []),
|
|
382
412
|
],
|
|
383
413
|
};
|
|
384
414
|
}
|
|
@@ -406,7 +436,11 @@ function buildPlatformSpec(input: DecisionSpecInput, platform: DecisionPlatformI
|
|
|
406
436
|
{
|
|
407
437
|
id: 'note-with-feedback',
|
|
408
438
|
label: 'Post comments, then…',
|
|
409
|
-
subtitle:
|
|
439
|
+
subtitle: requestChangesUnsupported
|
|
440
|
+
? postsAsComment
|
|
441
|
+
: requestChangesMuted
|
|
442
|
+
? `Posts as a neutral comment; you can't request changes on your own ${platform.mrLabel}`
|
|
443
|
+
: 'Request changes or stay neutral, chosen in the dialog',
|
|
410
444
|
tone: 'primary',
|
|
411
445
|
icon: 'send',
|
|
412
446
|
dividerBefore: true,
|