agentic-comments 0.6.1 → 0.7.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/README.md +48 -2
- package/SKILL.md +5 -0
- package/dist/{chunk-VAVKBAHM.js → chunk-6MS4LASK.js} +432 -432
- package/dist/{chunk-T77DPNIV.js → chunk-B7LTOLUO.js} +1 -1
- package/dist/{chunk-H6JK62SP.js → chunk-EFO5PSJL.js} +1 -1
- package/dist/{chunk-6N53KQVU.js → chunk-M77Q5CWI.js} +1 -1
- package/dist/{chunk-UNJL3N2L.js → chunk-OV2GGL3T.js} +3 -3
- package/dist/chunk-UEQL27KF.js +148 -0
- package/dist/cli.js +7 -7
- package/dist/client.js +4 -143
- package/dist/index.js +2 -2
- package/dist/local.js +3 -3
- package/dist/mcp-LP3X55C2.js +11 -0
- package/dist/mcp-bin.js +9 -9
- package/dist/ui.css +54 -0
- package/dist/ui.d.ts +46 -0
- package/dist/ui.js +332 -0
- package/dist/worker.js +2 -2
- package/llms.txt +1 -0
- package/package.json +35 -7
- package/dist/mcp-WBAHKMF4.js +0 -11
package/dist/ui.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createClient
|
|
3
|
+
} from "./chunk-UEQL27KF.js";
|
|
4
|
+
|
|
5
|
+
// src/ui/panel.tsx
|
|
6
|
+
import { Component, createContext, useCallback, useContext, useEffect, useId, useRef, useState } from "react";
|
|
7
|
+
import { AgentBadge, Avatar, Button, Input, Textarea } from "agentic-design/react";
|
|
8
|
+
|
|
9
|
+
// src/ui/rules.ts
|
|
10
|
+
var LEVELS = ["comments:read", "comments:comment", "comments:write", "comments:admin"];
|
|
11
|
+
var level = (scopes) => Math.max(-1, ...scopes.map((s) => LEVELS.indexOf(s)));
|
|
12
|
+
function holds(me, need) {
|
|
13
|
+
return me !== void 0 && level(me.scopes) >= LEVELS.indexOf(`comments:${need}`);
|
|
14
|
+
}
|
|
15
|
+
function mayDecide(me, suggestion) {
|
|
16
|
+
if (suggestion.status !== "pending") return { allowed: false };
|
|
17
|
+
if (!me) return { allowed: false, reason: "Sign in to decide this suggestion." };
|
|
18
|
+
const agent = me.kind !== "human";
|
|
19
|
+
if (agent ? level(me.scopes) < LEVELS.indexOf("comments:admin") && !me.scopes.includes("comments:accept") : !holds(me, "write")) {
|
|
20
|
+
return { allowed: false, reason: agent ? "An agent needs the accept right to decide a suggestion. A person can." : "Your access cannot accept or reject suggestions." };
|
|
21
|
+
}
|
|
22
|
+
const { author } = suggestion;
|
|
23
|
+
if (agent && author.kind !== "human" && author.name === me.name) {
|
|
24
|
+
return { allowed: false, reason: "An agent cannot accept or reject its own suggestion. A person or another agent decides." };
|
|
25
|
+
}
|
|
26
|
+
return { allowed: true };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/ui/panel.tsx
|
|
30
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
31
|
+
var VIEWS = [
|
|
32
|
+
{ id: "open", label: "Open" },
|
|
33
|
+
{ id: "resolved", label: "Resolved" },
|
|
34
|
+
{ id: "all", label: "All" }
|
|
35
|
+
];
|
|
36
|
+
var Ctx = createContext(void 0);
|
|
37
|
+
function useCtx() {
|
|
38
|
+
const value = useContext(Ctx);
|
|
39
|
+
if (!value) throw new Error("A panel card needs a CommentsPanel above it.");
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
var when = new Intl.DateTimeFormat("en", { dateStyle: "medium", timeStyle: "short", timeZone: "UTC" });
|
|
43
|
+
var stamp = (iso) => {
|
|
44
|
+
const date = new Date(iso);
|
|
45
|
+
return Number.isNaN(date.getTime()) ? iso : `${when.format(date)} UTC`;
|
|
46
|
+
};
|
|
47
|
+
var only = (list) => Array.isArray(list) ? list.filter((x) => typeof x === "object" && x !== null) : [];
|
|
48
|
+
function failure(error) {
|
|
49
|
+
const code = typeof error?.code === "string" ? `${error.code}: ` : "";
|
|
50
|
+
return `${code}${error instanceof Error ? error.message : String(error)}`;
|
|
51
|
+
}
|
|
52
|
+
function Who({ author, agentLabel }) {
|
|
53
|
+
const { actingFor } = useContext(Ctx) ?? {};
|
|
54
|
+
if (author.kind === "human") {
|
|
55
|
+
return /* @__PURE__ */ jsxs("span", { className: "agd-comments__who", children: [
|
|
56
|
+
/* @__PURE__ */ jsx(Avatar, { entry: { id: `person-${author.name}`, name: author.name, kind: "person" } }),
|
|
57
|
+
/* @__PURE__ */ jsx("strong", { className: "agd-comments__name", children: author.name })
|
|
58
|
+
] });
|
|
59
|
+
}
|
|
60
|
+
return /* @__PURE__ */ jsx(AgentBadge, { name: author.name, label: agentLabel, actingFor: actingFor?.(author) });
|
|
61
|
+
}
|
|
62
|
+
function anchorLabel(anchor) {
|
|
63
|
+
switch (anchor.type) {
|
|
64
|
+
case "text":
|
|
65
|
+
return "Text";
|
|
66
|
+
case "cell":
|
|
67
|
+
return `Cell ${anchor.key}`;
|
|
68
|
+
case "slide":
|
|
69
|
+
return `Slide element ${anchor.elementId}`;
|
|
70
|
+
case "canvas":
|
|
71
|
+
return `Canvas element ${anchor.elementId}`;
|
|
72
|
+
default:
|
|
73
|
+
return "Anchor";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function AnchorNote({ state }) {
|
|
77
|
+
if (state.state === "attached") return null;
|
|
78
|
+
const text = state.state === "orphaned" ? "The anchored content is gone. You can still read and reply." : state.state === "edited" ? `The text changed since this comment. It now reads: \u201C${state.currentText}\u201D` : "The panel cannot tell if the content is still there.";
|
|
79
|
+
return /* @__PURE__ */ jsx("p", { className: `agd-comments__state agd-comments__state--${state.state}`, children: text });
|
|
80
|
+
}
|
|
81
|
+
function Quote({ anchor }) {
|
|
82
|
+
return /* @__PURE__ */ jsxs("figure", { className: "agd-comments__anchor", children: [
|
|
83
|
+
/* @__PURE__ */ jsx("figcaption", { className: "agd-comments__label", children: anchorLabel(anchor) }),
|
|
84
|
+
anchor.type === "text" && /* @__PURE__ */ jsx("blockquote", { className: "agd-comments__quote", children: anchor.text })
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
87
|
+
function Body({ message }) {
|
|
88
|
+
return /* @__PURE__ */ jsxs("li", { className: "agd-comments__message", children: [
|
|
89
|
+
/* @__PURE__ */ jsxs("div", { className: "agd-comments__meta", children: [
|
|
90
|
+
/* @__PURE__ */ jsx(Who, { author: message.author, agentLabel: "Comment by an agent" }),
|
|
91
|
+
/* @__PURE__ */ jsx("time", { className: "agd-comments__time", dateTime: message.at, children: stamp(message.at) }),
|
|
92
|
+
message.editedAt && /* @__PURE__ */ jsx("span", { className: "agd-comments__time", children: "edited" })
|
|
93
|
+
] }),
|
|
94
|
+
/* @__PURE__ */ jsx("p", { className: "agd-comments__body", children: message.body })
|
|
95
|
+
] });
|
|
96
|
+
}
|
|
97
|
+
function changeText(change) {
|
|
98
|
+
if (typeof change === "object" && change !== null && "replace" in change && typeof change.replace === "string") return change.replace;
|
|
99
|
+
try {
|
|
100
|
+
return JSON.stringify(change) ?? "";
|
|
101
|
+
} catch {
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
var Status = ({ value }) => /* @__PURE__ */ jsx("span", { className: `agd-pill agd-comments__status agd-comments__status--${value}`, children: value[0].toUpperCase() + value.slice(1) });
|
|
106
|
+
function ThreadCard({ thread }) {
|
|
107
|
+
const { client, docId, me, busy, act } = useCtx();
|
|
108
|
+
const id = useId();
|
|
109
|
+
const [draft, setDraft] = useState("");
|
|
110
|
+
const resolved = thread.status === "resolved";
|
|
111
|
+
const mayComment = holds(me, "comment");
|
|
112
|
+
const submit = (event) => {
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
const body = draft.trim();
|
|
115
|
+
if (!body) return;
|
|
116
|
+
void act(
|
|
117
|
+
`${thread.id}:reply`,
|
|
118
|
+
async () => {
|
|
119
|
+
await client.reply(docId, thread.id, { body });
|
|
120
|
+
setDraft("");
|
|
121
|
+
},
|
|
122
|
+
"Reply sent."
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
return /* @__PURE__ */ jsx("li", { className: "agd-comments__thread", children: /* @__PURE__ */ jsxs("article", { "aria-labelledby": `${id}-h`, children: [
|
|
126
|
+
/* @__PURE__ */ jsxs("header", { className: "agd-comments__item-top", children: [
|
|
127
|
+
/* @__PURE__ */ jsxs("h3", { className: "agd-comments__item-title", id: `${id}-h`, children: [
|
|
128
|
+
anchorLabel(thread.anchor),
|
|
129
|
+
" ",
|
|
130
|
+
/* @__PURE__ */ jsx(Status, { value: thread.status })
|
|
131
|
+
] }),
|
|
132
|
+
mayComment && /* @__PURE__ */ jsxs(Button, { size: "sm", loading: busy === `${thread.id}:status`, onClick: () => void act(`${thread.id}:status`, () => resolved ? client.reopen(docId, thread.id) : client.resolve(docId, thread.id), resolved ? "Thread reopened." : "Thread resolved.", "threads"), children: [
|
|
133
|
+
resolved ? "Reopen" : "Resolve",
|
|
134
|
+
/* @__PURE__ */ jsxs("span", { className: "agd-sr-only", children: [
|
|
135
|
+
" thread on ",
|
|
136
|
+
anchorLabel(thread.anchor)
|
|
137
|
+
] })
|
|
138
|
+
] })
|
|
139
|
+
] }),
|
|
140
|
+
/* @__PURE__ */ jsx(Quote, { anchor: thread.anchor }),
|
|
141
|
+
/* @__PURE__ */ jsx(AnchorNote, { state: thread.anchorState }),
|
|
142
|
+
/* @__PURE__ */ jsx("ul", { className: "agd-comments__messages", "aria-label": "Messages", children: thread.messages.map((m) => /* @__PURE__ */ jsx(Body, { message: m }, m.id)) }),
|
|
143
|
+
resolved ? /* @__PURE__ */ jsx("p", { className: "agd-comments__note", children: "Reopen this thread to reply." }) : mayComment ? /* @__PURE__ */ jsxs("form", { className: "agd-comments__reply", onSubmit: submit, children: [
|
|
144
|
+
/* @__PURE__ */ jsx(Textarea, { label: "Reply", rows: 2, value: draft, onChange: (e) => setDraft(e.target.value), maxLength: 1e4 }),
|
|
145
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", variant: "primary", size: "sm", loading: busy === `${thread.id}:reply`, children: "Reply" })
|
|
146
|
+
] }) : null
|
|
147
|
+
] }) });
|
|
148
|
+
}
|
|
149
|
+
function SuggestionCard({ suggestion }) {
|
|
150
|
+
const { client, docId, me, busy, act } = useCtx();
|
|
151
|
+
const decision = mayDecide(me, suggestion);
|
|
152
|
+
const usable = suggestion.anchorState.state === "attached";
|
|
153
|
+
const pending = suggestion.status === "pending";
|
|
154
|
+
return /* @__PURE__ */ jsxs("li", { className: "agd-comments__thread", children: [
|
|
155
|
+
/* @__PURE__ */ jsxs("div", { className: "agd-comments__meta", children: [
|
|
156
|
+
/* @__PURE__ */ jsx(Who, { author: suggestion.author, agentLabel: "Suggested by an agent" }),
|
|
157
|
+
/* @__PURE__ */ jsx("time", { className: "agd-comments__time", dateTime: suggestion.createdAt, children: stamp(suggestion.createdAt) }),
|
|
158
|
+
/* @__PURE__ */ jsx(Status, { value: suggestion.status })
|
|
159
|
+
] }),
|
|
160
|
+
/* @__PURE__ */ jsx(Quote, { anchor: suggestion.anchor }),
|
|
161
|
+
suggestion.summary && /* @__PURE__ */ jsx("p", { className: "agd-comments__body", children: suggestion.summary }),
|
|
162
|
+
/* @__PURE__ */ jsxs("p", { className: "agd-comments__change", children: [
|
|
163
|
+
/* @__PURE__ */ jsx("span", { className: "agd-comments__label", children: "Proposed change" }),
|
|
164
|
+
/* @__PURE__ */ jsx("code", { className: "agd-comments__code", children: changeText(suggestion.change) })
|
|
165
|
+
] }),
|
|
166
|
+
pending && /* @__PURE__ */ jsx(AnchorNote, { state: suggestion.anchorState }),
|
|
167
|
+
suggestion.decidedBy && /* @__PURE__ */ jsxs("p", { className: "agd-comments__note", children: [
|
|
168
|
+
suggestion.status === "accepted" ? "Accepted" : "Rejected",
|
|
169
|
+
" by ",
|
|
170
|
+
suggestion.decidedBy.kind === "human" ? "" : "an agent, ",
|
|
171
|
+
suggestion.decidedBy.name,
|
|
172
|
+
"."
|
|
173
|
+
] }),
|
|
174
|
+
pending && decision.allowed && /* @__PURE__ */ jsxs("div", { className: "agd-comments__actions", children: [
|
|
175
|
+
/* @__PURE__ */ jsxs(Button, { variant: "primary", size: "sm", loading: busy === `${suggestion.id}:accept`, disabled: !usable, onClick: () => void act(`${suggestion.id}:accept`, () => client.accept(docId, suggestion.id), "Suggestion accepted.", "suggestions"), children: [
|
|
176
|
+
"Accept",
|
|
177
|
+
/* @__PURE__ */ jsx("span", { className: "agd-sr-only", children: " suggestion" })
|
|
178
|
+
] }),
|
|
179
|
+
/* @__PURE__ */ jsxs(Button, { size: "sm", loading: busy === `${suggestion.id}:reject`, onClick: () => void act(`${suggestion.id}:reject`, () => client.reject(docId, suggestion.id), "Suggestion rejected.", "suggestions"), children: [
|
|
180
|
+
"Reject",
|
|
181
|
+
/* @__PURE__ */ jsx("span", { className: "agd-sr-only", children: " suggestion" })
|
|
182
|
+
] })
|
|
183
|
+
] }),
|
|
184
|
+
pending && decision.allowed && !usable && /* @__PURE__ */ jsx("p", { className: "agd-comments__note", children: suggestion.anchorState.state === "unknown" ? "The panel cannot tell if the anchored text is still there, so Accept is off. You can reject this suggestion." : "The anchored text changed or is gone. You can reject this suggestion, and you cannot accept it." }),
|
|
185
|
+
pending && !decision.allowed && decision.reason && /* @__PURE__ */ jsx("p", { className: "agd-comments__note", children: decision.reason })
|
|
186
|
+
] });
|
|
187
|
+
}
|
|
188
|
+
var CardBoundary = class extends Component {
|
|
189
|
+
state = { bad: false, seen: this.props.item };
|
|
190
|
+
static getDerivedStateFromError() {
|
|
191
|
+
return { bad: true };
|
|
192
|
+
}
|
|
193
|
+
// A read gives new objects. When the item is not the one that failed, the card gets another try.
|
|
194
|
+
static getDerivedStateFromProps(props, state) {
|
|
195
|
+
return props.item === state.seen ? null : { bad: false, seen: props.item };
|
|
196
|
+
}
|
|
197
|
+
render() {
|
|
198
|
+
return this.state.bad ? /* @__PURE__ */ jsx("li", { className: "agd-comments__thread agd-comments__note", children: "This item has bad data, so the panel does not show it." }) : this.props.children;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
function Section({ id, title, count, children }) {
|
|
202
|
+
return /* @__PURE__ */ jsxs("section", { className: "agd-comments__section", "aria-labelledby": id, children: [
|
|
203
|
+
/* @__PURE__ */ jsxs("h2", { className: "agd-comments__heading", id, tabIndex: -1, children: [
|
|
204
|
+
title,
|
|
205
|
+
" ",
|
|
206
|
+
/* @__PURE__ */ jsx("span", { className: "agd-comments__count", children: count })
|
|
207
|
+
] }),
|
|
208
|
+
children
|
|
209
|
+
] });
|
|
210
|
+
}
|
|
211
|
+
function CommentsPanel({ client, docId, title = "Comments", newThread = true, actingFor }) {
|
|
212
|
+
const base = useId();
|
|
213
|
+
const [notice, setNotice] = useState("");
|
|
214
|
+
const [me, setMe] = useState();
|
|
215
|
+
const [threads, setThreads] = useState();
|
|
216
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
217
|
+
const [view, setView] = useState("open");
|
|
218
|
+
const [error, setError] = useState();
|
|
219
|
+
const [busy, setBusy] = useState();
|
|
220
|
+
const [quote, setQuote] = useState("");
|
|
221
|
+
const [body, setBody] = useState("");
|
|
222
|
+
const run = useRef(0);
|
|
223
|
+
const writing = useRef(false);
|
|
224
|
+
const load = useCallback(
|
|
225
|
+
async (gen) => {
|
|
226
|
+
const [m, t, s] = await Promise.all([client.me(), client.listThreads(docId), client.listSuggestions(docId)]);
|
|
227
|
+
if (gen !== run.current) return;
|
|
228
|
+
if (!(typeof m === "object" && m !== null && typeof m.kind === "string" && typeof m.name === "string" && Array.isArray(m.scopes) && m.scopes.every((x) => typeof x === "string"))) {
|
|
229
|
+
throw new Error("the client returned a principal with no kind, name, or scopes");
|
|
230
|
+
}
|
|
231
|
+
setMe(m);
|
|
232
|
+
setThreads(only(t));
|
|
233
|
+
setSuggestions(only(s));
|
|
234
|
+
},
|
|
235
|
+
[client, docId]
|
|
236
|
+
);
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
const gen = ++run.current;
|
|
239
|
+
writing.current = false;
|
|
240
|
+
setMe(void 0);
|
|
241
|
+
setThreads(void 0);
|
|
242
|
+
setSuggestions([]);
|
|
243
|
+
setBusy(void 0);
|
|
244
|
+
setError(void 0);
|
|
245
|
+
setNotice("");
|
|
246
|
+
setQuote("");
|
|
247
|
+
setBody("");
|
|
248
|
+
setView("open");
|
|
249
|
+
load(gen).catch((e) => gen === run.current && setError(failure(e)));
|
|
250
|
+
return () => {
|
|
251
|
+
run.current += 1;
|
|
252
|
+
};
|
|
253
|
+
}, [load]);
|
|
254
|
+
const act = async (key, call, done, focus) => {
|
|
255
|
+
if (writing.current) {
|
|
256
|
+
setNotice("Wait for the current action to finish, then try again.");
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
writing.current = true;
|
|
260
|
+
const gen = run.current;
|
|
261
|
+
const current = () => gen === run.current;
|
|
262
|
+
const refocus = () => focus && requestAnimationFrame(() => current() && document.getElementById(`${base}-${focus}`)?.focus());
|
|
263
|
+
setBusy(key);
|
|
264
|
+
setError(void 0);
|
|
265
|
+
setNotice("");
|
|
266
|
+
try {
|
|
267
|
+
await call();
|
|
268
|
+
await load(gen);
|
|
269
|
+
if (current()) {
|
|
270
|
+
if (done) setNotice(done);
|
|
271
|
+
refocus();
|
|
272
|
+
}
|
|
273
|
+
} catch (e) {
|
|
274
|
+
if (current()) setError(failure(e));
|
|
275
|
+
await load(gen).catch(() => void 0);
|
|
276
|
+
if (current()) refocus();
|
|
277
|
+
} finally {
|
|
278
|
+
if (current()) {
|
|
279
|
+
writing.current = false;
|
|
280
|
+
setBusy(void 0);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
const start = (event) => {
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
const q = quote.trim();
|
|
287
|
+
const b = body.trim();
|
|
288
|
+
if (!q || !b) return;
|
|
289
|
+
const gen = run.current;
|
|
290
|
+
void act(
|
|
291
|
+
"new",
|
|
292
|
+
async () => {
|
|
293
|
+
await client.addThread(docId, { anchor: { type: "text", quote: q }, body: b });
|
|
294
|
+
if (gen === run.current) {
|
|
295
|
+
setQuote("");
|
|
296
|
+
setBody("");
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
"Thread added."
|
|
300
|
+
);
|
|
301
|
+
};
|
|
302
|
+
const shown = (threads ?? []).filter((t) => view === "all" || t.status === view);
|
|
303
|
+
const pending = suggestions.filter((s) => s.status === "pending");
|
|
304
|
+
const decided = suggestions.filter((s) => s.status !== "pending");
|
|
305
|
+
return /* @__PURE__ */ jsx(Ctx.Provider, { value: { client, docId, me, busy, act, actingFor }, children: /* @__PURE__ */ jsxs("div", { className: "agd-comments", children: [
|
|
306
|
+
/* @__PURE__ */ jsxs("header", { className: "agd-comments__top", children: [
|
|
307
|
+
/* @__PURE__ */ jsx("h1", { className: "agd-comments__title", children: title }),
|
|
308
|
+
me && /* @__PURE__ */ jsxs("p", { className: "agd-comments__me", children: [
|
|
309
|
+
"Signed in as ",
|
|
310
|
+
/* @__PURE__ */ jsx(Who, { author: me, agentLabel: "Signed in as an agent" })
|
|
311
|
+
] })
|
|
312
|
+
] }),
|
|
313
|
+
error && /* @__PURE__ */ jsx("p", { className: "agd-comments__error", role: "alert", children: error }),
|
|
314
|
+
/* @__PURE__ */ jsx("p", { className: "agd-sr-only", role: "status", children: threads === void 0 && !error ? "Loading comments" : notice }),
|
|
315
|
+
newThread && holds(me, "comment") && /* @__PURE__ */ jsxs("form", { className: "agd-comments__new", onSubmit: start, children: [
|
|
316
|
+
/* @__PURE__ */ jsx(Input, { label: "Quote to comment on", value: quote, onChange: (e) => setQuote(e.target.value), hint: "Text that occurs once in the document.", maxLength: 2e3 }),
|
|
317
|
+
/* @__PURE__ */ jsx(Textarea, { label: "Comment", rows: 2, value: body, onChange: (e) => setBody(e.target.value), maxLength: 1e4 }),
|
|
318
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", variant: "primary", loading: busy === "new", children: "Add comment" })
|
|
319
|
+
] }),
|
|
320
|
+
/* @__PURE__ */ jsxs(Section, { id: `${base}-threads`, title: "Threads", count: shown.length, children: [
|
|
321
|
+
/* @__PURE__ */ jsx("div", { className: "agd-comments__views", role: "group", "aria-label": "Show threads", children: VIEWS.map((v) => /* @__PURE__ */ jsx(Button, { size: "sm", variant: view === v.id ? "primary" : "secondary", "aria-pressed": view === v.id, onClick: () => setView(v.id), children: v.label }, v.id)) }),
|
|
322
|
+
threads === void 0 ? null : shown.length === 0 ? /* @__PURE__ */ jsx("p", { className: "agd-comments__empty", children: view === "all" ? "No threads yet." : `No ${view} threads.` }) : /* @__PURE__ */ jsx("ul", { className: "agd-comments__list", children: shown.map((t) => /* @__PURE__ */ jsx(CardBoundary, { item: t, children: /* @__PURE__ */ jsx(ThreadCard, { thread: t }) }, t.id)) })
|
|
323
|
+
] }),
|
|
324
|
+
/* @__PURE__ */ jsx(Section, { id: `${base}-suggestions`, title: "Suggestions", count: pending.length, children: suggestions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "agd-comments__empty", children: "No suggestions." }) : /* @__PURE__ */ jsx("ul", { className: "agd-comments__list", children: [...pending, ...decided].map((s) => /* @__PURE__ */ jsx(CardBoundary, { item: s, children: /* @__PURE__ */ jsx(SuggestionCard, { suggestion: s }) }, s.id)) }) })
|
|
325
|
+
] }) });
|
|
326
|
+
}
|
|
327
|
+
export {
|
|
328
|
+
CommentsPanel,
|
|
329
|
+
createClient,
|
|
330
|
+
holds,
|
|
331
|
+
mayDecide
|
|
332
|
+
};
|
package/dist/worker.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CommentsService,
|
|
3
3
|
TEXT_ROOT
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-B7LTOLUO.js";
|
|
5
5
|
import {
|
|
6
6
|
ACCEPT_SCOPE,
|
|
7
7
|
CommentsError,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
exact,
|
|
10
10
|
invalid,
|
|
11
11
|
stamp
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6MS4LASK.js";
|
|
13
13
|
|
|
14
14
|
// src/worker.ts
|
|
15
15
|
import { Hono } from "hono";
|
package/llms.txt
CHANGED
|
@@ -8,6 +8,7 @@ Keywords: comments,threads,mentions,suggestions,annotations,collaboration,agents
|
|
|
8
8
|
- Plain text anchors: import { createAnchor, resolveAnchor, mapRange } from "agentic-comments/anchor". No Yjs. resolveAnchor maps through edits, then falls back to quote + context, else orphaned.
|
|
9
9
|
- Threads live on a Yjs document and merge across replicas.
|
|
10
10
|
- Lifecycle: comments.editMessage, deleteMessage, deleteThread, exportPerson, erasePerson (and the same on the CLI, MCP, HTTP API, and client). Only the author may change their own messages; comments:moderate may change anyone's.
|
|
11
|
+
- React panel: import { CommentsPanel, createClient, mayDecide } from "agentic-comments/ui" and "agentic-comments/ui.css" (with agentic-design/styles.css). Threads, quotes, replies, resolve and reopen, agent badge, accept and reject. An agent never decides its own suggestion. Peer: react, react-dom.
|
|
11
12
|
- Events: new Notifications(comments, suggestions).subscribe(listener). Ids only, never text.
|
|
12
13
|
- CLI: agentic-comments <command>. MCP: agentic-comments mcp. Both use a local store file.
|
|
13
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-comments",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Comment threads and suggestions anchored to any content, from people and agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"comments",
|
|
@@ -59,36 +59,64 @@
|
|
|
59
59
|
"./worker": {
|
|
60
60
|
"types": "./dist/worker.d.ts",
|
|
61
61
|
"import": "./dist/worker.js"
|
|
62
|
-
}
|
|
62
|
+
},
|
|
63
|
+
"./ui": {
|
|
64
|
+
"types": "./dist/ui.d.ts",
|
|
65
|
+
"import": "./dist/ui.js"
|
|
66
|
+
},
|
|
67
|
+
"./ui.css": "./dist/ui.css"
|
|
63
68
|
},
|
|
64
69
|
"bin": {
|
|
65
70
|
"agentic-comments": "./dist/cli.js",
|
|
66
71
|
"agentic-comments-mcp": "./dist/mcp-bin.js"
|
|
67
72
|
},
|
|
68
73
|
"scripts": {
|
|
69
|
-
"build": "tsup",
|
|
70
|
-
"typecheck": "tsc --noEmit",
|
|
74
|
+
"build": "tsup && node scripts/build-css.mjs",
|
|
75
|
+
"typecheck": "tsc --noEmit && tsc -p ui --noEmit",
|
|
71
76
|
"test": "vitest run",
|
|
72
|
-
"deploy:demo": "npm run build && WRANGLER_SEND_METRICS=false wrangler deploy --config wrangler.demo.jsonc",
|
|
77
|
+
"deploy:demo": "npm run build && npm run build:ui && WRANGLER_SEND_METRICS=false wrangler deploy --config wrangler.demo.jsonc",
|
|
73
78
|
"clean": "rm -rf dist",
|
|
79
|
+
"dev": "npm run build && WRANGLER_SEND_METRICS=false wrangler dev --port 8794",
|
|
80
|
+
"dev:smoke": "npm run build && node scripts/dev-smoke.mjs 8794 /health",
|
|
74
81
|
"prepack": "npm run build",
|
|
75
|
-
"ci:local": "npm run build && npm run typecheck && npm test"
|
|
82
|
+
"ci:local": "npm run build && npm run typecheck && npm test && npm run test:ui",
|
|
83
|
+
"build:ui": "node scripts/build-ui.mjs",
|
|
84
|
+
"test:ui": "npm run build && npm run build:ui && playwright test"
|
|
76
85
|
},
|
|
77
86
|
"dependencies": {
|
|
78
87
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
88
|
+
"agentic-design": "^0.5.0",
|
|
79
89
|
"agentic-identity": "^0.8.0",
|
|
80
90
|
"hono": "^4.13.14",
|
|
81
91
|
"zod": "^4.6.7"
|
|
82
92
|
},
|
|
83
93
|
"peerDependencies": {
|
|
94
|
+
"react": "^19.0.0",
|
|
95
|
+
"react-dom": "^19.0.0",
|
|
84
96
|
"yjs": "^13.6.0"
|
|
85
97
|
},
|
|
86
98
|
"devDependencies": {
|
|
99
|
+
"@axe-core/playwright": "^4.14.0",
|
|
100
|
+
"@playwright/test": "^1.64.1",
|
|
87
101
|
"@types/node": "^22.0.0",
|
|
102
|
+
"@types/react": "^19.3.0",
|
|
103
|
+
"@types/react-dom": "^19.3.0",
|
|
104
|
+
"esbuild": "^0.28.2",
|
|
88
105
|
"tsup": "^8.0.0",
|
|
89
106
|
"typescript": "^5.6.0",
|
|
90
107
|
"vitest": "^3.0.0",
|
|
91
108
|
"wrangler": "^4.155.0",
|
|
92
109
|
"yjs": "^13.6.0"
|
|
93
|
-
}
|
|
110
|
+
},
|
|
111
|
+
"peerDependenciesMeta": {
|
|
112
|
+
"react": {
|
|
113
|
+
"optional": true
|
|
114
|
+
},
|
|
115
|
+
"react-dom": {
|
|
116
|
+
"optional": true
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"sideEffects": [
|
|
120
|
+
"**/*.css"
|
|
121
|
+
]
|
|
94
122
|
}
|