@coframe-gtm/annotations 1.0.4 → 1.2.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/package.json +4 -8
- package/src/README.md +4 -3
- package/src/api.ts +12 -0
- package/src/bundle.ts +11 -5
- package/src/inject/build.ts +0 -2
- package/src/inject/bundle-source.generated.ts +1 -1
- package/src/output.ts +1 -1
- package/src/picker.ts +23 -5
- package/src/signal.ts +56 -0
- package/src/store.ts +36 -2
- package/src/ui/Avatar.ts +62 -0
- package/src/ui/Composer.ts +206 -0
- package/src/ui/Cursors.ts +48 -0
- package/src/ui/Pins.ts +138 -0
- package/src/ui/ThreadPanel.ts +132 -0
- package/src/ui/Toolbar.ts +270 -0
- package/src/ui/brand.ts +16 -0
- package/src/ui/dom.ts +95 -0
- package/src/ui/helpers.ts +89 -0
- package/src/ui/overlay.ts +163 -0
- package/src/ui/styles.ts +212 -34
- package/src/ui/App.tsx +0 -69
- package/src/ui/Composer.tsx +0 -138
- package/src/ui/Cursors.tsx +0 -50
- package/src/ui/Pins.tsx +0 -58
- package/src/ui/ThreadPanel.tsx +0 -124
- package/src/ui/Toolbar.tsx +0 -116
package/src/ui/ThreadPanel.tsx
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thread panel — the per-pin discussion (human ↔ agent). Shows the
|
|
3
|
-
* root annotation comment plus the reply thread, with a compose box
|
|
4
|
-
* and a resolve affordance. Reads the active annotation from the
|
|
5
|
-
* `../store.js` signals by id.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ComponentChild } from "preact";
|
|
9
|
-
import { useState } from "preact/hooks";
|
|
10
|
-
|
|
11
|
-
import { api } from "../api.js";
|
|
12
|
-
import { activeThreadId, annotations, author } from "../store.js";
|
|
13
|
-
import { SEVERITY_COLOR } from "./constants.js";
|
|
14
|
-
|
|
15
|
-
interface MessageProps {
|
|
16
|
-
role: "human" | "agent";
|
|
17
|
-
name: string;
|
|
18
|
-
content: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function Message({ role, name, content }: MessageProps): ComponentChild {
|
|
22
|
-
return (
|
|
23
|
-
<div class={`cf-msg cf-msg-${role}`}>
|
|
24
|
-
<div key="meta" class="cf-msg-meta">
|
|
25
|
-
<span key="av" class={`cf-avatar cf-avatar-${role}`}>
|
|
26
|
-
{role === "agent" ? "🤖" : "🧑"}
|
|
27
|
-
</span>
|
|
28
|
-
<span key="n" class="cf-msg-name">{name}</span>
|
|
29
|
-
</div>
|
|
30
|
-
<div key="c" class="cf-msg-body">{content}</div>
|
|
31
|
-
</div>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface ThreadPanelProps {
|
|
36
|
-
id: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function ThreadPanel({ id }: ThreadPanelProps): ComponentChild {
|
|
40
|
-
const [reply, setReply] = useState("");
|
|
41
|
-
const a = annotations.value.find((x) => x.id === id);
|
|
42
|
-
if (!a) return null;
|
|
43
|
-
|
|
44
|
-
const send = (): void => {
|
|
45
|
-
const text = reply.trim();
|
|
46
|
-
if (!text) return;
|
|
47
|
-
api.replyToAnnotation(id, { role: author.value.kind, content: text });
|
|
48
|
-
setReply("");
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<div class="cf-popup cf-thread">
|
|
53
|
-
<div key="head" class="cf-popup-head">
|
|
54
|
-
<span key="t" class="cf-popup-target">{`<${a.element}>`}</span>
|
|
55
|
-
{a.severity ? (
|
|
56
|
-
<span
|
|
57
|
-
key="sev"
|
|
58
|
-
class="cf-tag"
|
|
59
|
-
style={`--cf-tag:${SEVERITY_COLOR[a.severity]};`}
|
|
60
|
-
>
|
|
61
|
-
{a.severity}
|
|
62
|
-
</span>
|
|
63
|
-
) : null}
|
|
64
|
-
<button
|
|
65
|
-
key="x"
|
|
66
|
-
class="cf-icon-btn"
|
|
67
|
-
onClick={() => (activeThreadId.value = null)}
|
|
68
|
-
title="Close"
|
|
69
|
-
>
|
|
70
|
-
✕
|
|
71
|
-
</button>
|
|
72
|
-
</div>
|
|
73
|
-
<div key="body" class="cf-thread-body">
|
|
74
|
-
<Message
|
|
75
|
-
key="root"
|
|
76
|
-
role={a.author?.kind ?? "human"}
|
|
77
|
-
name={a.author?.displayName ?? "You"}
|
|
78
|
-
content={a.comment}
|
|
79
|
-
/>
|
|
80
|
-
{(a.thread ?? []).map((m) => (
|
|
81
|
-
<Message
|
|
82
|
-
key={m.id}
|
|
83
|
-
role={m.role}
|
|
84
|
-
name={m.role === "agent" ? "Agent" : "You"}
|
|
85
|
-
content={m.content}
|
|
86
|
-
/>
|
|
87
|
-
))}
|
|
88
|
-
</div>
|
|
89
|
-
<div key="compose" class="cf-thread-compose">
|
|
90
|
-
<textarea
|
|
91
|
-
key="ta"
|
|
92
|
-
class="cf-textarea cf-textarea-sm"
|
|
93
|
-
placeholder="Reply…"
|
|
94
|
-
value={reply}
|
|
95
|
-
onInput={(e: Event) => setReply((e.target as HTMLTextAreaElement).value)}
|
|
96
|
-
onKeyDown={(e: KeyboardEvent) => {
|
|
97
|
-
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") send();
|
|
98
|
-
}}
|
|
99
|
-
/>
|
|
100
|
-
<div key="row" class="cf-thread-actions">
|
|
101
|
-
{a.status !== "resolved" ? (
|
|
102
|
-
<button
|
|
103
|
-
key="resolve"
|
|
104
|
-
class="cf-btn cf-btn-ghost"
|
|
105
|
-
onClick={() => api.resolveAnnotation(id, author.value.kind)}
|
|
106
|
-
>
|
|
107
|
-
Resolve
|
|
108
|
-
</button>
|
|
109
|
-
) : (
|
|
110
|
-
<span key="resolved" class="cf-resolved-tag">✓ resolved</span>
|
|
111
|
-
)}
|
|
112
|
-
<button
|
|
113
|
-
key="send"
|
|
114
|
-
class="cf-btn cf-btn-primary"
|
|
115
|
-
disabled={!reply.trim()}
|
|
116
|
-
onClick={send}
|
|
117
|
-
>
|
|
118
|
-
Reply
|
|
119
|
-
</button>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
);
|
|
124
|
-
}
|
package/src/ui/Toolbar.tsx
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Toolbar — mode + tool switches, annotation count, and a contextual
|
|
3
|
-
* hint. Reads cross-cutting state from the `../store.js` signals and
|
|
4
|
-
* commits multi-selections through `../picker.js`.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { ComponentChild } from "preact";
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
annotations,
|
|
11
|
-
feedbackTool,
|
|
12
|
-
hoverBox,
|
|
13
|
-
mode,
|
|
14
|
-
multiSelection,
|
|
15
|
-
} from "../store.js";
|
|
16
|
-
import { commitMultiSelection } from "../picker.js";
|
|
17
|
-
|
|
18
|
-
export function hintFor(tool: string): string {
|
|
19
|
-
if (tool === "text") return "Select text on the page to annotate it.";
|
|
20
|
-
if (tool === "multi") return "Click elements to group them, then commit.";
|
|
21
|
-
return "Click any element to comment. Esc to exit.";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface SegButtonProps {
|
|
25
|
-
label: string;
|
|
26
|
-
on: boolean;
|
|
27
|
-
onClick: () => void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function SegButton({ label, on, onClick }: SegButtonProps): ComponentChild {
|
|
31
|
-
return (
|
|
32
|
-
<button class={`cf-seg-btn${on ? " on" : ""}`} onClick={onClick}>
|
|
33
|
-
{label}
|
|
34
|
-
</button>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function Toolbar(): ComponentChild {
|
|
39
|
-
const active = mode.value === "feedback";
|
|
40
|
-
const count = annotations.value.length;
|
|
41
|
-
const multi = multiSelection.value.length;
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<div key="toolbar" class="cf-toolbar">
|
|
45
|
-
<div key="head" class="cf-toolbar-head">
|
|
46
|
-
<span key="dot" class="cf-logo-dot" />
|
|
47
|
-
<span key="label">Annotations</span>
|
|
48
|
-
<span key="count" class="cf-count">{String(count)}</span>
|
|
49
|
-
</div>
|
|
50
|
-
<div key="modes" class="cf-seg">
|
|
51
|
-
<SegButton
|
|
52
|
-
key="View"
|
|
53
|
-
label="View"
|
|
54
|
-
on={mode.value === "view"}
|
|
55
|
-
onClick={() => {
|
|
56
|
-
mode.value = "view";
|
|
57
|
-
hoverBox.value = null;
|
|
58
|
-
multiSelection.value = [];
|
|
59
|
-
}}
|
|
60
|
-
/>
|
|
61
|
-
<SegButton
|
|
62
|
-
key="Comment"
|
|
63
|
-
label="Comment"
|
|
64
|
-
on={active}
|
|
65
|
-
onClick={() => {
|
|
66
|
-
mode.value = "feedback";
|
|
67
|
-
}}
|
|
68
|
-
/>
|
|
69
|
-
</div>
|
|
70
|
-
{active ? (
|
|
71
|
-
<div key="tools" class="cf-seg cf-tools">
|
|
72
|
-
<SegButton
|
|
73
|
-
key="Element"
|
|
74
|
-
label="Element"
|
|
75
|
-
on={feedbackTool.value === "element"}
|
|
76
|
-
onClick={() => {
|
|
77
|
-
feedbackTool.value = "element";
|
|
78
|
-
multiSelection.value = [];
|
|
79
|
-
}}
|
|
80
|
-
/>
|
|
81
|
-
<SegButton
|
|
82
|
-
key="Text"
|
|
83
|
-
label="Text"
|
|
84
|
-
on={feedbackTool.value === "text"}
|
|
85
|
-
onClick={() => {
|
|
86
|
-
feedbackTool.value = "text";
|
|
87
|
-
multiSelection.value = [];
|
|
88
|
-
}}
|
|
89
|
-
/>
|
|
90
|
-
<SegButton
|
|
91
|
-
key="Multi"
|
|
92
|
-
label="Multi"
|
|
93
|
-
on={feedbackTool.value === "multi"}
|
|
94
|
-
onClick={() => {
|
|
95
|
-
feedbackTool.value = "multi";
|
|
96
|
-
}}
|
|
97
|
-
/>
|
|
98
|
-
</div>
|
|
99
|
-
) : null}
|
|
100
|
-
{active && feedbackTool.value === "multi" && multi ? (
|
|
101
|
-
<button
|
|
102
|
-
key="commit"
|
|
103
|
-
class="cf-btn cf-btn-primary cf-commit"
|
|
104
|
-
onClick={commitMultiSelection}
|
|
105
|
-
>
|
|
106
|
-
{`Comment ${multi} element${multi === 1 ? "" : "s"}`}
|
|
107
|
-
</button>
|
|
108
|
-
) : null}
|
|
109
|
-
{active ? (
|
|
110
|
-
<div key="hint" class="cf-hint">
|
|
111
|
-
{hintFor(feedbackTool.value)}
|
|
112
|
-
</div>
|
|
113
|
-
) : null}
|
|
114
|
-
</div>
|
|
115
|
-
);
|
|
116
|
-
}
|