@devaccel-labs/comment-service 0.5.3 → 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/dist/react.js +570 -170
- package/dist/types/react/client.d.ts +36 -3
- package/dist/types/react/details.d.ts +34 -0
- package/dist/types/react/handoff.d.ts +15 -0
- package/dist/types/react/icons.d.ts +3 -0
- package/dist/types/react/styles.d.ts +1 -1
- package/dist/types/react/table.d.ts +16 -0
- package/dist/types/react/toolbar.d.ts +6 -1
- package/package.json +3 -3
package/dist/react.js
CHANGED
|
@@ -206,11 +206,11 @@ function overlayFor(element) {
|
|
|
206
206
|
return { role, name: labelFor(node) || "dialog" };
|
|
207
207
|
}
|
|
208
208
|
function fiberNames(element) {
|
|
209
|
-
const
|
|
210
|
-
if (!
|
|
209
|
+
const key2 = Object.keys(element).find((name) => name.startsWith("__reactFiber$") || name.startsWith("__reactInternalInstance$"));
|
|
210
|
+
if (!key2)
|
|
211
211
|
return [];
|
|
212
212
|
const names2 = [];
|
|
213
|
-
let fiber = element[
|
|
213
|
+
let fiber = element[key2];
|
|
214
214
|
for (let guard = 0; fiber && guard < 40; guard++) {
|
|
215
215
|
const type = fiber.type;
|
|
216
216
|
if (typeof type === "function") {
|
|
@@ -267,7 +267,7 @@ function ownership(element) {
|
|
|
267
267
|
owner: null
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
|
-
function candidateFromElement(element, route,
|
|
270
|
+
function candidateFromElement(element, route, key2) {
|
|
271
271
|
const owned = ownership(element);
|
|
272
272
|
const label = labelFor(element);
|
|
273
273
|
const explicit = EXPLICIT.flatMap((attr) => {
|
|
@@ -275,7 +275,7 @@ function candidateFromElement(element, route, key) {
|
|
|
275
275
|
return value && usableExplicit(attr, value) ? [{ attr, value }] : [];
|
|
276
276
|
});
|
|
277
277
|
return {
|
|
278
|
-
key,
|
|
278
|
+
key: key2,
|
|
279
279
|
route,
|
|
280
280
|
visible: !obviouslyHidden(element),
|
|
281
281
|
path: domPath(element),
|
|
@@ -354,8 +354,8 @@ function describeElement(element) {
|
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
// ../react/dist/toolbar.js
|
|
357
|
-
import { jsx as
|
|
358
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as
|
|
357
|
+
import { jsx as _jsx6, jsxs as _jsxs6, Fragment as _Fragment3 } from "react/jsx-runtime";
|
|
358
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef3, useState as useState5 } from "react";
|
|
359
359
|
import { createPortal as createPortal2 } from "react-dom";
|
|
360
360
|
|
|
361
361
|
// ../react/dist/client.js
|
|
@@ -445,8 +445,14 @@ async function listMembers(session, memberToken) {
|
|
|
445
445
|
return payload.members;
|
|
446
446
|
}
|
|
447
447
|
async function listThreads(session, memberToken) {
|
|
448
|
-
|
|
449
|
-
|
|
448
|
+
return request(`${session.apiUrl}/v1/threads`, memberToken);
|
|
449
|
+
}
|
|
450
|
+
async function saveSettings(session, memberToken, settings) {
|
|
451
|
+
const payload = await request(`${session.apiUrl}/v1/settings`, memberToken, {
|
|
452
|
+
method: "PUT",
|
|
453
|
+
body: JSON.stringify({ settings })
|
|
454
|
+
});
|
|
455
|
+
return payload.settings;
|
|
450
456
|
}
|
|
451
457
|
async function createThread(session, memberToken, anchor, body, extras = {}) {
|
|
452
458
|
return request(`${session.apiUrl}/v1/threads`, memberToken, {
|
|
@@ -467,6 +473,120 @@ async function updateThread(session, memberToken, threadId, patch) {
|
|
|
467
473
|
});
|
|
468
474
|
}
|
|
469
475
|
|
|
476
|
+
// ../react/dist/details.js
|
|
477
|
+
import { jsx as _jsx2, jsxs as _jsxs2, Fragment as _Fragment } from "react/jsx-runtime";
|
|
478
|
+
import { useState } from "react";
|
|
479
|
+
|
|
480
|
+
// ../react/dist/icons.js
|
|
481
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
482
|
+
function Icon({ children, size = 16 }) {
|
|
483
|
+
return _jsx("svg", { className: "cs-icon", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children });
|
|
484
|
+
}
|
|
485
|
+
var AddComment = () => _jsxs(Icon, { size: 17, children: [_jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }), _jsx("path", { d: "M12 7v6M9 10h6" })] });
|
|
486
|
+
var Inbox = () => _jsxs(Icon, { size: 17, children: [_jsx("polyline", { points: "22 12 16 12 14 15 10 15 8 12 2 12" }), _jsx("path", { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" })] });
|
|
487
|
+
var Eye = () => _jsxs(Icon, { size: 17, children: [_jsx("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] });
|
|
488
|
+
var EyeOff = () => _jsxs(Icon, { size: 17, children: [_jsx("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), _jsx("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), _jsx("path", { d: "M6.61 6.61A13.53 13.53 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), _jsx("path", { d: "M2 2l20 20" })] });
|
|
489
|
+
var Lock = ({ size = 15 }) => _jsxs(Icon, { size, children: [_jsx("rect", { width: "16", height: "10", x: "4", y: "11", rx: "2" }), _jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })] });
|
|
490
|
+
var Close = () => _jsx(Icon, { size: 15, children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) });
|
|
491
|
+
var Send = () => _jsx(Icon, { size: 15, children: _jsx("path", { d: "m5 12 7-7 7 7M12 19V5" }) });
|
|
492
|
+
var Check = () => _jsx(Icon, { size: 14, children: _jsx("polyline", { points: "20 6 9 17 4 12" }) });
|
|
493
|
+
var Target = () => _jsxs(Icon, { size: 14, children: [_jsx("circle", { cx: "12", cy: "12", r: "8" }), _jsx("circle", { cx: "12", cy: "12", r: "2.5" })] });
|
|
494
|
+
var Relink = () => _jsx(Icon, { size: 14, children: _jsx("path", { d: "M9 17H7A5 5 0 0 1 7 7h2M15 7h2a5 5 0 1 1 0 10h-2M8 12h8" }) });
|
|
495
|
+
var Undo = () => _jsxs(Icon, { size: 14, children: [_jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }), _jsx("path", { d: "M3 3v5h5" })] });
|
|
496
|
+
var Pencil = () => _jsx(Icon, { size: 13, children: _jsx("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }) });
|
|
497
|
+
var Cursor = () => _jsx(Icon, { size: 14, children: _jsx("path", { d: "m4 4 7.07 17 2.51-7.39L21 11.07z" }) });
|
|
498
|
+
var Paperclip = () => _jsx(Icon, { size: 15, children: _jsx("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
|
|
499
|
+
var Camera = () => _jsxs(Icon, { size: 15, children: [_jsx("path", { d: "M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" }), _jsx("circle", { cx: "12", cy: "13", r: "3" })] });
|
|
500
|
+
var FileIcon = () => _jsxs(Icon, { size: 15, children: [_jsx("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }), _jsx("polyline", { points: "14 2 14 8 20 8" })] });
|
|
501
|
+
var Back = () => _jsx(Icon, { children: _jsx("path", { d: "m15 18-6-6 6-6" }) });
|
|
502
|
+
var Table = () => _jsxs(Icon, { children: [_jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }), _jsx("path", { d: "M3 10h18M3 15h18M9 10v10" })] });
|
|
503
|
+
var List = () => _jsx(Icon, { children: _jsx("path", { d: "M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01" }) });
|
|
504
|
+
|
|
505
|
+
// ../react/dist/details.js
|
|
506
|
+
var noDetails = { role: null, fields: {} };
|
|
507
|
+
function detailsEnabled(settings) {
|
|
508
|
+
if (!settings)
|
|
509
|
+
return false;
|
|
510
|
+
return settings.roles.enabled && settings.roles.options.length > 0 || settings.fields.enabled && settings.fields.items.length > 0;
|
|
511
|
+
}
|
|
512
|
+
function hasFieldValues(details) {
|
|
513
|
+
return Object.values(details.fields).some((value) => value.trim());
|
|
514
|
+
}
|
|
515
|
+
function detailsPayload(settings, details) {
|
|
516
|
+
if (!settings)
|
|
517
|
+
return {};
|
|
518
|
+
const payload = {};
|
|
519
|
+
if (settings.roles.enabled && details.role)
|
|
520
|
+
payload.role = details.role;
|
|
521
|
+
if (settings.fields.enabled) {
|
|
522
|
+
const fields = Object.fromEntries(Object.entries(details.fields).filter(([, value]) => value.trim()));
|
|
523
|
+
if (Object.keys(fields).length)
|
|
524
|
+
payload.fields = fields;
|
|
525
|
+
}
|
|
526
|
+
return payload;
|
|
527
|
+
}
|
|
528
|
+
function fieldId(label, taken) {
|
|
529
|
+
const base = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32) || "field";
|
|
530
|
+
let id = base;
|
|
531
|
+
for (let n = 2; taken.some((field) => field.id === id); n += 1)
|
|
532
|
+
id = `${base}-${n}`;
|
|
533
|
+
return id;
|
|
534
|
+
}
|
|
535
|
+
function DetailsInput({ settings, value, onChange, onKeyDown }) {
|
|
536
|
+
if (!settings || !detailsEnabled(settings))
|
|
537
|
+
return null;
|
|
538
|
+
const roles = settings.roles.enabled ? settings.roles.options : [];
|
|
539
|
+
const fields = settings.fields.enabled ? settings.fields.items : [];
|
|
540
|
+
return _jsxs2("div", { className: "cs-details-input", "data-cs-ui": "details-input", children: [fields.map((field) => _jsxs2("label", { className: "cs-detail-field", children: [_jsx2("span", { children: field.label }), _jsx2("textarea", { "data-cs-ui": `field-${field.id}`, rows: 1, maxLength: 4e3, value: value.fields[field.id] ?? "", onChange: (event) => onChange({ ...value, fields: { ...value.fields, [field.id]: event.target.value } }), onKeyDown })] }, field.id)), roles.length ? _jsxs2("div", { className: "cs-roles", role: "radiogroup", "aria-label": "Role", children: [_jsx2("span", { className: "cs-roles-label", children: "Role" }), roles.map((role) => _jsx2("button", { type: "button", role: "radio", "data-cs-ui": "role-option", "aria-checked": value.role === role, className: value.role === role ? "cs-chip is-on" : "cs-chip", onClick: () => onChange({ ...value, role: value.role === role ? null : role }), children: role }, role))] }) : null] });
|
|
541
|
+
}
|
|
542
|
+
function RoleBadge({ role }) {
|
|
543
|
+
return role ? _jsx2("span", { className: "cs-role", "data-cs-ui": "role", children: role }) : null;
|
|
544
|
+
}
|
|
545
|
+
function FieldValues({ fields, clamp = false }) {
|
|
546
|
+
if (!fields.length)
|
|
547
|
+
return null;
|
|
548
|
+
return _jsx2("dl", { className: clamp ? "cs-detail-list is-clamped" : "cs-detail-list", "data-cs-ui": "fields", children: fields.map((field) => _jsxs2("div", { children: [_jsx2("dt", { children: field.label }), _jsx2("dd", { children: field.value })] }, field.id)) });
|
|
549
|
+
}
|
|
550
|
+
function DetailsEditor({ settings, thread, onSave, onClose }) {
|
|
551
|
+
const [value, setValue] = useState(() => ({
|
|
552
|
+
role: thread.role,
|
|
553
|
+
fields: Object.fromEntries(thread.fields.map((field) => [field.id, field.value]))
|
|
554
|
+
}));
|
|
555
|
+
const [busy, setBusy] = useState(false);
|
|
556
|
+
return _jsxs2("form", { className: "cs-details-edit", "data-cs-ui": "details-editor", onSubmit: (event) => {
|
|
557
|
+
event.preventDefault();
|
|
558
|
+
const patch = {};
|
|
559
|
+
if (settings.roles.enabled && value.role !== thread.role)
|
|
560
|
+
patch.role = value.role;
|
|
561
|
+
if (settings.fields.enabled) {
|
|
562
|
+
patch.fields = Object.fromEntries(settings.fields.items.map((field) => [field.id, value.fields[field.id] ?? ""]));
|
|
563
|
+
}
|
|
564
|
+
setBusy(true);
|
|
565
|
+
onSave(patch).then(onClose).finally(() => setBusy(false));
|
|
566
|
+
}, children: [_jsx2(DetailsInput, { settings, value, onChange: setValue }), _jsxs2("div", { className: "cs-row-end", children: [_jsx2("button", { className: "cs-button", type: "button", onClick: onClose, children: "Cancel" }), _jsx2("button", { className: "cs-button is-primary", "data-cs-ui": "details-save", type: "submit", disabled: busy, children: "Save" })] })] });
|
|
567
|
+
}
|
|
568
|
+
function AddRow({ label, hook, onAdd }) {
|
|
569
|
+
const [text, setText] = useState("");
|
|
570
|
+
return _jsxs2("form", { className: "cs-add-row", onSubmit: (event) => {
|
|
571
|
+
event.preventDefault();
|
|
572
|
+
const next = text.trim().replace(/\s+/g, " ");
|
|
573
|
+
if (!next)
|
|
574
|
+
return;
|
|
575
|
+
onAdd(next);
|
|
576
|
+
setText("");
|
|
577
|
+
}, children: [_jsx2("input", { "data-cs-ui": hook, "aria-label": label, placeholder: label, value: text, maxLength: 60, onChange: (event) => setText(event.target.value) }), _jsx2("button", { className: "cs-icon-button", type: "submit", "aria-label": label, disabled: !text.trim(), children: _jsx2(Check, {}) })] });
|
|
578
|
+
}
|
|
579
|
+
function SettingsPanel({ settings, onChange, onBack, error }) {
|
|
580
|
+
return _jsxs2("section", { className: "cs-card cs-menu cs-settings cs-hit", "data-cs-ui": "settings", "aria-label": "Comment fields", children: [_jsxs2("header", { className: "cs-settings-head", children: [_jsx2("button", { className: "cs-icon-button", type: "button", "aria-label": "Back", onClick: onBack, children: _jsx2(Back, {}) }), _jsx2("h2", { children: "Comment fields" })] }), !settings ? _jsx2("p", { className: "cs-hint", children: "Loading\u2026" }) : _jsxs2(_Fragment, { children: [_jsxs2("button", { className: "cs-item", "data-cs-ui": "roles-toggle", type: "button", role: "switch", "aria-checked": settings.roles.enabled, onClick: () => onChange({ ...settings, roles: { ...settings.roles, enabled: !settings.roles.enabled } }), children: [_jsx2("span", { children: "Role" }), _jsx2("span", { className: settings.roles.enabled ? "cs-switch is-on" : "cs-switch" })] }), settings.roles.enabled ? _jsxs2("div", { className: "cs-settings-body", children: [settings.roles.options.length ? _jsx2("div", { className: "cs-chips", children: settings.roles.options.map((role) => _jsxs2("span", { className: "cs-chip is-static", "data-cs-ui": "role-chip", children: [role, _jsx2("button", { type: "button", "aria-label": `Remove ${role}`, onClick: () => onChange({ ...settings, roles: { ...settings.roles, options: settings.roles.options.filter((item) => item !== role) } }), children: _jsx2(Close, {}) })] }, role)) }) : null, _jsx2(AddRow, { label: "Add a role", hook: "role-add", onAdd: (role) => {
|
|
581
|
+
if (settings.roles.options.some((item) => item.toLowerCase() === role.toLowerCase()))
|
|
582
|
+
return;
|
|
583
|
+
onChange({ ...settings, roles: { ...settings.roles, options: [...settings.roles.options, role.slice(0, 40)] } });
|
|
584
|
+
} })] }) : null, _jsx2("div", { className: "cs-sep" }), _jsxs2("button", { className: "cs-item", "data-cs-ui": "fields-toggle", type: "button", role: "switch", "aria-checked": settings.fields.enabled, onClick: () => onChange({ ...settings, fields: { ...settings.fields, enabled: !settings.fields.enabled } }), children: [_jsx2("span", { children: "Custom fields" }), _jsx2("span", { className: settings.fields.enabled ? "cs-switch is-on" : "cs-switch" })] }), settings.fields.enabled ? _jsxs2("div", { className: "cs-settings-body", children: [settings.fields.items.map((field) => _jsxs2("div", { className: "cs-field-row", "data-cs-ui": "field-def", children: [_jsx2("span", { children: field.label }), _jsx2("button", { className: "cs-icon-button", type: "button", "aria-label": `Remove ${field.label}`, onClick: () => onChange({ ...settings, fields: { ...settings.fields, items: settings.fields.items.filter((item) => item.id !== field.id) } }), children: _jsx2(Close, {}) })] }, field.id)), _jsx2(AddRow, { label: "Add a field", hook: "field-add", onAdd: (label) => onChange({
|
|
585
|
+
...settings,
|
|
586
|
+
fields: { ...settings.fields, items: [...settings.fields.items, { id: fieldId(label, settings.fields.items), label }] }
|
|
587
|
+
}) })] }) : null, error ? _jsx2("p", { className: "cs-error cs-pad", children: error }) : null, _jsx2("p", { className: "cs-hint", children: "Shared with everyone here. Turning off or removing only affects new comments." })] })] });
|
|
588
|
+
}
|
|
589
|
+
|
|
470
590
|
// ../react/dist/format.js
|
|
471
591
|
var palette = ["#007aff", "#34c759", "#ff9500", "#af52de", "#ff2d55", "#5ac8fa", "#5856d6", "#a2845e"];
|
|
472
592
|
function initials(name) {
|
|
@@ -537,31 +657,9 @@ function deliveryText(delivery) {
|
|
|
537
657
|
return parts.length ? parts.join(" \xB7 ") : null;
|
|
538
658
|
}
|
|
539
659
|
|
|
540
|
-
// ../react/dist/icons.js
|
|
541
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
542
|
-
function Icon({ children, size = 16 }) {
|
|
543
|
-
return _jsx("svg", { className: "cs-icon", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children });
|
|
544
|
-
}
|
|
545
|
-
var AddComment = () => _jsxs(Icon, { size: 17, children: [_jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }), _jsx("path", { d: "M12 7v6M9 10h6" })] });
|
|
546
|
-
var Inbox = () => _jsxs(Icon, { size: 17, children: [_jsx("polyline", { points: "22 12 16 12 14 15 10 15 8 12 2 12" }), _jsx("path", { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" })] });
|
|
547
|
-
var Eye = () => _jsxs(Icon, { size: 17, children: [_jsx("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] });
|
|
548
|
-
var EyeOff = () => _jsxs(Icon, { size: 17, children: [_jsx("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), _jsx("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), _jsx("path", { d: "M6.61 6.61A13.53 13.53 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), _jsx("path", { d: "M2 2l20 20" })] });
|
|
549
|
-
var Lock = ({ size = 15 }) => _jsxs(Icon, { size, children: [_jsx("rect", { width: "16", height: "10", x: "4", y: "11", rx: "2" }), _jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })] });
|
|
550
|
-
var Close = () => _jsx(Icon, { size: 15, children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) });
|
|
551
|
-
var Send = () => _jsx(Icon, { size: 15, children: _jsx("path", { d: "m5 12 7-7 7 7M12 19V5" }) });
|
|
552
|
-
var Check = () => _jsx(Icon, { size: 14, children: _jsx("polyline", { points: "20 6 9 17 4 12" }) });
|
|
553
|
-
var Target = () => _jsxs(Icon, { size: 14, children: [_jsx("circle", { cx: "12", cy: "12", r: "8" }), _jsx("circle", { cx: "12", cy: "12", r: "2.5" })] });
|
|
554
|
-
var Relink = () => _jsx(Icon, { size: 14, children: _jsx("path", { d: "M9 17H7A5 5 0 0 1 7 7h2M15 7h2a5 5 0 1 1 0 10h-2M8 12h8" }) });
|
|
555
|
-
var Undo = () => _jsxs(Icon, { size: 14, children: [_jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }), _jsx("path", { d: "M3 3v5h5" })] });
|
|
556
|
-
var Pencil = () => _jsx(Icon, { size: 13, children: _jsx("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }) });
|
|
557
|
-
var Cursor = () => _jsx(Icon, { size: 14, children: _jsx("path", { d: "m4 4 7.07 17 2.51-7.39L21 11.07z" }) });
|
|
558
|
-
var Paperclip = () => _jsx(Icon, { size: 15, children: _jsx("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
|
|
559
|
-
var Camera = () => _jsxs(Icon, { size: 15, children: [_jsx("path", { d: "M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" }), _jsx("circle", { cx: "12", cy: "13", r: "3" })] });
|
|
560
|
-
var FileIcon = () => _jsxs(Icon, { size: 15, children: [_jsx("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }), _jsx("polyline", { points: "14 2 14 8 20 8" })] });
|
|
561
|
-
|
|
562
660
|
// ../react/dist/attachments.js
|
|
563
|
-
import { jsx as
|
|
564
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
661
|
+
import { jsx as _jsx3, jsxs as _jsxs3 } from "react/jsx-runtime";
|
|
662
|
+
import { useCallback, useEffect, useRef, useState as useState2 } from "react";
|
|
565
663
|
var MAX_BYTES = 10 * 1024 * 1024;
|
|
566
664
|
var MAX_FILES = 5;
|
|
567
665
|
var ACCEPT = "image/png,image/jpeg,image/gif,image/webp,application/pdf,text/plain,text/csv,application/json,application/zip,video/mp4,video/webm,video/quicktime";
|
|
@@ -574,10 +672,10 @@ function formatSize(bytes) {
|
|
|
574
672
|
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
575
673
|
}
|
|
576
674
|
function useAttachments(session, memberToken, onError) {
|
|
577
|
-
const [items, setItems] =
|
|
675
|
+
const [items, setItems] = useState2([]);
|
|
578
676
|
const itemsRef = useRef(items);
|
|
579
677
|
itemsRef.current = items;
|
|
580
|
-
const patch = (
|
|
678
|
+
const patch = (key2, next) => setItems((current) => current.map((item) => item.key === key2 ? { ...item, ...next } : item));
|
|
581
679
|
const add = useCallback((files) => {
|
|
582
680
|
if (!session || !memberToken)
|
|
583
681
|
return;
|
|
@@ -593,22 +691,22 @@ function useAttachments(session, memberToken, onError) {
|
|
|
593
691
|
onError(`${name} is over 10 MB.`);
|
|
594
692
|
continue;
|
|
595
693
|
}
|
|
596
|
-
const
|
|
694
|
+
const key2 = crypto.randomUUID();
|
|
597
695
|
const preview = blob.type.startsWith("image/") ? URL.createObjectURL(blob) : null;
|
|
598
|
-
setItems((current) => [...current, { key, name, size: blob.size, contentType: blob.type, preview, status: "uploading" }]);
|
|
599
|
-
uploadAttachment(session, memberToken, blob, name).then((attachment) => patch(
|
|
696
|
+
setItems((current) => [...current, { key: key2, name, size: blob.size, contentType: blob.type, preview, status: "uploading" }]);
|
|
697
|
+
uploadAttachment(session, memberToken, blob, name).then((attachment) => patch(key2, { status: "ready", id: attachment.id })).catch((caught) => {
|
|
600
698
|
const message = caught instanceof Error ? caught.message : "Upload failed.";
|
|
601
|
-
patch(
|
|
699
|
+
patch(key2, { status: "failed", error: message });
|
|
602
700
|
onError(`${name}: ${message}`);
|
|
603
701
|
});
|
|
604
702
|
}
|
|
605
703
|
}, [memberToken, onError, session]);
|
|
606
|
-
const remove = useCallback((
|
|
704
|
+
const remove = useCallback((key2) => {
|
|
607
705
|
setItems((current) => {
|
|
608
|
-
const item = current.find((candidate) => candidate.key ===
|
|
706
|
+
const item = current.find((candidate) => candidate.key === key2);
|
|
609
707
|
if (item?.preview)
|
|
610
708
|
URL.revokeObjectURL(item.preview);
|
|
611
|
-
return current.filter((candidate) => candidate.key !==
|
|
709
|
+
return current.filter((candidate) => candidate.key !== key2);
|
|
612
710
|
});
|
|
613
711
|
}, []);
|
|
614
712
|
const reset = useCallback(() => {
|
|
@@ -633,26 +731,26 @@ function filesFromClipboard(event) {
|
|
|
633
731
|
function AttachmentTray({ items, onRemove }) {
|
|
634
732
|
if (!items.length)
|
|
635
733
|
return null;
|
|
636
|
-
return
|
|
734
|
+
return _jsx3("div", { className: "cs-tray", "data-cs-ui": "attachment-tray", children: items.map((item) => _jsxs3("div", { className: `cs-pending is-${item.status}`, "data-cs-ui": "pending-file", "data-status": item.status, title: item.error ?? item.name, children: [item.preview ? _jsx3("img", { src: item.preview, alt: "" }) : _jsx3(FileIcon, {}), _jsx3("span", { className: "cs-pending-name", children: item.name }), _jsx3("button", { type: "button", className: "cs-pending-remove", "aria-label": `Remove ${item.name}`, onClick: () => onRemove(item.key), children: _jsx3(Close, {}) })] }, item.key)) });
|
|
637
735
|
}
|
|
638
736
|
function AttachButtons({ onFiles, onSnapshot, capturing }) {
|
|
639
737
|
const input = useRef(null);
|
|
640
|
-
return
|
|
738
|
+
return _jsxs3("span", { className: "cs-attach", children: [_jsx3("input", { ref: input, type: "file", multiple: true, accept: ACCEPT, hidden: true, "data-cs-ui": "file-input", onChange: (event) => {
|
|
641
739
|
onFiles([...event.target.files ?? []].map((file) => ({ blob: file, name: file.name })));
|
|
642
740
|
event.target.value = "";
|
|
643
|
-
} }),
|
|
741
|
+
} }), _jsx3("button", { type: "button", className: "cs-icon-button", "data-cs-ui": "attach", "aria-label": "Attach files", onClick: () => input.current?.click(), children: _jsx3(Paperclip, {}) }), _jsx3("button", { type: "button", className: "cs-icon-button", "data-cs-ui": "snapshot", "aria-label": "Attach a screenshot of the page", disabled: capturing, onClick: onSnapshot, children: _jsx3(Camera, {}) })] });
|
|
644
742
|
}
|
|
645
743
|
function MessageAttachments({ attachments }) {
|
|
646
744
|
if (!attachments?.length)
|
|
647
745
|
return null;
|
|
648
746
|
const images = attachments.filter((file) => file.contentType.startsWith("image/"));
|
|
649
747
|
const others = attachments.filter((file) => !file.contentType.startsWith("image/"));
|
|
650
|
-
return
|
|
748
|
+
return _jsxs3("div", { className: "cs-files", "data-cs-ui": "attachments", children: [images.length ? _jsx3("div", { className: "cs-thumbs", children: images.map((file) => _jsx3("a", { href: file.url, target: "_blank", rel: "noreferrer noopener", className: "cs-thumb", title: file.name, "data-cs-ui": "attachment", children: _jsx3("img", { src: file.url, alt: file.name, loading: "lazy" }) }, file.id)) }) : null, others.map((file) => _jsxs3("a", { href: file.url, target: "_blank", rel: "noreferrer noopener", className: "cs-file", "data-cs-ui": "attachment", children: [_jsx3(FileIcon, {}), _jsx3("span", { className: "cs-file-name", children: file.name }), _jsx3("span", { className: "cs-file-size", children: formatSize(file.size) })] }, file.id))] });
|
|
651
749
|
}
|
|
652
750
|
|
|
653
751
|
// ../react/dist/mentions.js
|
|
654
|
-
import { Fragment as
|
|
655
|
-
import { useMemo, useRef as useRef2, useState as
|
|
752
|
+
import { Fragment as _Fragment2, jsx as _jsx4, jsxs as _jsxs4 } from "react/jsx-runtime";
|
|
753
|
+
import { useMemo, useRef as useRef2, useState as useState3 } from "react";
|
|
656
754
|
import { createPortal } from "react-dom";
|
|
657
755
|
function queryAt(text, caret) {
|
|
658
756
|
const match = text.slice(0, caret).match(/(^|\s)@([^\s@]{0,30})$/);
|
|
@@ -666,16 +764,16 @@ function mentionsIn(text, picked) {
|
|
|
666
764
|
}
|
|
667
765
|
function MessageText({ body, mentions }) {
|
|
668
766
|
if (!mentions?.length)
|
|
669
|
-
return
|
|
767
|
+
return _jsx4(_Fragment2, { children: body });
|
|
670
768
|
const pattern = new RegExp(`(@(?:${mentions.map((mention) => escapeRegex(mention.name)).join("|")}))`, "g");
|
|
671
|
-
const parts = body.split(pattern).map((part, index) => index % 2 === 1 ?
|
|
672
|
-
return
|
|
769
|
+
const parts = body.split(pattern).map((part, index) => index % 2 === 1 ? _jsx4("span", { className: "cs-mention", children: part }, index) : part);
|
|
770
|
+
return _jsx4(_Fragment2, { children: parts });
|
|
673
771
|
}
|
|
674
772
|
function MentionTextarea({ value, onValueChange, members, selfEmail, onMention, onKeyDown, ...rest }) {
|
|
675
773
|
const ref = useRef2(null);
|
|
676
|
-
const [caret, setCaret] =
|
|
677
|
-
const [index, setIndex] =
|
|
678
|
-
const [dismissed, setDismissed] =
|
|
774
|
+
const [caret, setCaret] = useState3(0);
|
|
775
|
+
const [index, setIndex] = useState3(0);
|
|
776
|
+
const [dismissed, setDismissed] = useState3(null);
|
|
679
777
|
const active = queryAt(value, caret);
|
|
680
778
|
const query = active?.query.toLowerCase() ?? null;
|
|
681
779
|
const matches = useMemo(() => {
|
|
@@ -702,7 +800,7 @@ function MentionTextarea({ value, onValueChange, members, selfEmail, onMention,
|
|
|
702
800
|
const rect = open ? ref.current?.getBoundingClientRect() : null;
|
|
703
801
|
const root = open ? ref.current?.closest("#cs-root") : null;
|
|
704
802
|
const below = rect ? window.innerHeight - rect.bottom > 220 : true;
|
|
705
|
-
return
|
|
803
|
+
return _jsxs4(_Fragment2, { children: [_jsx4("textarea", { ref, ...rest, value, "aria-autocomplete": "list", "aria-expanded": open, onChange: (event) => {
|
|
706
804
|
onValueChange(event.target.value);
|
|
707
805
|
setCaret(event.target.selectionStart ?? event.target.value.length);
|
|
708
806
|
setIndex(0);
|
|
@@ -728,13 +826,13 @@ function MentionTextarea({ value, onValueChange, members, selfEmail, onMention,
|
|
|
728
826
|
}
|
|
729
827
|
}
|
|
730
828
|
onKeyDown?.(event);
|
|
731
|
-
} }), open && rect && root ? createPortal(
|
|
829
|
+
} }), open && rect && root ? createPortal(_jsx4("div", { className: "cs-people cs-hit", role: "listbox", "data-cs-ui": "mention-list", style: {
|
|
732
830
|
left: Math.min(rect.left, window.innerWidth - 272),
|
|
733
831
|
...below ? { top: rect.bottom + 6 } : { bottom: window.innerHeight - rect.top + 6 }
|
|
734
|
-
}, children: matches.map((member) =>
|
|
832
|
+
}, children: matches.map((member) => _jsxs4("div", { role: "option", "aria-selected": member === selected, "data-cs-ui": "mention-option", className: member === selected ? "cs-person is-on" : "cs-person", onMouseDown: (event) => {
|
|
735
833
|
event.preventDefault();
|
|
736
834
|
insert(member);
|
|
737
|
-
}, onMouseEnter: () => setIndex(matches.indexOf(member)), children: [
|
|
835
|
+
}, onMouseEnter: () => setIndex(matches.indexOf(member)), children: [_jsx4("span", { className: "cs-avatar", style: { width: 24, height: 24, fontSize: 10, background: colorFor(member.name) }, children: initials(member.name) }), _jsxs4("span", { className: "cs-person-text", children: [_jsx4("strong", { children: member.name }), _jsx4("small", { children: member.email })] })] }, member.id)) }), root) : null] });
|
|
738
836
|
}
|
|
739
837
|
|
|
740
838
|
// ../../node_modules/modern-screenshot/dist/index.mjs
|
|
@@ -1050,14 +1148,14 @@ async function createContext(node, options) {
|
|
|
1050
1148
|
drawImageCount: 0,
|
|
1051
1149
|
tasks: [],
|
|
1052
1150
|
features,
|
|
1053
|
-
isEnable: (
|
|
1054
|
-
if (
|
|
1055
|
-
return typeof features === "boolean" ? false : features[
|
|
1151
|
+
isEnable: (key2) => {
|
|
1152
|
+
if (key2 === "restoreScrollPosition") {
|
|
1153
|
+
return typeof features === "boolean" ? false : features[key2] ?? false;
|
|
1056
1154
|
}
|
|
1057
1155
|
if (typeof features === "boolean") {
|
|
1058
1156
|
return features;
|
|
1059
1157
|
}
|
|
1060
|
-
return features[
|
|
1158
|
+
return features[key2] ?? true;
|
|
1061
1159
|
},
|
|
1062
1160
|
shadowRoots: []
|
|
1063
1161
|
};
|
|
@@ -1298,14 +1396,14 @@ function getDefaultStyle(node, pseudoElement, context) {
|
|
|
1298
1396
|
const nodeName = node.nodeName.toLowerCase();
|
|
1299
1397
|
const isSvgNode = isSVGElementNode(node) && nodeName !== "svg";
|
|
1300
1398
|
const attributes = isSvgNode ? includedAttributes.map((name) => [name, node.getAttribute(name)]).filter(([, value]) => value !== null) : [];
|
|
1301
|
-
const
|
|
1399
|
+
const key2 = [
|
|
1302
1400
|
isSvgNode && "svg",
|
|
1303
1401
|
nodeName,
|
|
1304
1402
|
attributes.map((name, value) => `${name}=${value}`).join(","),
|
|
1305
1403
|
pseudoElement
|
|
1306
1404
|
].filter(Boolean).join(":");
|
|
1307
|
-
if (defaultComputedStyles.has(
|
|
1308
|
-
return defaultComputedStyles.get(
|
|
1405
|
+
if (defaultComputedStyles.has(key2))
|
|
1406
|
+
return defaultComputedStyles.get(key2);
|
|
1309
1407
|
const sandbox = getSandBox(context);
|
|
1310
1408
|
const sandboxWindow = sandbox?.contentWindow;
|
|
1311
1409
|
if (!sandboxWindow)
|
|
@@ -1334,7 +1432,7 @@ function getDefaultStyle(node, pseudoElement, context) {
|
|
|
1334
1432
|
styles.set(name, computedStyle.getPropertyValue(name));
|
|
1335
1433
|
}
|
|
1336
1434
|
sandboxDocument.body.removeChild(root);
|
|
1337
|
-
defaultComputedStyles.set(
|
|
1435
|
+
defaultComputedStyles.set(key2, styles);
|
|
1338
1436
|
return styles;
|
|
1339
1437
|
}
|
|
1340
1438
|
function getDiffStyle(style, defaultStyle, includeStyleProperties) {
|
|
@@ -1382,8 +1480,8 @@ function copyCssStyles(node, cloned, isRoot, context) {
|
|
|
1382
1480
|
const clonedStyle = cloned.style;
|
|
1383
1481
|
const computedStyle = ownerWindow.getComputedStyle(node);
|
|
1384
1482
|
const defaultStyle = getDefaultStyle(node, null, context);
|
|
1385
|
-
currentParentNodeStyle?.forEach((_,
|
|
1386
|
-
defaultStyle.delete(
|
|
1483
|
+
currentParentNodeStyle?.forEach((_, key2) => {
|
|
1484
|
+
defaultStyle.delete(key2);
|
|
1387
1485
|
});
|
|
1388
1486
|
const style = getDiffStyle(computedStyle, defaultStyle, includeStyleProperties);
|
|
1389
1487
|
style.delete("transition-property");
|
|
@@ -1454,8 +1552,8 @@ function copyPseudoClass(node, cloned, copyScrollbar, context, addWordToFontFami
|
|
|
1454
1552
|
content = content.replace(/(')|(")|(counter\(.+\))/g, "");
|
|
1455
1553
|
const klasses = [uuid()];
|
|
1456
1554
|
const defaultStyle = getDefaultStyle(node, pseudoClass, context);
|
|
1457
|
-
currentNodeStyle?.forEach((_,
|
|
1458
|
-
defaultStyle.delete(
|
|
1555
|
+
currentNodeStyle?.forEach((_, key2) => {
|
|
1556
|
+
defaultStyle.delete(key2);
|
|
1459
1557
|
});
|
|
1460
1558
|
const style = getDiffStyle(computedStyle, defaultStyle, context.includeStyleProperties);
|
|
1461
1559
|
style.delete("content");
|
|
@@ -2244,6 +2342,145 @@ async function captureAround(element) {
|
|
|
2244
2342
|
return out.toDataURL("image/jpeg", 0.82);
|
|
2245
2343
|
}
|
|
2246
2344
|
|
|
2345
|
+
// ../react/dist/table.js
|
|
2346
|
+
import { jsx as _jsx5, jsxs as _jsxs5 } from "react/jsx-runtime";
|
|
2347
|
+
import { useMemo as useMemo2, useState as useState4 } from "react";
|
|
2348
|
+
var columns = [
|
|
2349
|
+
{ key: "comment", label: "Comment" },
|
|
2350
|
+
{ key: "role", label: "Role" },
|
|
2351
|
+
{ key: "page", label: "Page" },
|
|
2352
|
+
{ key: "author", label: "Author" },
|
|
2353
|
+
{ key: "replies", label: "Replies" },
|
|
2354
|
+
{ key: "status", label: "Status" },
|
|
2355
|
+
{ key: "updated", label: "Updated" }
|
|
2356
|
+
];
|
|
2357
|
+
function sortValue(thread, key2) {
|
|
2358
|
+
if (key2 === "comment")
|
|
2359
|
+
return thread.messages[0]?.body ?? "";
|
|
2360
|
+
if (key2 === "role")
|
|
2361
|
+
return thread.role ?? "";
|
|
2362
|
+
if (key2 === "page")
|
|
2363
|
+
return thread.url || thread.route;
|
|
2364
|
+
if (key2 === "author")
|
|
2365
|
+
return thread.createdBy;
|
|
2366
|
+
if (key2 === "replies")
|
|
2367
|
+
return thread.messages.length;
|
|
2368
|
+
if (key2 === "status")
|
|
2369
|
+
return thread.status;
|
|
2370
|
+
return thread.updatedAt;
|
|
2371
|
+
}
|
|
2372
|
+
function searchText(thread) {
|
|
2373
|
+
return [
|
|
2374
|
+
thread.createdBy,
|
|
2375
|
+
thread.role ?? "",
|
|
2376
|
+
thread.url || thread.route,
|
|
2377
|
+
targetName(thread.anchor),
|
|
2378
|
+
...thread.messages.map((message) => message.body),
|
|
2379
|
+
...thread.fields.map((field) => `${field.label} ${field.value}`)
|
|
2380
|
+
].join("\n").toLowerCase();
|
|
2381
|
+
}
|
|
2382
|
+
function CommentTable({ rows, route, activeId, tabs, onSelect, onList, onClose }) {
|
|
2383
|
+
const [query, setQuery] = useState4("");
|
|
2384
|
+
const [sort, setSort] = useState4({ key: "updated", desc: true });
|
|
2385
|
+
const showRole = rows.some((row) => row.thread.role);
|
|
2386
|
+
const fieldLabels = useMemo2(() => {
|
|
2387
|
+
const labels = [];
|
|
2388
|
+
for (const { thread } of rows)
|
|
2389
|
+
for (const field of thread.fields)
|
|
2390
|
+
if (!labels.includes(field.label))
|
|
2391
|
+
labels.push(field.label);
|
|
2392
|
+
return labels;
|
|
2393
|
+
}, [rows]);
|
|
2394
|
+
const shown = useMemo2(() => {
|
|
2395
|
+
const needle = query.trim().toLowerCase();
|
|
2396
|
+
const matched = needle ? rows.filter((row) => searchText(row.thread).includes(needle)) : rows;
|
|
2397
|
+
return [...matched].sort((a, b) => {
|
|
2398
|
+
const left = sortValue(a.thread, sort.key);
|
|
2399
|
+
const right = sortValue(b.thread, sort.key);
|
|
2400
|
+
const order = typeof left === "number" && typeof right === "number" ? left - right : String(left).localeCompare(String(right));
|
|
2401
|
+
return sort.desc ? -order : order;
|
|
2402
|
+
});
|
|
2403
|
+
}, [query, rows, sort]);
|
|
2404
|
+
const visible = columns.filter((column) => column.key !== "role" || showRole);
|
|
2405
|
+
const header = (column) => _jsx5("th", { "aria-sort": sort.key === column.key ? sort.desc ? "descending" : "ascending" : "none", children: _jsxs5("button", { type: "button", onClick: () => setSort((current) => ({ key: column.key, desc: current.key === column.key ? !current.desc : column.key === "updated" })), children: [column.label, sort.key === column.key ? _jsx5("span", { className: "cs-sort", children: sort.desc ? "\u2193" : "\u2191" }) : null] }) }, column.key);
|
|
2406
|
+
return _jsxs5("section", { className: "cs-card cs-table-sheet cs-hit", "data-cs-ui": "panel", "aria-label": "Comments table", children: [_jsxs5("header", { className: "cs-sheet-head", children: [_jsx5("h2", { children: "Comments" }), _jsx5("input", { className: "cs-table-search", "data-cs-ui": "table-search", type: "search", "aria-label": "Search comments", placeholder: "Search", value: query, onChange: (event) => setQuery(event.target.value) }), _jsx5("button", { className: "cs-icon-button", "data-cs-ui": "list-view", type: "button", "aria-label": "List view", onClick: onList, children: _jsx5(List, {}) }), _jsx5("button", { className: "cs-icon-button", type: "button", "aria-label": "Close comments", onClick: onClose, children: _jsx5(Close, {}) })] }), tabs, _jsx5("div", { className: "cs-table-wrap", children: shown.length === 0 ? _jsx5("p", { className: "cs-empty", children: query ? "No comments match." : "No comments here." }) : _jsxs5("table", { className: "cs-table", "data-cs-ui": "table", children: [_jsx5("thead", { children: _jsxs5("tr", { children: [visible.slice(0, showRole ? 2 : 1).map(header), fieldLabels.map((label) => _jsx5("th", { children: _jsx5("span", { children: label }) }, `field:${label}`)), visible.slice(showRole ? 2 : 1).map(header)] }) }), _jsx5("tbody", { children: shown.map((row) => {
|
|
2407
|
+
const { thread } = row;
|
|
2408
|
+
const first = thread.messages[0];
|
|
2409
|
+
const note = statusNote(row.status, thread.anchor, thread.route === route);
|
|
2410
|
+
return _jsxs5("tr", { className: activeId === thread.id ? "is-active" : void 0, "data-cs-ui": "table-row", "data-thread-id": thread.id, tabIndex: 0, onClick: () => onSelect(row), onKeyDown: (event) => {
|
|
2411
|
+
if (event.key === "Enter") {
|
|
2412
|
+
event.preventDefault();
|
|
2413
|
+
onSelect(row);
|
|
2414
|
+
}
|
|
2415
|
+
}, children: [_jsxs5("td", { className: "cs-cell-comment", children: [_jsx5("span", { className: "cs-cell-clamp", children: first?.body || "\u2014" }), _jsx5("small", { children: targetName(thread.anchor) })] }), showRole ? _jsx5("td", { children: thread.role ? _jsx5("span", { className: "cs-role", children: thread.role }) : null }) : null, fieldLabels.map((label) => _jsx5("td", { children: _jsx5("span", { className: "cs-cell-clamp", children: thread.fields.find((field) => field.label === label)?.value ?? "" }) }, `field:${label}`)), _jsx5("td", { className: "cs-cell-page", title: thread.url || thread.route, children: thread.url || thread.route }), _jsx5("td", { className: "cs-cell-nowrap", children: thread.createdBy }), _jsx5("td", { className: "cs-cell-num", children: thread.messages.length - 1 || "" }), _jsxs5("td", { className: "cs-cell-nowrap", children: [_jsx5("span", { className: thread.status === "resolved" ? "cs-state is-done" : "cs-state", children: thread.status === "resolved" ? "Resolved" : "Open" }), note && thread.status === "open" ? _jsx5("small", { className: `cs-note is-${note.tone}`, children: note.text }) : null] }), _jsx5("td", { className: "cs-cell-nowrap", children: _jsx5("time", { dateTime: thread.updatedAt, children: ago(thread.updatedAt) }) })] }, thread.id);
|
|
2416
|
+
}) })] }) })] });
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
// ../react/dist/handoff.js
|
|
2420
|
+
var key = "devaccel.comment.handoff";
|
|
2421
|
+
var ttl = 30 * 60 * 1e3;
|
|
2422
|
+
function readHandoff() {
|
|
2423
|
+
try {
|
|
2424
|
+
const value = JSON.parse(sessionStorage.getItem(key) ?? "null");
|
|
2425
|
+
return value && Date.now() - value.at < ttl ? value : null;
|
|
2426
|
+
} catch {
|
|
2427
|
+
return null;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
function write(value) {
|
|
2431
|
+
try {
|
|
2432
|
+
if (value.invite || value.thread)
|
|
2433
|
+
sessionStorage.setItem(key, JSON.stringify(value));
|
|
2434
|
+
else
|
|
2435
|
+
sessionStorage.removeItem(key);
|
|
2436
|
+
} catch {
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
function linkParams() {
|
|
2440
|
+
const url = new URL(window.location.href);
|
|
2441
|
+
const hash = new URLSearchParams(url.hash.slice(1));
|
|
2442
|
+
return {
|
|
2443
|
+
url,
|
|
2444
|
+
hash,
|
|
2445
|
+
invite: url.searchParams.get("cs-invite") ?? hash.get("cs-invite"),
|
|
2446
|
+
thread: url.searchParams.get("cs-thread") ?? hash.get("cs-thread")
|
|
2447
|
+
};
|
|
2448
|
+
}
|
|
2449
|
+
function captureHandoff() {
|
|
2450
|
+
if (typeof window === "undefined")
|
|
2451
|
+
return;
|
|
2452
|
+
const { invite, thread } = linkParams();
|
|
2453
|
+
if (!invite && !thread)
|
|
2454
|
+
return;
|
|
2455
|
+
write({ ...readHandoff(), ...invite ? { invite } : {}, ...thread ? { thread } : {}, at: Date.now() });
|
|
2456
|
+
}
|
|
2457
|
+
function dropHandoff(part) {
|
|
2458
|
+
const current = readHandoff();
|
|
2459
|
+
if (!current)
|
|
2460
|
+
return;
|
|
2461
|
+
const { [part]: _dropped, ...rest } = current;
|
|
2462
|
+
write(rest);
|
|
2463
|
+
}
|
|
2464
|
+
function stripHandoffFromUrl() {
|
|
2465
|
+
const { url, hash } = linkParams();
|
|
2466
|
+
const inQuery = url.searchParams.has("cs-invite") || url.searchParams.has("cs-thread");
|
|
2467
|
+
const inHash = hash.has("cs-invite") || hash.has("cs-thread");
|
|
2468
|
+
if (!inQuery && !inHash)
|
|
2469
|
+
return;
|
|
2470
|
+
url.searchParams.delete("cs-invite");
|
|
2471
|
+
url.searchParams.delete("cs-thread");
|
|
2472
|
+
if (inHash)
|
|
2473
|
+
url.hash = "";
|
|
2474
|
+
history.replaceState(history.state, "", url);
|
|
2475
|
+
}
|
|
2476
|
+
function linkWith(href, name, value) {
|
|
2477
|
+
const url = new URL(href, window.location.origin);
|
|
2478
|
+
url.searchParams.delete("cs-invite");
|
|
2479
|
+
url.searchParams.delete("cs-thread");
|
|
2480
|
+
url.hash = `${name}=${encodeURIComponent(value)}`;
|
|
2481
|
+
return url.toString();
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2247
2484
|
// ../react/dist/styles.js
|
|
2248
2485
|
var toolbarCss = `
|
|
2249
2486
|
#cs-root {
|
|
@@ -2497,6 +2734,7 @@ var toolbarCss = `
|
|
|
2497
2734
|
display: flex; flex-direction: column; overflow: hidden;
|
|
2498
2735
|
}
|
|
2499
2736
|
#cs-root .cs-sheet-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 10px 4px 16px; }
|
|
2737
|
+
#cs-root .cs-sheet-head .cs-push { margin-left: auto; }
|
|
2500
2738
|
#cs-root .cs-sheet-head h2 { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
|
|
2501
2739
|
#cs-root .cs-tabs { display: flex; gap: 2px; margin: 4px 12px 6px; padding: 2px; border-radius: 9px; background: var(--cs-fill); }
|
|
2502
2740
|
#cs-root .cs-tabs button {
|
|
@@ -2568,6 +2806,88 @@ var toolbarCss = `
|
|
|
2568
2806
|
#cs-root .cs-composer-foot { display: flex; align-items: center; gap: 6px; padding: 6px 8px 8px 40px; }
|
|
2569
2807
|
#cs-root .cs-composer-foot .cs-hint { margin-left: auto; }
|
|
2570
2808
|
|
|
2809
|
+
/* Roles and fields */
|
|
2810
|
+
#cs-root .cs-details-input { display: grid; gap: 8px; padding: 2px 12px 6px 46px; }
|
|
2811
|
+
#cs-root .cs-details-edit .cs-details-input { padding: 0; }
|
|
2812
|
+
#cs-root .cs-detail-field { display: grid; gap: 3px; }
|
|
2813
|
+
#cs-root .cs-detail-field span, #cs-root .cs-roles-label, #cs-root .cs-detail-list dt {
|
|
2814
|
+
font-size: 11px; font-weight: 600; color: var(--cs-text-3); text-transform: uppercase; letter-spacing: 0.03em;
|
|
2815
|
+
}
|
|
2816
|
+
#cs-root .cs-detail-field textarea { padding: 6px 8px; min-height: 30px; max-height: 120px; field-sizing: content; }
|
|
2817
|
+
#cs-root .cs-thread.is-open .cs-detail-field textarea { background: var(--cs-card); }
|
|
2818
|
+
#cs-root .cs-roles { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
|
|
2819
|
+
#cs-root .cs-roles-label { margin-right: 4px; }
|
|
2820
|
+
#cs-root .cs-chip {
|
|
2821
|
+
display: inline-flex; align-items: center; gap: 2px; height: 24px; padding: 0 9px; border-radius: 999px;
|
|
2822
|
+
background: var(--cs-fill); font-size: 12px; font-weight: 500; color: var(--cs-text-2);
|
|
2823
|
+
}
|
|
2824
|
+
#cs-root button.cs-chip:hover { background: var(--cs-fill-2); }
|
|
2825
|
+
#cs-root .cs-chip.is-on, #cs-root button.cs-chip.is-on:hover { background: var(--cs-accent); color: #fff; }
|
|
2826
|
+
#cs-root .cs-chip.is-static { padding-right: 3px; color: var(--cs-text); }
|
|
2827
|
+
#cs-root .cs-chip.is-static button {
|
|
2828
|
+
width: 18px; height: 18px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; color: var(--cs-text-3);
|
|
2829
|
+
}
|
|
2830
|
+
#cs-root .cs-chip.is-static button:hover { background: var(--cs-fill-2); color: var(--cs-text); }
|
|
2831
|
+
#cs-root .cs-chip.is-static .cs-icon { width: 11px; height: 11px; }
|
|
2832
|
+
#cs-root .cs-chips { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
2833
|
+
#cs-root .cs-role {
|
|
2834
|
+
display: inline-flex; align-items: center; height: 18px; padding: 0 7px; border-radius: 999px;
|
|
2835
|
+
background: var(--cs-accent-soft); color: var(--cs-accent); font-size: 11px; font-weight: 600;
|
|
2836
|
+
}
|
|
2837
|
+
#cs-root .cs-detail-list { display: grid; gap: 6px; margin: 6px 0 0; }
|
|
2838
|
+
#cs-root .cs-detail-list > div { padding: 6px 8px; border-radius: 8px; background: var(--cs-fill); }
|
|
2839
|
+
#cs-root .cs-thread.is-open .cs-detail-list > div { background: var(--cs-card); }
|
|
2840
|
+
#cs-root .cs-detail-list dd { margin: 1px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
2841
|
+
#cs-root .cs-detail-list.is-clamped dd { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
2842
|
+
#cs-root .cs-details-edit { display: grid; gap: 8px; }
|
|
2843
|
+
#cs-root .cs-details-edit .cs-row-end { margin-top: 0; }
|
|
2844
|
+
#cs-root .cs-settings { width: 280px; }
|
|
2845
|
+
#cs-root .cs-settings-head { display: flex; align-items: center; gap: 4px; padding: 4px 4px 6px; }
|
|
2846
|
+
#cs-root .cs-settings-head h2 { font-size: 14px; font-weight: 600; }
|
|
2847
|
+
#cs-root .cs-settings-body { display: grid; gap: 6px; padding: 2px 8px 8px; }
|
|
2848
|
+
#cs-root .cs-field-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; min-height: 28px; padding-left: 2px; }
|
|
2849
|
+
#cs-root .cs-add-row { display: flex; align-items: center; gap: 4px; }
|
|
2850
|
+
#cs-root .cs-add-row input { padding: 5px 8px; }
|
|
2851
|
+
#cs-root .cs-settings .cs-hint { padding: 6px 8px 4px; }
|
|
2852
|
+
|
|
2853
|
+
/* Table */
|
|
2854
|
+
#cs-root .cs-table-sheet {
|
|
2855
|
+
top: 12px; right: 12px; width: min(1100px, calc(100vw - 24px)); max-height: calc(100% - 88px);
|
|
2856
|
+
display: flex; flex-direction: column; overflow: hidden;
|
|
2857
|
+
}
|
|
2858
|
+
#cs-root .cs-table-sheet .cs-sheet-head { gap: 6px; }
|
|
2859
|
+
#cs-root .cs-table-sheet .cs-sheet-head h2 { margin-right: auto; }
|
|
2860
|
+
#cs-root .cs-table-search { width: 220px; padding: 5px 10px; }
|
|
2861
|
+
#cs-root .cs-table-sheet .cs-tabs { max-width: 420px; }
|
|
2862
|
+
#cs-root .cs-table-wrap { flex: 1 1 auto; min-height: 0; overflow: auto; border-top: 1px solid var(--cs-line); overscroll-behavior: contain; }
|
|
2863
|
+
#cs-root .cs-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
|
|
2864
|
+
#cs-root .cs-table th {
|
|
2865
|
+
position: sticky; top: 0; z-index: 1; background: var(--cs-card); text-align: left; font-weight: 600;
|
|
2866
|
+
font-size: 11px; color: var(--cs-text-3); text-transform: uppercase; letter-spacing: 0.03em;
|
|
2867
|
+
padding: 0; border-bottom: 1px solid var(--cs-line); white-space: nowrap;
|
|
2868
|
+
}
|
|
2869
|
+
#cs-root .cs-table th > button, #cs-root .cs-table th > span {
|
|
2870
|
+
display: inline-flex; align-items: center; gap: 4px; padding: 8px 10px; font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
|
|
2871
|
+
}
|
|
2872
|
+
#cs-root .cs-table th > button:hover { color: var(--cs-text); }
|
|
2873
|
+
#cs-root .cs-sort { color: var(--cs-accent); }
|
|
2874
|
+
#cs-root .cs-table td { padding: 8px 10px; vertical-align: top; border-bottom: 1px solid var(--cs-line); max-width: 260px; }
|
|
2875
|
+
#cs-root .cs-table tbody tr { cursor: pointer; }
|
|
2876
|
+
#cs-root .cs-table tbody tr:hover { background: var(--cs-fill); }
|
|
2877
|
+
#cs-root .cs-table tbody tr.is-active { background: var(--cs-accent-soft); }
|
|
2878
|
+
#cs-root .cs-table tbody tr:focus-visible { outline: 2px solid var(--cs-accent); outline-offset: -2px; }
|
|
2879
|
+
#cs-root .cs-cell-comment { min-width: 220px; }
|
|
2880
|
+
#cs-root .cs-cell-comment small, #cs-root .cs-table td small { display: block; margin-top: 2px; font-size: 11.5px; color: var(--cs-text-3); }
|
|
2881
|
+
#cs-root .cs-cell-clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
2882
|
+
#cs-root .cs-cell-page { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--cs-text-2); max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2883
|
+
#cs-root .cs-cell-nowrap { white-space: nowrap; }
|
|
2884
|
+
#cs-root .cs-cell-num { text-align: right; font-variant-numeric: tabular-nums; color: var(--cs-text-2); }
|
|
2885
|
+
#cs-root .cs-state { display: inline-flex; align-items: center; gap: 5px; font-weight: 500; }
|
|
2886
|
+
#cs-root .cs-state::before { content: ""; width: 6px; height: 6px; border-radius: 999px; background: #34c759; }
|
|
2887
|
+
#cs-root .cs-state.is-done { color: var(--cs-text-3); }
|
|
2888
|
+
#cs-root .cs-state.is-done::before { background: var(--cs-text-3); }
|
|
2889
|
+
#cs-root .cs-table .cs-note { display: flex; margin-top: 2px; font-size: 11px; }
|
|
2890
|
+
|
|
2571
2891
|
/* Attachments */
|
|
2572
2892
|
#cs-root .cs-attach { display: inline-flex; gap: 0; flex: none; }
|
|
2573
2893
|
#cs-root .cs-tray { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 12px 2px 46px; }
|
|
@@ -2658,6 +2978,7 @@ var toolbarCss = `
|
|
|
2658
2978
|
`;
|
|
2659
2979
|
|
|
2660
2980
|
// ../react/dist/toolbar.js
|
|
2981
|
+
captureHandoff();
|
|
2661
2982
|
var historyFlag = "__devaccelCommentHistory";
|
|
2662
2983
|
var prefsKey = "devaccel.comment.prefs";
|
|
2663
2984
|
var positions = [
|
|
@@ -2690,6 +3011,13 @@ function placeAgainst(element, host) {
|
|
|
2690
3011
|
height: rect.height
|
|
2691
3012
|
};
|
|
2692
3013
|
}
|
|
3014
|
+
function pageHref() {
|
|
3015
|
+
const url = new URL(window.location.href);
|
|
3016
|
+
url.searchParams.delete("cs-thread");
|
|
3017
|
+
url.searchParams.delete("cs-invite");
|
|
3018
|
+
const hash = url.hash.includes("cs-thread=") || url.hash.includes("cs-invite=") ? "" : url.hash;
|
|
3019
|
+
return `${url.pathname}${url.search}${hash}`;
|
|
3020
|
+
}
|
|
2693
3021
|
function isTyping(target) {
|
|
2694
3022
|
if (!(target instanceof HTMLElement))
|
|
2695
3023
|
return false;
|
|
@@ -2712,44 +3040,48 @@ async function loadSession(sessionPath) {
|
|
|
2712
3040
|
return { apiUrl: payload.apiUrl, project: payload.project, environment: payload.environment, hostToken: payload.hostToken };
|
|
2713
3041
|
}
|
|
2714
3042
|
function Avatar({ name, size = 24 }) {
|
|
2715
|
-
return
|
|
2716
|
-
}
|
|
2717
|
-
function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs = 4e3, position = "bottom-center", theme = "light" }) {
|
|
2718
|
-
const [phase, setPhase] =
|
|
2719
|
-
const [session, setSession] =
|
|
2720
|
-
const [member, setMember] =
|
|
2721
|
-
const [threads, setThreads] =
|
|
2722
|
-
const [route, setRoute] =
|
|
2723
|
-
const [surface, setSurface] =
|
|
2724
|
-
const [tab, setTab] =
|
|
2725
|
-
const [mode, setMode] =
|
|
2726
|
-
const [reattachId, setReattachId] =
|
|
2727
|
-
const [activeId, setActiveId] =
|
|
2728
|
-
const [peekId, setPeekId] =
|
|
2729
|
-
const [draft, setDraft] =
|
|
2730
|
-
const [body, setBody] =
|
|
2731
|
-
const [reply, setReply] =
|
|
2732
|
-
const [name, setName] =
|
|
2733
|
-
const [passphrase, setPassphrase] =
|
|
2734
|
-
const [email, setEmail] =
|
|
2735
|
-
const [members, setMembers] =
|
|
2736
|
-
const [profile, setProfile] =
|
|
2737
|
-
const [deliveries, setDeliveries] =
|
|
2738
|
-
const [invite, setInvite] =
|
|
2739
|
-
const [capturing, setCapturing] =
|
|
2740
|
-
const [inviteLink, setInviteLink] =
|
|
2741
|
-
const [renaming, setRenaming] =
|
|
2742
|
-
const [confirmReset, setConfirmReset] =
|
|
2743
|
-
const [error, setError] =
|
|
2744
|
-
const [toast, setToast] =
|
|
2745
|
-
const [busy, setBusy] =
|
|
2746
|
-
const [prefs, setPrefs] =
|
|
2747
|
-
const [portal, setPortal] =
|
|
2748
|
-
const [mounted, setMounted] =
|
|
2749
|
-
const [highlight, setHighlight] =
|
|
2750
|
-
const [flash, setFlash] =
|
|
2751
|
-
const [placed, setPlaced] =
|
|
2752
|
-
const [systemDark, setSystemDark] =
|
|
3043
|
+
return _jsx6("span", { className: "cs-avatar", style: { width: size, height: size, fontSize: size * 0.4, background: colorFor(name) }, children: initials(name) });
|
|
3044
|
+
}
|
|
3045
|
+
function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs = 4e3, position = "bottom-center", theme = "light", navigate }) {
|
|
3046
|
+
const [phase, setPhase] = useState5("boot");
|
|
3047
|
+
const [session, setSession] = useState5(null);
|
|
3048
|
+
const [member, setMember] = useState5(null);
|
|
3049
|
+
const [threads, setThreads] = useState5([]);
|
|
3050
|
+
const [route, setRoute] = useState5("/");
|
|
3051
|
+
const [surface, setSurface] = useState5("none");
|
|
3052
|
+
const [tab, setTab] = useState5("page");
|
|
3053
|
+
const [mode, setMode] = useState5("browse");
|
|
3054
|
+
const [reattachId, setReattachId] = useState5(null);
|
|
3055
|
+
const [activeId, setActiveId] = useState5(null);
|
|
3056
|
+
const [peekId, setPeekId] = useState5(null);
|
|
3057
|
+
const [draft, setDraft] = useState5(null);
|
|
3058
|
+
const [body, setBody] = useState5("");
|
|
3059
|
+
const [reply, setReply] = useState5("");
|
|
3060
|
+
const [name, setName] = useState5("");
|
|
3061
|
+
const [passphrase, setPassphrase] = useState5("");
|
|
3062
|
+
const [email, setEmail] = useState5("");
|
|
3063
|
+
const [members, setMembers] = useState5([]);
|
|
3064
|
+
const [profile, setProfile] = useState5(null);
|
|
3065
|
+
const [deliveries, setDeliveries] = useState5({});
|
|
3066
|
+
const [invite, setInvite] = useState5(null);
|
|
3067
|
+
const [capturing, setCapturing] = useState5(false);
|
|
3068
|
+
const [inviteLink, setInviteLink] = useState5(null);
|
|
3069
|
+
const [renaming, setRenaming] = useState5(null);
|
|
3070
|
+
const [confirmReset, setConfirmReset] = useState5(false);
|
|
3071
|
+
const [error, setError] = useState5("");
|
|
3072
|
+
const [toast, setToast] = useState5("");
|
|
3073
|
+
const [busy, setBusy] = useState5(false);
|
|
3074
|
+
const [prefs, setPrefs] = useState5({});
|
|
3075
|
+
const [portal, setPortal] = useState5(null);
|
|
3076
|
+
const [mounted, setMounted] = useState5(false);
|
|
3077
|
+
const [highlight, setHighlight] = useState5(null);
|
|
3078
|
+
const [flash, setFlash] = useState5(null);
|
|
3079
|
+
const [placed, setPlaced] = useState5([]);
|
|
3080
|
+
const [systemDark, setSystemDark] = useState5(false);
|
|
3081
|
+
const [settings, setSettings] = useState5(null);
|
|
3082
|
+
const [draftDetails, setDraftDetails] = useState5(noDetails);
|
|
3083
|
+
const [editingId, setEditingId] = useState5(null);
|
|
3084
|
+
const [view, setView] = useState5("list");
|
|
2753
3085
|
const threadsRef = useRef3(threads);
|
|
2754
3086
|
threadsRef.current = threads;
|
|
2755
3087
|
const modeRef = useRef3(mode);
|
|
@@ -2759,6 +3091,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
2759
3091
|
const draftShot = useRef3(null);
|
|
2760
3092
|
const replyShot = useRef3(null);
|
|
2761
3093
|
const pendingThread = useRef3(null);
|
|
3094
|
+
const arrival = useRef3(null);
|
|
2762
3095
|
const draftFiles = useAttachments(session, member, setError);
|
|
2763
3096
|
const replyFiles = useAttachments(session, member, setError);
|
|
2764
3097
|
const resetReplyFiles = replyFiles.reset;
|
|
@@ -2858,7 +3191,9 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
2858
3191
|
const refresh = useCallback2(async () => {
|
|
2859
3192
|
if (!session || !member)
|
|
2860
3193
|
return;
|
|
2861
|
-
|
|
3194
|
+
const next = await listThreads(session, member);
|
|
3195
|
+
setThreads(next.threads);
|
|
3196
|
+
setSettings(next.settings);
|
|
2862
3197
|
}, [member, session]);
|
|
2863
3198
|
useEffect2(() => {
|
|
2864
3199
|
if (!session || !member)
|
|
@@ -2866,8 +3201,10 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
2866
3201
|
let stop = false;
|
|
2867
3202
|
const tick = () => {
|
|
2868
3203
|
listThreads(session, member).then((next) => {
|
|
2869
|
-
if (
|
|
2870
|
-
|
|
3204
|
+
if (stop)
|
|
3205
|
+
return;
|
|
3206
|
+
setThreads(next.threads);
|
|
3207
|
+
setSettings(next.settings);
|
|
2871
3208
|
}).catch((caught) => {
|
|
2872
3209
|
if (caught instanceof ApiError && caught.status === 401) {
|
|
2873
3210
|
localStorage.removeItem(storageKey(session));
|
|
@@ -2900,22 +3237,23 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
2900
3237
|
};
|
|
2901
3238
|
}, [member, session]);
|
|
2902
3239
|
useEffect2(() => {
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
if (
|
|
2907
|
-
setInvite(
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
3240
|
+
captureHandoff();
|
|
3241
|
+
const handoff = readHandoff();
|
|
3242
|
+
pendingThread.current = handoff?.thread ?? null;
|
|
3243
|
+
if (handoff?.invite)
|
|
3244
|
+
setInvite(handoff.invite);
|
|
3245
|
+
stripHandoffFromUrl();
|
|
3246
|
+
}, []);
|
|
3247
|
+
const dropInvite = useCallback2(() => {
|
|
3248
|
+
setInvite(null);
|
|
3249
|
+
dropHandoff("invite");
|
|
2912
3250
|
}, []);
|
|
2913
3251
|
useEffect2(() => {
|
|
2914
3252
|
if (phase === "ready" && !member && (pendingThread.current || invite))
|
|
2915
3253
|
setSurface("unlock");
|
|
2916
3254
|
if (member && invite)
|
|
2917
|
-
|
|
2918
|
-
}, [invite, member, phase]);
|
|
3255
|
+
dropInvite();
|
|
3256
|
+
}, [dropInvite, invite, member, phase]);
|
|
2919
3257
|
useEffect2(() => {
|
|
2920
3258
|
if (!mounted)
|
|
2921
3259
|
return;
|
|
@@ -3003,12 +3341,13 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3003
3341
|
}
|
|
3004
3342
|
if (!member || event.metaKey || event.ctrlKey || event.altKey || isTyping(event.target))
|
|
3005
3343
|
return;
|
|
3006
|
-
const
|
|
3007
|
-
if (
|
|
3344
|
+
const key2 = event.key.toLowerCase();
|
|
3345
|
+
if (key2 === "c")
|
|
3008
3346
|
startComment();
|
|
3009
|
-
else if (
|
|
3347
|
+
else if (key2 === "i") {
|
|
3348
|
+
setView(prefs.view ?? "list");
|
|
3010
3349
|
setSurface((current) => current === "inbox" ? "none" : "inbox");
|
|
3011
|
-
else if (
|
|
3350
|
+
} else if (key2 === "h")
|
|
3012
3351
|
togglePins();
|
|
3013
3352
|
else
|
|
3014
3353
|
return;
|
|
@@ -3016,7 +3355,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3016
3355
|
};
|
|
3017
3356
|
document.addEventListener("keydown", onKey);
|
|
3018
3357
|
return () => document.removeEventListener("keydown", onKey);
|
|
3019
|
-
}, [draft, member, mode, startComment, surface, togglePins]);
|
|
3358
|
+
}, [draft, member, mode, prefs.view, startComment, surface, togglePins]);
|
|
3020
3359
|
useEffect2(() => {
|
|
3021
3360
|
if (mode === "browse") {
|
|
3022
3361
|
setHighlight(null);
|
|
@@ -3048,7 +3387,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3048
3387
|
const box = placeAgainst(element, target);
|
|
3049
3388
|
if (modeRef.current === "reattach" && reattachId && member) {
|
|
3050
3389
|
setBusy(true);
|
|
3051
|
-
updateThread(session, member, reattachId, { anchor }).then(() => refresh()).then(() => {
|
|
3390
|
+
updateThread(session, member, reattachId, { anchor, url: pageHref() }).then(() => refresh()).then(() => {
|
|
3052
3391
|
setMode("browse");
|
|
3053
3392
|
setReattachId(null);
|
|
3054
3393
|
setSurface("inbox");
|
|
@@ -3069,6 +3408,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3069
3408
|
draftShot.current = null;
|
|
3070
3409
|
draftFiles.reset();
|
|
3071
3410
|
setBody("");
|
|
3411
|
+
setDraftDetails(noDetails);
|
|
3072
3412
|
setMode("browse");
|
|
3073
3413
|
setError("");
|
|
3074
3414
|
};
|
|
@@ -3100,19 +3440,19 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3100
3440
|
all: open.length,
|
|
3101
3441
|
resolved: placed.length - open.length
|
|
3102
3442
|
};
|
|
3103
|
-
const listed =
|
|
3443
|
+
const listed = useMemo3(() => {
|
|
3104
3444
|
if (tab === "resolved")
|
|
3105
3445
|
return placed.filter((item) => item.thread.status === "resolved");
|
|
3106
3446
|
const openOnly = placed.filter((item) => item.thread.status === "open");
|
|
3107
3447
|
return tab === "page" ? openOnly.filter((item) => item.thread.route === route) : openOnly;
|
|
3108
3448
|
}, [placed, route, tab]);
|
|
3109
|
-
const groups =
|
|
3449
|
+
const groups = useMemo3(() => {
|
|
3110
3450
|
if (tab !== "all")
|
|
3111
3451
|
return [{ route: null, items: listed }];
|
|
3112
3452
|
const byRoute = /* @__PURE__ */ new Map();
|
|
3113
3453
|
for (const item of listed)
|
|
3114
3454
|
byRoute.set(item.thread.route, [...byRoute.get(item.thread.route) ?? [], item]);
|
|
3115
|
-
return [...byRoute.entries()].sort(([a], [b]) => a === route ? -1 : b === route ? 1 : a.localeCompare(b)).map(([
|
|
3455
|
+
return [...byRoute.entries()].sort(([a], [b]) => a === route ? -1 : b === route ? 1 : a.localeCompare(b)).map(([key2, items]) => ({ route: key2, items }));
|
|
3116
3456
|
}, [listed, route, tab]);
|
|
3117
3457
|
const showOnPage = useCallback2((thread) => {
|
|
3118
3458
|
const { element } = resolveInDocument(thread.anchor, window.location.pathname);
|
|
@@ -3133,21 +3473,67 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3133
3473
|
if (!item)
|
|
3134
3474
|
return;
|
|
3135
3475
|
pendingThread.current = null;
|
|
3136
|
-
|
|
3137
|
-
url.searchParams.delete("cs-thread");
|
|
3138
|
-
history.replaceState(history.state, "", url);
|
|
3476
|
+
dropHandoff("thread");
|
|
3139
3477
|
setTab(item.thread.status === "resolved" ? "resolved" : item.thread.route === route ? "page" : "all");
|
|
3140
3478
|
setActiveId(id);
|
|
3141
3479
|
setSurface("inbox");
|
|
3142
3480
|
if (item.status === "pinned" || item.status === "drifted")
|
|
3143
3481
|
showOnPage(item.thread);
|
|
3482
|
+
else
|
|
3483
|
+
arrival.current = { id, until: Date.now() + 6e3 };
|
|
3144
3484
|
}, [member, placed, route, showOnPage]);
|
|
3485
|
+
useEffect2(() => {
|
|
3486
|
+
const pending = arrival.current;
|
|
3487
|
+
if (!pending)
|
|
3488
|
+
return;
|
|
3489
|
+
if (Date.now() > pending.until) {
|
|
3490
|
+
arrival.current = null;
|
|
3491
|
+
return;
|
|
3492
|
+
}
|
|
3493
|
+
const item = placed.find((candidate) => candidate.thread.id === pending.id);
|
|
3494
|
+
if (!item || item.status !== "pinned" && item.status !== "drifted")
|
|
3495
|
+
return;
|
|
3496
|
+
arrival.current = null;
|
|
3497
|
+
showOnPage(item.thread);
|
|
3498
|
+
}, [placed, showOnPage]);
|
|
3499
|
+
const select = useCallback2((item, keepOpen = false) => {
|
|
3500
|
+
const { thread } = item;
|
|
3501
|
+
if (!keepOpen && activeId === thread.id) {
|
|
3502
|
+
setActiveId(null);
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3505
|
+
setActiveId(thread.id);
|
|
3506
|
+
setEditingId(null);
|
|
3507
|
+
const onPage = item.status === "pinned" || item.status === "drifted";
|
|
3508
|
+
const target = thread.url || thread.route;
|
|
3509
|
+
const legacyHere = thread.url === thread.route && thread.route === route;
|
|
3510
|
+
if (target === pageHref() || onPage && legacyHere)
|
|
3511
|
+
return;
|
|
3512
|
+
if (navigate) {
|
|
3513
|
+
arrival.current = { id: thread.id, until: Date.now() + 6e3 };
|
|
3514
|
+
navigate(target);
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
window.location.assign(linkWith(target, "cs-thread", thread.id));
|
|
3518
|
+
}, [activeId, navigate, route]);
|
|
3145
3519
|
if (!mounted || phase !== "ready" || !session)
|
|
3146
3520
|
return null;
|
|
3147
3521
|
const memberName = member ? readJwt(member)?.name ?? name : "";
|
|
3148
3522
|
const host = portal ?? document.body;
|
|
3149
3523
|
const inside = host !== document.body;
|
|
3150
3524
|
const peek = peekId ? placed.find((item) => item.thread.id === peekId && item.top != null) : void 0;
|
|
3525
|
+
const canPost = Boolean(body.trim() || draftFiles.ids.length || settings?.fields.enabled && hasFieldValues(draftDetails));
|
|
3526
|
+
const changeSettings = (next) => {
|
|
3527
|
+
if (!member)
|
|
3528
|
+
return;
|
|
3529
|
+
const previous = settings;
|
|
3530
|
+
setSettings(next);
|
|
3531
|
+
setError("");
|
|
3532
|
+
saveSettings(session, member, next).then(setSettings).catch((caught) => {
|
|
3533
|
+
setSettings(previous);
|
|
3534
|
+
setError(caught instanceof Error ? caught.message : "Couldn\u2019t save.");
|
|
3535
|
+
});
|
|
3536
|
+
};
|
|
3151
3537
|
const lock = () => {
|
|
3152
3538
|
localStorage.removeItem(storageKey(session));
|
|
3153
3539
|
setMember(null);
|
|
@@ -3173,22 +3559,23 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3173
3559
|
setDraft(null);
|
|
3174
3560
|
setSurface("unlock");
|
|
3175
3561
|
};
|
|
3176
|
-
const bar =
|
|
3562
|
+
const bar = _jsx6("div", { className: "cs-bar cs-hit", "data-cs-ui": "bar", role: "toolbar", "aria-label": "Comments toolbar", children: !member ? _jsxs6("button", { className: "cs-bar-lock", "data-cs-ui": "launcher", type: "button", "aria-label": "Comments", onClick: () => {
|
|
3177
3563
|
setError("");
|
|
3178
3564
|
setSurface(surface === "unlock" ? "none" : "unlock");
|
|
3179
|
-
}, children: [
|
|
3565
|
+
}, children: [_jsx6(Lock, {}), _jsx6("span", { children: "Comments" })] }) : _jsxs6(_Fragment3, { children: [_jsx6("button", { className: mode === "select" ? "cs-tool is-on is-accent" : "cs-tool", "data-cs-ui": "comment-mode", "data-tip": "Comment C", type: "button", "aria-label": "Add comment", "aria-pressed": mode === "select", onClick: startComment, children: _jsx6(AddComment, {}) }), _jsxs6("button", { className: surface === "inbox" ? "cs-tool is-on" : "cs-tool", "data-cs-ui": "launcher", "data-tip": "Comments I", type: "button", "aria-label": "Comments", "aria-expanded": surface === "inbox", onClick: () => {
|
|
3180
3566
|
setError("");
|
|
3181
3567
|
setMode("browse");
|
|
3568
|
+
setView(prefs.view ?? "list");
|
|
3182
3569
|
setSurface(surface === "inbox" ? "none" : "inbox");
|
|
3183
|
-
}, children: [
|
|
3570
|
+
}, children: [_jsx6(Inbox, {}), counts.page > 0 ? _jsx6("span", { className: "cs-badge", children: counts.page }) : null] }), _jsx6("button", { className: pinsHidden ? "cs-tool is-on" : "cs-tool", "data-cs-ui": "pins-toggle", "data-tip": pinsHidden ? "Show pins H" : "Hide pins H", type: "button", "aria-label": pinsHidden ? "Show pins" : "Hide pins", "aria-pressed": pinsHidden, onClick: togglePins, children: pinsHidden ? _jsx6(EyeOff, {}) : _jsx6(Eye, {}) }), _jsx6("span", { className: "cs-divider" }), _jsx6("button", { className: surface === "menu" ? "cs-tool cs-me is-on" : "cs-tool cs-me", "data-cs-ui": "menu-button", "data-tip": memberName, type: "button", "aria-label": "You", "aria-expanded": surface === "menu", onClick: () => {
|
|
3184
3571
|
setConfirmReset(false);
|
|
3185
3572
|
setRenaming(null);
|
|
3186
3573
|
if (surface !== "menu" && member) {
|
|
3187
3574
|
getProfile(session, member).then(setProfile).catch(() => void 0);
|
|
3188
3575
|
}
|
|
3189
3576
|
setSurface(surface === "menu" ? "none" : "menu");
|
|
3190
|
-
}, children:
|
|
3191
|
-
const unlockCard =
|
|
3577
|
+
}, children: _jsx6(Avatar, { name: memberName, size: 24 }) })] }) });
|
|
3578
|
+
const unlockCard = _jsxs6("section", { className: "cs-card cs-unlock cs-hit", "data-cs-ui": "panel", "aria-label": "Unlock comments", children: [_jsx6("div", { className: "cs-unlock-icon", children: _jsx6(Lock, { size: 18 }) }), _jsx6("h2", { children: "Comments" }), _jsx6("p", { className: "cs-sub", children: invite ? `${readJwt(invite)?.name ?? "A teammate"} invited you to comment. Add your name and email to join.` : "Enter the team passphrase to see and add comments." }), _jsxs6("form", { onSubmit: (event) => {
|
|
3192
3579
|
event.preventDefault();
|
|
3193
3580
|
setBusy(true);
|
|
3194
3581
|
setError("");
|
|
@@ -3201,16 +3588,16 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3201
3588
|
localStorage.setItem(storageKey(fresh), token);
|
|
3202
3589
|
setMember(token);
|
|
3203
3590
|
setPassphrase("");
|
|
3204
|
-
|
|
3591
|
+
dropInvite();
|
|
3205
3592
|
setTab("page");
|
|
3206
3593
|
setSurface("inbox");
|
|
3207
3594
|
}).catch((caught) => {
|
|
3208
3595
|
if (caught instanceof ApiError && caught.code === "invalid_invite")
|
|
3209
|
-
|
|
3596
|
+
dropInvite();
|
|
3210
3597
|
setError(caught instanceof Error ? caught.message : "Couldn\u2019t unlock.");
|
|
3211
3598
|
}).finally(() => setBusy(false));
|
|
3212
|
-
}, children: [
|
|
3213
|
-
const menuCard =
|
|
3599
|
+
}, children: [_jsxs6("label", { className: "cs-field", children: [_jsx6("span", { children: "Your name" }), _jsx6("input", { "data-cs-ui": "unlock-name", value: name, onChange: (event) => setName(event.target.value), autoComplete: "name", required: true })] }), invite ? null : _jsxs6("label", { className: "cs-field", children: [_jsx6("span", { children: "Passphrase" }), _jsx6("input", { "data-cs-ui": "unlock-passphrase", type: "password", value: passphrase, onChange: (event) => setPassphrase(event.target.value), autoComplete: "current-password", required: true })] }), _jsxs6("label", { className: "cs-field", children: [_jsx6("span", { children: "Email" }), _jsx6("input", { "data-cs-ui": "unlock-email", type: "email", value: email, onChange: (event) => setEmail(event.target.value), autoComplete: "email", required: true }), _jsx6("small", { children: "So teammates can @mention you." })] }), error ? _jsx6("p", { className: "cs-error", "data-cs-ui": "error", children: error }) : null, _jsx6("button", { className: "cs-button is-primary is-wide", "data-cs-ui": "unlock-submit", type: "submit", disabled: busy, children: busy ? "Unlocking\u2026" : invite ? "Join" : "Unlock" })] })] });
|
|
3600
|
+
const menuCard = _jsx6("section", { className: "cs-card cs-menu cs-hit", "data-cs-ui": "menu", "aria-label": "Your comment settings", children: confirmReset ? _jsxs6("div", { className: "cs-confirm", "data-cs-ui": "reset-confirm", children: [_jsx6("h2", { children: "Reset on this device?" }), _jsx6("p", { className: "cs-sub", children: "You\u2019ll be signed out and your name and preferences are cleared. Comments aren\u2019t affected." }), _jsxs6("div", { className: "cs-row-end", children: [_jsx6("button", { className: "cs-button", type: "button", onClick: () => setConfirmReset(false), children: "Cancel" }), _jsx6("button", { className: "cs-button is-danger", "data-cs-ui": "reset-submit", type: "button", onClick: reset, children: "Reset" })] })] }) : _jsxs6(_Fragment3, { children: [_jsxs6("div", { className: "cs-me-row", children: [_jsx6(Avatar, { name: renaming ?? memberName, size: 36 }), renaming != null ? _jsxs6("form", { className: "cs-rename", onSubmit: (event) => {
|
|
3214
3601
|
event.preventDefault();
|
|
3215
3602
|
const next = renaming.trim();
|
|
3216
3603
|
if (!next || next === memberName || !member) {
|
|
@@ -3226,15 +3613,15 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3226
3613
|
setRenaming(null);
|
|
3227
3614
|
say("Name updated");
|
|
3228
3615
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t rename.")).finally(() => setBusy(false));
|
|
3229
|
-
}, children: [
|
|
3616
|
+
}, children: [_jsx6("input", { "data-cs-ui": "rename-input", "aria-label": "Your name", value: renaming, maxLength: 40, autoFocus: true, onChange: (event) => setRenaming(event.target.value), onKeyDown: (event) => {
|
|
3230
3617
|
if (event.key === "Escape") {
|
|
3231
3618
|
event.preventDefault();
|
|
3232
3619
|
setRenaming(null);
|
|
3233
3620
|
}
|
|
3234
|
-
} }),
|
|
3621
|
+
} }), _jsx6("button", { className: "cs-icon-button", type: "submit", "aria-label": "Save name", disabled: busy, children: _jsx6(Check, {}) })] }) : _jsxs6("div", { className: "cs-me-name", children: [_jsxs6("span", { className: "cs-me-lines", children: [_jsx6("strong", { children: memberName }), _jsx6("small", { children: profile?.email ?? readJwt(member ?? "")?.email ?? "" })] }), _jsx6("button", { className: "cs-icon-button", "data-cs-ui": "rename", type: "button", "aria-label": "Change name", onClick: () => {
|
|
3235
3622
|
setError("");
|
|
3236
3623
|
setRenaming(memberName);
|
|
3237
|
-
}, children:
|
|
3624
|
+
}, children: _jsx6(Pencil, {}) })] })] }), error ? _jsx6("p", { className: "cs-error", children: error }) : null, _jsx6("div", { className: "cs-sep" }), _jsxs6("button", { className: "cs-item", type: "button", role: "switch", "aria-checked": !pinsHidden, onClick: togglePins, children: [_jsx6("span", { children: "Show pins" }), _jsx6("span", { className: pinsHidden ? "cs-switch" : "cs-switch is-on" })] }), _jsxs6("button", { className: "cs-item", "data-cs-ui": "mention-emails", type: "button", role: "switch", "aria-checked": profile?.mentionEmails ?? true, disabled: !profile, onClick: () => {
|
|
3238
3625
|
if (!member || !profile)
|
|
3239
3626
|
return;
|
|
3240
3627
|
updateProfile(session, member, { mentionEmails: !profile.mentionEmails }).then((updated) => {
|
|
@@ -3243,36 +3630,46 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3243
3630
|
setProfile(updated);
|
|
3244
3631
|
say(updated.mentionEmails ? "Mention emails on" : "Mention emails off");
|
|
3245
3632
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t update."));
|
|
3246
|
-
}, children: [
|
|
3633
|
+
}, children: [_jsx6("span", { children: "Email me when mentioned" }), _jsx6("span", { className: profile?.mentionEmails ?? true ? "cs-switch is-on" : "cs-switch" })] }), _jsxs6("div", { className: "cs-item is-static", children: [_jsx6("span", { children: "Position" }), _jsx6("span", { className: "cs-segment", role: "radiogroup", "aria-label": "Toolbar position", children: positions.map((option) => _jsx6("button", { type: "button", role: "radio", "aria-checked": barPosition === option.value, className: barPosition === option.value ? "is-on" : "", onClick: () => savePrefs({ ...prefs, position: option.value }), children: option.label }, option.value)) })] }), _jsxs6("button", { className: "cs-item", "data-cs-ui": "settings-open", type: "button", onClick: () => {
|
|
3634
|
+
setError("");
|
|
3635
|
+
setSurface("settings");
|
|
3636
|
+
}, children: [_jsx6("span", { children: "Comment fields" }), _jsx6("span", { className: "cs-hint", children: detailsEnabled(settings) ? "On" : "Off" })] }), _jsx6("div", { className: "cs-sep" }), _jsx6("button", { className: "cs-item", "data-cs-ui": "invite", type: "button", disabled: busy, onClick: () => {
|
|
3247
3637
|
if (!member)
|
|
3248
3638
|
return;
|
|
3249
3639
|
setBusy(true);
|
|
3250
3640
|
createInvite(session, member).then(({ invite: code }) => {
|
|
3251
|
-
const
|
|
3252
|
-
url.search = "";
|
|
3253
|
-
url.hash = "";
|
|
3254
|
-
url.searchParams.set("cs-invite", code);
|
|
3255
|
-
const link = url.toString();
|
|
3641
|
+
const link = linkWith(pageHref(), "cs-invite", code);
|
|
3256
3642
|
setInviteLink(link);
|
|
3257
3643
|
return navigator.clipboard.writeText(link).then(() => say("Invite link copied")).catch(() => say("Invite link ready"));
|
|
3258
3644
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t create an invite.")).finally(() => setBusy(false));
|
|
3259
|
-
}, children:
|
|
3260
|
-
const
|
|
3645
|
+
}, children: _jsx6("span", { children: "Invite someone" }) }), inviteLink ? _jsxs6("div", { className: "cs-invite", children: [_jsx6("input", { "data-cs-ui": "invite-link", "aria-label": "Invite link", readOnly: true, value: inviteLink, onFocus: (event) => event.currentTarget.select() }), _jsx6("p", { className: "cs-hint", children: "Anyone with this link can join for 7 days. Changing the passphrase cancels it." })] }) : null, _jsx6("button", { className: "cs-item", "data-cs-ui": "lock", type: "button", onClick: lock, children: _jsx6("span", { children: "Lock" }) }), _jsx6("button", { className: "cs-item is-danger", "data-cs-ui": "reset", type: "button", onClick: () => setConfirmReset(true), children: _jsx6("span", { children: "Reset on this device\u2026" }) }), _jsx6("p", { className: "cs-hint", children: "C comment \xB7 I list \xB7 H pins" })] }) });
|
|
3646
|
+
const tabsBar = _jsx6("div", { className: "cs-tabs", role: "tablist", children: [
|
|
3261
3647
|
["page", "This page", counts.page, "scope-page"],
|
|
3262
3648
|
["all", "All pages", counts.all, "scope-all"],
|
|
3263
3649
|
["resolved", "Resolved", counts.resolved, "scope-resolved"]
|
|
3264
|
-
].map(([value, label, count, hook]) =>
|
|
3650
|
+
].map(([value, label, count, hook]) => _jsxs6("button", { type: "button", role: "tab", "aria-selected": tab === value, "data-cs-ui": hook, className: tab === value ? "is-on" : "", onClick: () => setTab(value), children: [label, count > 0 ? _jsx6("span", { className: "cs-count", children: count }) : null] }, value)) });
|
|
3651
|
+
const setViewPref = (next) => {
|
|
3652
|
+
setView(next);
|
|
3653
|
+
savePrefs({ ...prefs, view: next });
|
|
3654
|
+
};
|
|
3655
|
+
const table = _jsx6(CommentTable, { rows: listed, route, activeId, tabs: tabsBar, onList: () => setViewPref("list"), onClose: () => setSurface("none"), onSelect: (row) => {
|
|
3656
|
+
setView("list");
|
|
3657
|
+
const item = placed.find((candidate) => candidate.thread.id === row.thread.id);
|
|
3658
|
+
if (item)
|
|
3659
|
+
select(item, true);
|
|
3660
|
+
} });
|
|
3661
|
+
const inbox = _jsxs6("section", { className: "cs-card cs-sheet cs-hit", "data-cs-ui": "panel", "aria-label": "Comments", children: [_jsxs6("header", { className: "cs-sheet-head", children: [_jsx6("h2", { children: "Comments" }), _jsx6("button", { className: "cs-icon-button cs-push", "data-cs-ui": "table-view", type: "button", "aria-label": "Table view", onClick: () => setViewPref("table"), children: _jsx6(Table, {}) }), _jsx6("button", { className: "cs-icon-button", type: "button", "aria-label": "Close comments", onClick: () => setSurface("none"), children: _jsx6(Close, {}) })] }), tabsBar, error ? _jsx6("p", { className: "cs-error cs-pad", "data-cs-ui": "error", children: error }) : null, _jsxs6("div", { className: "cs-list", children: [listed.length === 0 ? _jsxs6("div", { className: "cs-empty", children: [_jsx6("p", { children: tab === "resolved" ? "Nothing resolved yet." : tab === "page" ? "No comments on this page." : "No open comments." }), tab !== "resolved" ? _jsxs6("button", { className: "cs-button", type: "button", onClick: startComment, children: [_jsx6(AddComment, {}), " Add a comment"] }) : null] }) : null, groups.map((group) => _jsxs6("div", { children: [group.route ? _jsx6("div", { className: "cs-group", children: group.route === route ? "This page" : group.route }) : null, group.items.map((item) => {
|
|
3265
3662
|
const { thread } = item;
|
|
3266
3663
|
const expanded = activeId === thread.id;
|
|
3267
3664
|
const first = thread.messages[0];
|
|
3268
3665
|
const note = statusNote(item.status, thread.anchor, thread.route === route);
|
|
3269
3666
|
const onPage = item.status === "pinned" || item.status === "drifted";
|
|
3270
|
-
return
|
|
3667
|
+
return _jsxs6("article", { className: expanded ? "cs-thread is-open" : "cs-thread", "data-cs-ui": "thread", "data-thread-id": thread.id, "data-cs-status": item.status, children: [_jsxs6("div", { className: "cs-thread-main", role: "button", tabIndex: 0, "aria-expanded": expanded, "aria-label": `${thread.createdBy}: ${first?.body ?? ""}`, onClick: () => select(item), onKeyDown: (event) => {
|
|
3271
3668
|
if (event.key === "Enter" || event.key === " ") {
|
|
3272
3669
|
event.preventDefault();
|
|
3273
|
-
|
|
3670
|
+
select(item);
|
|
3274
3671
|
}
|
|
3275
|
-
}, onMouseEnter: () => setPeekId(null), children: [
|
|
3672
|
+
}, onMouseEnter: () => setPeekId(null), children: [_jsx6(Avatar, { name: thread.createdBy }), _jsxs6("div", { className: "cs-thread-body", children: [_jsxs6("div", { className: "cs-meta", children: [_jsx6("strong", { children: thread.createdBy }), _jsx6("time", { dateTime: thread.createdAt, children: ago(thread.createdAt) }), _jsx6(RoleBadge, { role: thread.role })] }), first?.body ? _jsx6("p", { className: "cs-text", children: _jsx6(MessageText, { body: first.body, mentions: first.mentions }) }) : null, _jsx6(FieldValues, { fields: thread.fields, clamp: !expanded }), _jsx6(MessageAttachments, { attachments: first?.attachments }), first && deliveryText(deliveries[first.id]) ? _jsx6("div", { className: "cs-delivery", "data-cs-ui": "delivery", children: deliveryText(deliveries[first.id]) }) : null, _jsxs6("div", { className: "cs-target", children: [_jsx6("span", { title: thread.anchor.component?.file ?? void 0, children: targetName(thread.anchor) }), note ? _jsx6("span", { className: `cs-note is-${note.tone}`, children: note.text }) : null] }), !expanded && thread.messages.length > 1 ? _jsxs6("div", { className: "cs-replies", children: [thread.messages.length - 1, " ", thread.messages.length === 2 ? "reply" : "replies"] }) : null] })] }), expanded ? _jsxs6("div", { className: "cs-thread-more", children: [editingId === thread.id && settings ? _jsx6(DetailsEditor, { settings, thread, onClose: () => setEditingId(null), onSave: (patch) => updateThread(session, member, thread.id, patch).then(() => refresh()).then(() => say("Details saved")).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t save.")) }) : null, thread.messages.slice(1).map((message) => _jsxs6("div", { className: "cs-message", children: [_jsx6(Avatar, { name: message.author, size: 20 }), _jsxs6("div", { children: [_jsxs6("div", { className: "cs-meta", children: [_jsx6("strong", { children: message.author }), _jsx6("time", { dateTime: message.createdAt, children: ago(message.createdAt) })] }), _jsx6("p", { className: "cs-text", children: _jsx6(MessageText, { body: message.body, mentions: message.mentions }) }), _jsx6(MessageAttachments, { attachments: message.attachments }), deliveryText(deliveries[message.id]) ? _jsx6("div", { className: "cs-delivery", "data-cs-ui": "delivery", children: deliveryText(deliveries[message.id]) }) : null] })] }, message.id)), _jsxs6("form", { className: "cs-reply", onSubmit: async (event) => {
|
|
3276
3673
|
event.preventDefault();
|
|
3277
3674
|
if (!member || !reply.trim() && !replyFiles.ids.length || replyFiles.uploading)
|
|
3278
3675
|
return;
|
|
@@ -3298,7 +3695,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3298
3695
|
}
|
|
3299
3696
|
await refresh();
|
|
3300
3697
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t reply.")).finally(() => setBusy(false));
|
|
3301
|
-
}, children: [
|
|
3698
|
+
}, children: [_jsx6(MentionTextarea, { "data-cs-ui": "reply-body", "aria-label": "Reply", placeholder: "Reply\u2026 (@ to mention)", rows: 1, value: reply, onValueChange: setReply, members, selfEmail: readJwt(member ?? "")?.email, onMention: (person) => {
|
|
3302
3699
|
replyPicked.current.set(person.id, person);
|
|
3303
3700
|
const element = thread.route === route ? resolveInDocument(thread.anchor, route).element : null;
|
|
3304
3701
|
if (element && !replyShot.current)
|
|
@@ -3309,25 +3706,25 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3309
3706
|
event.preventDefault();
|
|
3310
3707
|
replyFiles.add(files);
|
|
3311
3708
|
}
|
|
3312
|
-
} }),
|
|
3709
|
+
} }), _jsx6(AttachButtons, { onFiles: replyFiles.add, capturing, onSnapshot: () => snapshot(thread.route === route ? resolveInDocument(thread.anchor, route).element : null, replyFiles.add) }), _jsx6("button", { className: "cs-send", "data-cs-ui": "reply-submit", type: "submit", "aria-label": "Post reply", disabled: busy || replyFiles.uploading || !reply.trim() && !replyFiles.ids.length, children: _jsx6(Send, {}) })] }), _jsx6(AttachmentTray, { items: replyFiles.items, onRemove: replyFiles.remove }), _jsxs6("div", { className: "cs-actions", children: [_jsxs6("button", { className: "cs-button", "data-cs-ui": "resolve", type: "button", onClick: () => {
|
|
3313
3710
|
if (!member)
|
|
3314
3711
|
return;
|
|
3315
3712
|
const next = thread.status === "resolved" ? "open" : "resolved";
|
|
3316
3713
|
updateThread(session, member, thread.id, { status: next }).then(() => refresh()).then(() => say(next === "resolved" ? "Resolved" : "Reopened")).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t update."));
|
|
3317
|
-
}, children: [thread.status === "resolved" ?
|
|
3714
|
+
}, children: [thread.status === "resolved" ? _jsx6(Undo, {}) : _jsx6(Check, {}), thread.status === "resolved" ? "Reopen" : "Resolve"] }), onPage && thread.status === "open" ? _jsxs6("button", { className: "cs-button", type: "button", onClick: () => showOnPage(thread), children: [_jsx6(Target, {}), " Show"] }) : null, detailsEnabled(settings) && editingId !== thread.id ? _jsxs6("button", { className: "cs-button", "data-cs-ui": "details-edit", type: "button", onClick: () => setEditingId(thread.id), children: [_jsx6(Pencil, {}), " Details"] }) : null, !onPage && thread.status === "open" && thread.route === route ? _jsxs6("button", { className: "cs-button", "data-cs-ui": "reattach", type: "button", onClick: () => {
|
|
3318
3715
|
setReattachId(thread.id);
|
|
3319
3716
|
setMode("reattach");
|
|
3320
3717
|
setSurface("none");
|
|
3321
|
-
}, children: [
|
|
3718
|
+
}, children: [_jsx6(Relink, {}), " Reattach"] }) : null] })] }) : null] }, thread.id);
|
|
3322
3719
|
})] }, group.route ?? "all"))] })] });
|
|
3323
|
-
const ui =
|
|
3720
|
+
const ui = _jsxs6("div", { id: "cs-root", className: [
|
|
3324
3721
|
inside ? "cs-inside" : "cs-fixed",
|
|
3325
3722
|
`cs-at-${barPosition.replace("bottom-", "")}`,
|
|
3326
3723
|
theme === "dark" || theme === "system" && systemDark ? "cs-dark" : ""
|
|
3327
|
-
].join(" "), children: [highlight ?
|
|
3724
|
+
].join(" "), children: [highlight ? _jsxs6(_Fragment3, { children: [_jsx6("div", { className: "cs-outline", style: { top: highlight.top, left: highlight.left, width: highlight.width, height: highlight.height } }), _jsx6("div", { className: "cs-outline-label", style: { top: Math.max(4, highlight.top - 26), left: highlight.left }, children: highlight.label })] }) : null, flash ? _jsx6("div", { className: "cs-outline is-flash", style: { top: flash.top, left: flash.left, width: flash.width, height: flash.height } }) : null, mode !== "browse" ? _jsxs6("div", { className: "cs-banner cs-hit", "data-cs-ui": "mode", children: [_jsx6(Cursor, {}), _jsx6("span", { children: mode === "reattach" ? "Click the element this comment belongs to" : "Click anything to comment" }), _jsx6("button", { type: "button", onClick: () => {
|
|
3328
3725
|
setMode("browse");
|
|
3329
3726
|
setReattachId(null);
|
|
3330
|
-
}, children: "Cancel" })] }) : null, pinsHidden ? null : placed.map((item) => item.top == null || item.left == null || item.status !== "pinned" && item.status !== "drifted" ? null :
|
|
3727
|
+
}, children: "Cancel" })] }) : null, pinsHidden ? null : placed.map((item) => item.top == null || item.left == null || item.status !== "pinned" && item.status !== "drifted" ? null : _jsxs6("button", { type: "button", className: [
|
|
3331
3728
|
"cs-pin",
|
|
3332
3729
|
item.status === "drifted" ? "is-drifted" : "",
|
|
3333
3730
|
activeId === item.thread.id && surface === "inbox" ? "is-active" : ""
|
|
@@ -3336,9 +3733,9 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3336
3733
|
setTab("page");
|
|
3337
3734
|
setActiveId(item.thread.id);
|
|
3338
3735
|
setSurface("inbox");
|
|
3339
|
-
}, children: [
|
|
3736
|
+
}, children: [_jsx6(Avatar, { name: item.thread.createdBy, size: 22 }), item.thread.messages.length > 1 ? _jsx6("span", { className: "cs-pin-count", children: item.thread.messages.length }) : null] }, item.thread.id)), peek && !pinsHidden && peek.top != null && peek.left != null ? _jsxs6("div", { className: "cs-peek", style: { top: peek.top + 18, left: Math.max(8, peek.left - 12) }, children: [_jsxs6("div", { className: "cs-meta", children: [_jsx6(Avatar, { name: peek.thread.createdBy, size: 18 }), _jsx6("strong", { children: peek.thread.createdBy }), _jsx6("time", { children: ago(peek.thread.createdAt) })] }), _jsx6("p", { className: "cs-text", children: _jsx6(MessageText, { body: peek.thread.messages[0]?.body ?? "", mentions: peek.thread.messages[0]?.mentions }) }), peek.thread.messages.length > 1 ? _jsxs6("div", { className: "cs-replies", children: [peek.thread.messages.length - 1, " ", peek.thread.messages.length === 2 ? "reply" : "replies"] }) : null] }) : null, draft ? _jsxs6("form", { className: "cs-card cs-composer cs-hit", "data-cs-ui": "composer", style: { top: draft.top, left: draft.left }, onSubmit: async (event) => {
|
|
3340
3737
|
event.preventDefault();
|
|
3341
|
-
if (!member || !
|
|
3738
|
+
if (!member || !canPost || draftFiles.uploading)
|
|
3342
3739
|
return;
|
|
3343
3740
|
setBusy(true);
|
|
3344
3741
|
const picked = mentionsIn(body, draftPicked.current);
|
|
@@ -3346,10 +3743,13 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3346
3743
|
createThread(session, member, draft.anchor, body, {
|
|
3347
3744
|
mentions: picked.map((person) => person.id),
|
|
3348
3745
|
screenshot,
|
|
3349
|
-
attachments: draftFiles.ids
|
|
3746
|
+
attachments: draftFiles.ids,
|
|
3747
|
+
url: pageHref(),
|
|
3748
|
+
...detailsPayload(settings, draftDetails)
|
|
3350
3749
|
}).then(async ({ thread, delivery }) => {
|
|
3351
3750
|
setDraft(null);
|
|
3352
3751
|
setBody("");
|
|
3752
|
+
setDraftDetails(noDetails);
|
|
3353
3753
|
draftFiles.reset();
|
|
3354
3754
|
setError("");
|
|
3355
3755
|
draftPicked.current = /* @__PURE__ */ new Map();
|
|
@@ -3367,7 +3767,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3367
3767
|
setSurface("inbox");
|
|
3368
3768
|
await refresh();
|
|
3369
3769
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t post.")).finally(() => setBusy(false));
|
|
3370
|
-
}, children: [
|
|
3770
|
+
}, children: [_jsxs6("div", { className: "cs-composer-target", children: [_jsx6(Target, {}), _jsx6("span", { className: "cs-composer-name", children: targetName(draft.anchor) }), draft.anchor.component?.file ? _jsx6("code", { children: draft.anchor.component.file }) : null, _jsx6("button", { className: "cs-icon-button", type: "button", "aria-label": "Discard", onClick: () => setDraft(null), children: _jsx6(Close, {}) })] }), _jsxs6("div", { className: "cs-composer-body", children: [_jsx6(Avatar, { name: memberName, size: 24 }), _jsx6(MentionTextarea, { "data-cs-ui": "composer-body", "aria-label": "Comment", placeholder: "Add a comment\u2026 (@ to mention)", value: body, autoFocus: true, onValueChange: setBody, members, selfEmail: readJwt(member ?? "")?.email, onMention: (person) => {
|
|
3371
3771
|
draftPicked.current.set(person.id, person);
|
|
3372
3772
|
if (!draftShot.current)
|
|
3373
3773
|
draftShot.current = captureAround(draft.element);
|
|
@@ -3377,7 +3777,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3377
3777
|
event.preventDefault();
|
|
3378
3778
|
draftFiles.add(files);
|
|
3379
3779
|
}
|
|
3380
|
-
} })] }),
|
|
3780
|
+
} })] }), _jsx6(DetailsInput, { settings, value: draftDetails, onChange: setDraftDetails, onKeyDown: submitOnCmdEnter }), _jsx6(AttachmentTray, { items: draftFiles.items, onRemove: draftFiles.remove }), error ? _jsx6("p", { className: "cs-error cs-pad", children: error }) : null, _jsxs6("div", { className: "cs-composer-foot", children: [_jsx6(AttachButtons, { onFiles: draftFiles.add, capturing, onSnapshot: () => snapshot(draft.element, draftFiles.add) }), _jsx6("span", { className: "cs-hint", children: capturing ? "Capturing\u2026" : draftFiles.uploading ? "Uploading\u2026" : busy ? "Posting\u2026" : "\u2318\u21B5 to post" }), _jsx6("button", { className: "cs-send", "data-cs-ui": "composer-submit", type: "submit", "aria-label": "Post comment", disabled: busy || draftFiles.uploading || !canPost, children: _jsx6(Send, {}) })] })] }) : null, surface === "unlock" && !member ? unlockCard : null, surface === "menu" && member ? menuCard : null, surface === "settings" && member ? _jsx6(SettingsPanel, { settings, onChange: changeSettings, onBack: () => setSurface("menu"), error }) : null, surface === "inbox" && member ? view === "table" ? table : inbox : null, toast ? _jsx6("div", { className: "cs-toast", role: "status", children: toast }) : null, bar] });
|
|
3381
3781
|
return createPortal2(ui, host);
|
|
3382
3782
|
}
|
|
3383
3783
|
export {
|