@plannotator/ui 0.39.0 → 0.40.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 +99 -10
- package/README.md +3 -3
- package/components/AnnotationPanel.tsx +244 -13
- package/components/CommentPopover.tsx +91 -2
- package/components/HtmlSurfaceControls.tsx +188 -23
- package/components/ListMarker.tsx +10 -1
- package/components/MermaidBlock.tsx +39 -5
- package/components/TableOfContents.tsx +5 -1
- package/components/TerminalToolsAnnouncementDialog.tsx +454 -0
- package/components/Viewer.tsx +25 -1
- package/components/blocks/AlertBlock.tsx +7 -2
- package/components/html-viewer/HtmlViewer.tsx +32 -0
- package/components/html-viewer/bridge-script.asset.js +96 -9
- package/components/html-viewer/bridge-script.ts +96 -9
- package/components/html-viewer/useHtmlAnnotation.ts +44 -151
- package/hooks/useAnnotationHighlighter.ts +464 -14
- package/hooks/useLinkedDoc.ts +100 -9
- package/package.json +3 -3
- package/shortcuts/plan-review/htmlAnnotate.shortcuts.ts +21 -7
- package/styles.css +1 -1
- package/theme.css +62 -0
- package/utils/annotationScope.ts +159 -0
- package/utils/cssColor.ts +463 -0
- package/utils/htmlChrome.ts +70 -5
- package/utils/htmlLinkNavigation.ts +196 -0
- package/utils/mermaid-eager.ts +13 -11
- package/utils/mermaid.ts +19 -10
- package/utils/mermaidTheme.ts +732 -0
- package/utils/parser.ts +21 -6
- package/utils/terminalToolsAnnouncement.ts +76 -0
package/utils/parser.ts
CHANGED
|
@@ -1131,6 +1131,9 @@ export interface ElementContextExportOptions {
|
|
|
1131
1131
|
/** Emit the live-app route line. The grouped export already prints a
|
|
1132
1132
|
* `## Page:` heading, so it passes false; a single copied entry passes true. */
|
|
1133
1133
|
includeRoute?: boolean;
|
|
1134
|
+
/** Print the identity lines WITHOUT the fenced outline, for model turns where
|
|
1135
|
+
* the 600-char outline is the expensive part. Default true. */
|
|
1136
|
+
includeOutline?: boolean;
|
|
1134
1137
|
}
|
|
1135
1138
|
|
|
1136
1139
|
/** The agent-facing element block for a raw-HTML / live-app pinpoint: a
|
|
@@ -1141,8 +1144,9 @@ export interface ElementContextExportOptions {
|
|
|
1141
1144
|
export const elementContextExportBlock = (ann: any, opts: ElementContextExportOptions = {}): string => {
|
|
1142
1145
|
const context = ann?.elementContext;
|
|
1143
1146
|
if (!context || typeof context !== 'object' || typeof context.tag !== 'string') return '';
|
|
1147
|
+
const includeOutline = opts.includeOutline ?? true;
|
|
1144
1148
|
let block = '';
|
|
1145
|
-
if (typeof context.outline === 'string' && context.outline.trim()) {
|
|
1149
|
+
if (includeOutline && typeof context.outline === 'string' && context.outline.trim()) {
|
|
1146
1150
|
// Fence at 4 backticks; the boundary already defuses 3+ runs inside the
|
|
1147
1151
|
// outline, and a 4-run here cannot be closed by anything the page wrote.
|
|
1148
1152
|
const outline = context.outline.replace(/`{3,}/g, "'''").trim();
|
|
@@ -1205,11 +1209,18 @@ const additionalTargetsExportBlock = (ann: any): string => {
|
|
|
1205
1209
|
// Element identity for the agent, one line per extra target: the
|
|
1206
1210
|
// selector and the path (a full context block per target would swamp
|
|
1207
1211
|
// the comment; the rest of the context stays persisted, not exported).
|
|
1212
|
+
//
|
|
1213
|
+
// Gated on the target carrying element context, which is what makes the
|
|
1214
|
+
// element-context work additive in the literal sense it claims: a target
|
|
1215
|
+
// captured before it existed (or restored from an older draft) has an
|
|
1216
|
+
// anchor but no context, and exports byte-identically to before.
|
|
1208
1217
|
const locators: string[] = [];
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1218
|
+
if (target?.context && typeof target.context === 'object') {
|
|
1219
|
+
const selector = target?.anchor?.selector;
|
|
1220
|
+
if (typeof selector === 'string' && selector) locators.push(`\`${safeInline(selector, 300)}\``);
|
|
1221
|
+
const path = target?.context?.path;
|
|
1222
|
+
if (typeof path === 'string' && path) locators.push(`\`${safeInline(path, 512)}\``);
|
|
1223
|
+
}
|
|
1213
1224
|
block += `- ${label}"${clipped}"${locators.length ? ` — ${locators.join(' · ')}` : ''}\n`;
|
|
1214
1225
|
});
|
|
1215
1226
|
return block;
|
|
@@ -1241,7 +1252,11 @@ export const exportAnnotationEntry = (ann: any, opts: ElementContextExportOption
|
|
|
1241
1252
|
output += `${commentHeadingLine(ann)}\n> ${ann?.text ?? ''}\n`;
|
|
1242
1253
|
}
|
|
1243
1254
|
}
|
|
1244
|
-
|
|
1255
|
+
const resolvedOpts: ElementContextExportOptions = {
|
|
1256
|
+
includeRoute: opts.includeRoute ?? true,
|
|
1257
|
+
...(opts.includeOutline !== undefined ? { includeOutline: opts.includeOutline } : {}),
|
|
1258
|
+
};
|
|
1259
|
+
output += elementContextExportBlock(ann, resolvedOpts);
|
|
1245
1260
|
output += additionalTargetsExportBlock(ann);
|
|
1246
1261
|
if (Array.isArray(ann?.images) && ann.images.length > 0) {
|
|
1247
1262
|
output += `**Attached images:**\n`;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time gate for the terminal-tools announcement (Plannotator TUI and
|
|
3
|
+
* Herdr Annotate). Cookie-backed like the other announcement gates, so a
|
|
4
|
+
* dismissal survives Plannotator's random localhost ports, and shared by the
|
|
5
|
+
* plan editor, the annotate surfaces and the code review editor: dismissing it
|
|
6
|
+
* anywhere retires it everywhere.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { storage } from './storage';
|
|
10
|
+
|
|
11
|
+
const STORAGE_KEY = 'plannotator-announce-tui-herdr-seen';
|
|
12
|
+
// Bump to re-announce after a meaningful revision.
|
|
13
|
+
const CURRENT_VERSION = '1';
|
|
14
|
+
|
|
15
|
+
export function needsTerminalToolsAnnouncement(): boolean {
|
|
16
|
+
return storage.getItem(STORAGE_KEY) !== CURRENT_VERSION;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function markTerminalToolsAnnouncementSeen(): void {
|
|
20
|
+
storage.setItem(STORAGE_KEY, CURRENT_VERSION);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TerminalToolsAnnouncementGateState {
|
|
24
|
+
/**
|
|
25
|
+
* Latched at mount from needsTerminalToolsAnnouncement(). Latched rather than
|
|
26
|
+
* read per render so a dismissal cannot unmount the dialog before its own
|
|
27
|
+
* click handler finishes.
|
|
28
|
+
*/
|
|
29
|
+
readonly announcementPending: boolean;
|
|
30
|
+
/** The app has not finished loading its initial payload. */
|
|
31
|
+
readonly isLoading: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The session has no author to address: archive browsing, a read-only shared
|
|
34
|
+
* plan, and any session with no Plannotator server behind it (the share
|
|
35
|
+
* portal's root and its demo plan included — those are not "shared
|
|
36
|
+
* sessions", so the host must fold that in). Telling a viewer about a CLI
|
|
37
|
+
* they did not open is noise, and the cookie is deliberately NOT consumed,
|
|
38
|
+
* so the next authoring session still shows it.
|
|
39
|
+
*/
|
|
40
|
+
readonly readOnlySession: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Plannotator's compact touch shell. The panel is desktop-shaped (install
|
|
43
|
+
* commands to copy, four outbound links) and a phone is not where anyone
|
|
44
|
+
* installs a terminal tool. Also deferred rather than consumed.
|
|
45
|
+
*/
|
|
46
|
+
readonly compact: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Any other first-run dialog is on screen. The chain dialogs never stack.
|
|
49
|
+
*/
|
|
50
|
+
readonly otherFirstRunDialogVisible: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Chain gate for the announcement. It is LAST in each app's first-run dialog
|
|
55
|
+
* chain, after every dialog that asks the user to decide something (code
|
|
56
|
+
* review: guide intro, look-and-feel, review setup, edit mode, token hover
|
|
57
|
+
* cards; plan and annotate: look-and-feel, goal setup, permission mode).
|
|
58
|
+
*
|
|
59
|
+
* Last rather than first because none of those dialogs consume this cookie:
|
|
60
|
+
* a session that is busy asking questions defers the announcement to the next
|
|
61
|
+
* load instead of burning it. That also puts it in front of the right reader.
|
|
62
|
+
* Someone opening Plannotator for the first time is still learning this app;
|
|
63
|
+
* the people who should hear that it now runs in a terminal are the ones who
|
|
64
|
+
* already answered every setup question, and they see it on their next load.
|
|
65
|
+
*/
|
|
66
|
+
export function terminalToolsAnnouncementCanShow(
|
|
67
|
+
state: TerminalToolsAnnouncementGateState,
|
|
68
|
+
): boolean {
|
|
69
|
+
return (
|
|
70
|
+
state.announcementPending &&
|
|
71
|
+
!state.isLoading &&
|
|
72
|
+
!state.readOnlySession &&
|
|
73
|
+
!state.compact &&
|
|
74
|
+
!state.otherFirstRunDialogVisible
|
|
75
|
+
);
|
|
76
|
+
}
|