@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.
@@ -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
- }
@@ -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
- }