mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
782d9740c1
A pinpointed element exported as a one-word placeholder ('[element:
Navigation]') or its flattened textContent. The bridge now captures a
bounded description at click time (tag, id, classes, ancestor path, role,
accessible name, allowlisted attributes, rendered text, an adaptive HTML
skeleton, box, landmark, heading, component hint, live route) as
elementContext; the parent re-validates and re-caps it; the export prints
a fenced skeleton plus selector/path/role/name lines under the comment;
the annotation panel gains a per-row Copy for element-bearing cards; the
feedback archive records element identity. Additive: annotations without
the field export byte-identically, share links drop it, no protocol bump.
51 lines
2.2 KiB
TypeScript
51 lines
2.2 KiB
TypeScript
/**
|
|
* Share-URL contract for multi-target HTML annotations: element anchors are
|
|
* deliberately dropped from share payloads (they are meaningless in another
|
|
* viewer's DOM), and htmlAdditionalTargets follow the exact same rule — the
|
|
* compact tuple format never carries them. The COMMENT itself (text, quoted
|
|
* primary text, author, images) still shares.
|
|
*/
|
|
import { describe, expect, test } from "bun:test";
|
|
import { AnnotationType, type Annotation } from "../types";
|
|
import { fromShareable, toShareable } from "./sharing";
|
|
|
|
const MULTI: Annotation = {
|
|
id: "ann-1",
|
|
blockId: "",
|
|
startOffset: 0,
|
|
endOffset: 0,
|
|
type: AnnotationType.COMMENT,
|
|
text: "Unify these",
|
|
originalText: "Primary chip",
|
|
createdA: 1,
|
|
author: "reviewer",
|
|
htmlAnchor: { selector: "p.primary", tagName: "p", text: "Primary chip" },
|
|
// Element context describes a DOM the link's recipient does not have: it
|
|
// follows the anchor rule and never enters a share payload.
|
|
elementContext: { tag: "p", path: "body > p.primary", outline: "<p>Primary chip</p>" },
|
|
htmlAdditionalTargets: [
|
|
{ label: "Button", text: "Create", anchor: { selector: "span.btn", tagName: "span", text: "Create" } },
|
|
],
|
|
};
|
|
|
|
describe("sharing — multi-target annotations", () => {
|
|
test("toShareable serializes the comment without anchors or additional targets", () => {
|
|
const shareable = toShareable([MULTI]);
|
|
expect(shareable).toEqual([["C", "Primary chip", "Unify these", "reviewer", undefined]]);
|
|
expect(JSON.stringify(shareable)).not.toContain("htmlAdditionalTargets");
|
|
expect(JSON.stringify(shareable)).not.toContain("selector");
|
|
expect(JSON.stringify(shareable)).not.toContain("elementContext");
|
|
expect(JSON.stringify(shareable)).not.toContain("outline");
|
|
});
|
|
|
|
test("round trip keeps the comment but has no target array", () => {
|
|
const restored = fromShareable(toShareable([MULTI]));
|
|
expect(restored.length).toBe(1);
|
|
expect(restored[0]!.text).toBe("Unify these");
|
|
expect(restored[0]!.originalText).toBe("Primary chip");
|
|
expect(restored[0]!.htmlAnchor).toBeUndefined();
|
|
expect(restored[0]!.htmlAdditionalTargets).toBeUndefined();
|
|
expect(restored[0]!.elementContext).toBeUndefined();
|
|
});
|
|
});
|