@devaccel-labs/comment-service 0.5.3 → 0.6.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
CHANGED
|
@@ -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 useMemo2, useRef as useRef3, useState as
|
|
357
|
+
import { jsx as _jsx5, jsxs as _jsxs5, Fragment as _Fragment3 } from "react/jsx-runtime";
|
|
358
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef3, useState as useState4 } 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,118 @@ 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
|
+
|
|
503
|
+
// ../react/dist/details.js
|
|
504
|
+
var noDetails = { role: null, fields: {} };
|
|
505
|
+
function detailsEnabled(settings) {
|
|
506
|
+
if (!settings)
|
|
507
|
+
return false;
|
|
508
|
+
return settings.roles.enabled && settings.roles.options.length > 0 || settings.fields.enabled && settings.fields.items.length > 0;
|
|
509
|
+
}
|
|
510
|
+
function hasFieldValues(details) {
|
|
511
|
+
return Object.values(details.fields).some((value) => value.trim());
|
|
512
|
+
}
|
|
513
|
+
function detailsPayload(settings, details) {
|
|
514
|
+
if (!settings)
|
|
515
|
+
return {};
|
|
516
|
+
const payload = {};
|
|
517
|
+
if (settings.roles.enabled && details.role)
|
|
518
|
+
payload.role = details.role;
|
|
519
|
+
if (settings.fields.enabled) {
|
|
520
|
+
const fields = Object.fromEntries(Object.entries(details.fields).filter(([, value]) => value.trim()));
|
|
521
|
+
if (Object.keys(fields).length)
|
|
522
|
+
payload.fields = fields;
|
|
523
|
+
}
|
|
524
|
+
return payload;
|
|
525
|
+
}
|
|
526
|
+
function fieldId(label, taken) {
|
|
527
|
+
const base = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32) || "field";
|
|
528
|
+
let id = base;
|
|
529
|
+
for (let n = 2; taken.some((field) => field.id === id); n += 1)
|
|
530
|
+
id = `${base}-${n}`;
|
|
531
|
+
return id;
|
|
532
|
+
}
|
|
533
|
+
function DetailsInput({ settings, value, onChange, onKeyDown }) {
|
|
534
|
+
if (!settings || !detailsEnabled(settings))
|
|
535
|
+
return null;
|
|
536
|
+
const roles = settings.roles.enabled ? settings.roles.options : [];
|
|
537
|
+
const fields = settings.fields.enabled ? settings.fields.items : [];
|
|
538
|
+
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] });
|
|
539
|
+
}
|
|
540
|
+
function RoleBadge({ role }) {
|
|
541
|
+
return role ? _jsx2("span", { className: "cs-role", "data-cs-ui": "role", children: role }) : null;
|
|
542
|
+
}
|
|
543
|
+
function FieldValues({ fields, clamp = false }) {
|
|
544
|
+
if (!fields.length)
|
|
545
|
+
return null;
|
|
546
|
+
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)) });
|
|
547
|
+
}
|
|
548
|
+
function DetailsEditor({ settings, thread, onSave, onClose }) {
|
|
549
|
+
const [value, setValue] = useState(() => ({
|
|
550
|
+
role: thread.role,
|
|
551
|
+
fields: Object.fromEntries(thread.fields.map((field) => [field.id, field.value]))
|
|
552
|
+
}));
|
|
553
|
+
const [busy, setBusy] = useState(false);
|
|
554
|
+
return _jsxs2("form", { className: "cs-details-edit", "data-cs-ui": "details-editor", onSubmit: (event) => {
|
|
555
|
+
event.preventDefault();
|
|
556
|
+
const patch = {};
|
|
557
|
+
if (settings.roles.enabled && value.role !== thread.role)
|
|
558
|
+
patch.role = value.role;
|
|
559
|
+
if (settings.fields.enabled) {
|
|
560
|
+
patch.fields = Object.fromEntries(settings.fields.items.map((field) => [field.id, value.fields[field.id] ?? ""]));
|
|
561
|
+
}
|
|
562
|
+
setBusy(true);
|
|
563
|
+
onSave(patch).then(onClose).finally(() => setBusy(false));
|
|
564
|
+
}, 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" })] })] });
|
|
565
|
+
}
|
|
566
|
+
function AddRow({ label, hook, onAdd }) {
|
|
567
|
+
const [text, setText] = useState("");
|
|
568
|
+
return _jsxs2("form", { className: "cs-add-row", onSubmit: (event) => {
|
|
569
|
+
event.preventDefault();
|
|
570
|
+
const next = text.trim().replace(/\s+/g, " ");
|
|
571
|
+
if (!next)
|
|
572
|
+
return;
|
|
573
|
+
onAdd(next);
|
|
574
|
+
setText("");
|
|
575
|
+
}, 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, {}) })] });
|
|
576
|
+
}
|
|
577
|
+
function SettingsPanel({ settings, onChange, onBack, error }) {
|
|
578
|
+
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) => {
|
|
579
|
+
if (settings.roles.options.some((item) => item.toLowerCase() === role.toLowerCase()))
|
|
580
|
+
return;
|
|
581
|
+
onChange({ ...settings, roles: { ...settings.roles, options: [...settings.roles.options, role.slice(0, 40)] } });
|
|
582
|
+
} })] }) : 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({
|
|
583
|
+
...settings,
|
|
584
|
+
fields: { ...settings.fields, items: [...settings.fields.items, { id: fieldId(label, settings.fields.items), label }] }
|
|
585
|
+
}) })] }) : 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." })] })] });
|
|
586
|
+
}
|
|
587
|
+
|
|
470
588
|
// ../react/dist/format.js
|
|
471
589
|
var palette = ["#007aff", "#34c759", "#ff9500", "#af52de", "#ff2d55", "#5ac8fa", "#5856d6", "#a2845e"];
|
|
472
590
|
function initials(name) {
|
|
@@ -537,31 +655,9 @@ function deliveryText(delivery) {
|
|
|
537
655
|
return parts.length ? parts.join(" \xB7 ") : null;
|
|
538
656
|
}
|
|
539
657
|
|
|
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
658
|
// ../react/dist/attachments.js
|
|
563
|
-
import { jsx as
|
|
564
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
659
|
+
import { jsx as _jsx3, jsxs as _jsxs3 } from "react/jsx-runtime";
|
|
660
|
+
import { useCallback, useEffect, useRef, useState as useState2 } from "react";
|
|
565
661
|
var MAX_BYTES = 10 * 1024 * 1024;
|
|
566
662
|
var MAX_FILES = 5;
|
|
567
663
|
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,7 +670,7 @@ function formatSize(bytes) {
|
|
|
574
670
|
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
575
671
|
}
|
|
576
672
|
function useAttachments(session, memberToken, onError) {
|
|
577
|
-
const [items, setItems] =
|
|
673
|
+
const [items, setItems] = useState2([]);
|
|
578
674
|
const itemsRef = useRef(items);
|
|
579
675
|
itemsRef.current = items;
|
|
580
676
|
const patch = (key, next) => setItems((current) => current.map((item) => item.key === key ? { ...item, ...next } : item));
|
|
@@ -633,26 +729,26 @@ function filesFromClipboard(event) {
|
|
|
633
729
|
function AttachmentTray({ items, onRemove }) {
|
|
634
730
|
if (!items.length)
|
|
635
731
|
return null;
|
|
636
|
-
return
|
|
732
|
+
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
733
|
}
|
|
638
734
|
function AttachButtons({ onFiles, onSnapshot, capturing }) {
|
|
639
735
|
const input = useRef(null);
|
|
640
|
-
return
|
|
736
|
+
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
737
|
onFiles([...event.target.files ?? []].map((file) => ({ blob: file, name: file.name })));
|
|
642
738
|
event.target.value = "";
|
|
643
|
-
} }),
|
|
739
|
+
} }), _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
740
|
}
|
|
645
741
|
function MessageAttachments({ attachments }) {
|
|
646
742
|
if (!attachments?.length)
|
|
647
743
|
return null;
|
|
648
744
|
const images = attachments.filter((file) => file.contentType.startsWith("image/"));
|
|
649
745
|
const others = attachments.filter((file) => !file.contentType.startsWith("image/"));
|
|
650
|
-
return
|
|
746
|
+
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
747
|
}
|
|
652
748
|
|
|
653
749
|
// ../react/dist/mentions.js
|
|
654
|
-
import { Fragment as
|
|
655
|
-
import { useMemo, useRef as useRef2, useState as
|
|
750
|
+
import { Fragment as _Fragment2, jsx as _jsx4, jsxs as _jsxs4 } from "react/jsx-runtime";
|
|
751
|
+
import { useMemo, useRef as useRef2, useState as useState3 } from "react";
|
|
656
752
|
import { createPortal } from "react-dom";
|
|
657
753
|
function queryAt(text, caret) {
|
|
658
754
|
const match = text.slice(0, caret).match(/(^|\s)@([^\s@]{0,30})$/);
|
|
@@ -666,16 +762,16 @@ function mentionsIn(text, picked) {
|
|
|
666
762
|
}
|
|
667
763
|
function MessageText({ body, mentions }) {
|
|
668
764
|
if (!mentions?.length)
|
|
669
|
-
return
|
|
765
|
+
return _jsx4(_Fragment2, { children: body });
|
|
670
766
|
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
|
|
767
|
+
const parts = body.split(pattern).map((part, index) => index % 2 === 1 ? _jsx4("span", { className: "cs-mention", children: part }, index) : part);
|
|
768
|
+
return _jsx4(_Fragment2, { children: parts });
|
|
673
769
|
}
|
|
674
770
|
function MentionTextarea({ value, onValueChange, members, selfEmail, onMention, onKeyDown, ...rest }) {
|
|
675
771
|
const ref = useRef2(null);
|
|
676
|
-
const [caret, setCaret] =
|
|
677
|
-
const [index, setIndex] =
|
|
678
|
-
const [dismissed, setDismissed] =
|
|
772
|
+
const [caret, setCaret] = useState3(0);
|
|
773
|
+
const [index, setIndex] = useState3(0);
|
|
774
|
+
const [dismissed, setDismissed] = useState3(null);
|
|
679
775
|
const active = queryAt(value, caret);
|
|
680
776
|
const query = active?.query.toLowerCase() ?? null;
|
|
681
777
|
const matches = useMemo(() => {
|
|
@@ -702,7 +798,7 @@ function MentionTextarea({ value, onValueChange, members, selfEmail, onMention,
|
|
|
702
798
|
const rect = open ? ref.current?.getBoundingClientRect() : null;
|
|
703
799
|
const root = open ? ref.current?.closest("#cs-root") : null;
|
|
704
800
|
const below = rect ? window.innerHeight - rect.bottom > 220 : true;
|
|
705
|
-
return
|
|
801
|
+
return _jsxs4(_Fragment2, { children: [_jsx4("textarea", { ref, ...rest, value, "aria-autocomplete": "list", "aria-expanded": open, onChange: (event) => {
|
|
706
802
|
onValueChange(event.target.value);
|
|
707
803
|
setCaret(event.target.selectionStart ?? event.target.value.length);
|
|
708
804
|
setIndex(0);
|
|
@@ -728,13 +824,13 @@ function MentionTextarea({ value, onValueChange, members, selfEmail, onMention,
|
|
|
728
824
|
}
|
|
729
825
|
}
|
|
730
826
|
onKeyDown?.(event);
|
|
731
|
-
} }), open && rect && root ? createPortal(
|
|
827
|
+
} }), open && rect && root ? createPortal(_jsx4("div", { className: "cs-people cs-hit", role: "listbox", "data-cs-ui": "mention-list", style: {
|
|
732
828
|
left: Math.min(rect.left, window.innerWidth - 272),
|
|
733
829
|
...below ? { top: rect.bottom + 6 } : { bottom: window.innerHeight - rect.top + 6 }
|
|
734
|
-
}, children: matches.map((member) =>
|
|
830
|
+
}, 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
831
|
event.preventDefault();
|
|
736
832
|
insert(member);
|
|
737
|
-
}, onMouseEnter: () => setIndex(matches.indexOf(member)), children: [
|
|
833
|
+
}, 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
834
|
}
|
|
739
835
|
|
|
740
836
|
// ../../node_modules/modern-screenshot/dist/index.mjs
|
|
@@ -2568,6 +2664,50 @@ var toolbarCss = `
|
|
|
2568
2664
|
#cs-root .cs-composer-foot { display: flex; align-items: center; gap: 6px; padding: 6px 8px 8px 40px; }
|
|
2569
2665
|
#cs-root .cs-composer-foot .cs-hint { margin-left: auto; }
|
|
2570
2666
|
|
|
2667
|
+
/* Roles and fields */
|
|
2668
|
+
#cs-root .cs-details-input { display: grid; gap: 8px; padding: 2px 12px 6px 46px; }
|
|
2669
|
+
#cs-root .cs-details-edit .cs-details-input { padding: 0; }
|
|
2670
|
+
#cs-root .cs-detail-field { display: grid; gap: 3px; }
|
|
2671
|
+
#cs-root .cs-detail-field span, #cs-root .cs-roles-label, #cs-root .cs-detail-list dt {
|
|
2672
|
+
font-size: 11px; font-weight: 600; color: var(--cs-text-3); text-transform: uppercase; letter-spacing: 0.03em;
|
|
2673
|
+
}
|
|
2674
|
+
#cs-root .cs-detail-field textarea { padding: 6px 8px; min-height: 30px; max-height: 120px; field-sizing: content; }
|
|
2675
|
+
#cs-root .cs-thread.is-open .cs-detail-field textarea { background: var(--cs-card); }
|
|
2676
|
+
#cs-root .cs-roles { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
|
|
2677
|
+
#cs-root .cs-roles-label { margin-right: 4px; }
|
|
2678
|
+
#cs-root .cs-chip {
|
|
2679
|
+
display: inline-flex; align-items: center; gap: 2px; height: 24px; padding: 0 9px; border-radius: 999px;
|
|
2680
|
+
background: var(--cs-fill); font-size: 12px; font-weight: 500; color: var(--cs-text-2);
|
|
2681
|
+
}
|
|
2682
|
+
#cs-root button.cs-chip:hover { background: var(--cs-fill-2); }
|
|
2683
|
+
#cs-root .cs-chip.is-on, #cs-root button.cs-chip.is-on:hover { background: var(--cs-accent); color: #fff; }
|
|
2684
|
+
#cs-root .cs-chip.is-static { padding-right: 3px; color: var(--cs-text); }
|
|
2685
|
+
#cs-root .cs-chip.is-static button {
|
|
2686
|
+
width: 18px; height: 18px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; color: var(--cs-text-3);
|
|
2687
|
+
}
|
|
2688
|
+
#cs-root .cs-chip.is-static button:hover { background: var(--cs-fill-2); color: var(--cs-text); }
|
|
2689
|
+
#cs-root .cs-chip.is-static .cs-icon { width: 11px; height: 11px; }
|
|
2690
|
+
#cs-root .cs-chips { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
2691
|
+
#cs-root .cs-role {
|
|
2692
|
+
display: inline-flex; align-items: center; height: 18px; padding: 0 7px; border-radius: 999px;
|
|
2693
|
+
background: var(--cs-accent-soft); color: var(--cs-accent); font-size: 11px; font-weight: 600;
|
|
2694
|
+
}
|
|
2695
|
+
#cs-root .cs-detail-list { display: grid; gap: 6px; margin: 6px 0 0; }
|
|
2696
|
+
#cs-root .cs-detail-list > div { padding: 6px 8px; border-radius: 8px; background: var(--cs-fill); }
|
|
2697
|
+
#cs-root .cs-thread.is-open .cs-detail-list > div { background: var(--cs-card); }
|
|
2698
|
+
#cs-root .cs-detail-list dd { margin: 1px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
2699
|
+
#cs-root .cs-detail-list.is-clamped dd { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
2700
|
+
#cs-root .cs-details-edit { display: grid; gap: 8px; }
|
|
2701
|
+
#cs-root .cs-details-edit .cs-row-end { margin-top: 0; }
|
|
2702
|
+
#cs-root .cs-settings { width: 280px; }
|
|
2703
|
+
#cs-root .cs-settings-head { display: flex; align-items: center; gap: 4px; padding: 4px 4px 6px; }
|
|
2704
|
+
#cs-root .cs-settings-head h2 { font-size: 14px; font-weight: 600; }
|
|
2705
|
+
#cs-root .cs-settings-body { display: grid; gap: 6px; padding: 2px 8px 8px; }
|
|
2706
|
+
#cs-root .cs-field-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; min-height: 28px; padding-left: 2px; }
|
|
2707
|
+
#cs-root .cs-add-row { display: flex; align-items: center; gap: 4px; }
|
|
2708
|
+
#cs-root .cs-add-row input { padding: 5px 8px; }
|
|
2709
|
+
#cs-root .cs-settings .cs-hint { padding: 6px 8px 4px; }
|
|
2710
|
+
|
|
2571
2711
|
/* Attachments */
|
|
2572
2712
|
#cs-root .cs-attach { display: inline-flex; gap: 0; flex: none; }
|
|
2573
2713
|
#cs-root .cs-tray { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 12px 2px 46px; }
|
|
@@ -2690,6 +2830,12 @@ function placeAgainst(element, host) {
|
|
|
2690
2830
|
height: rect.height
|
|
2691
2831
|
};
|
|
2692
2832
|
}
|
|
2833
|
+
function pageHref() {
|
|
2834
|
+
const url = new URL(window.location.href);
|
|
2835
|
+
url.searchParams.delete("cs-thread");
|
|
2836
|
+
url.searchParams.delete("cs-invite");
|
|
2837
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
2838
|
+
}
|
|
2693
2839
|
function isTyping(target) {
|
|
2694
2840
|
if (!(target instanceof HTMLElement))
|
|
2695
2841
|
return false;
|
|
@@ -2712,44 +2858,47 @@ async function loadSession(sessionPath) {
|
|
|
2712
2858
|
return { apiUrl: payload.apiUrl, project: payload.project, environment: payload.environment, hostToken: payload.hostToken };
|
|
2713
2859
|
}
|
|
2714
2860
|
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] =
|
|
2861
|
+
return _jsx5("span", { className: "cs-avatar", style: { width: size, height: size, fontSize: size * 0.4, background: colorFor(name) }, children: initials(name) });
|
|
2862
|
+
}
|
|
2863
|
+
function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs = 4e3, position = "bottom-center", theme = "light", navigate }) {
|
|
2864
|
+
const [phase, setPhase] = useState4("boot");
|
|
2865
|
+
const [session, setSession] = useState4(null);
|
|
2866
|
+
const [member, setMember] = useState4(null);
|
|
2867
|
+
const [threads, setThreads] = useState4([]);
|
|
2868
|
+
const [route, setRoute] = useState4("/");
|
|
2869
|
+
const [surface, setSurface] = useState4("none");
|
|
2870
|
+
const [tab, setTab] = useState4("page");
|
|
2871
|
+
const [mode, setMode] = useState4("browse");
|
|
2872
|
+
const [reattachId, setReattachId] = useState4(null);
|
|
2873
|
+
const [activeId, setActiveId] = useState4(null);
|
|
2874
|
+
const [peekId, setPeekId] = useState4(null);
|
|
2875
|
+
const [draft, setDraft] = useState4(null);
|
|
2876
|
+
const [body, setBody] = useState4("");
|
|
2877
|
+
const [reply, setReply] = useState4("");
|
|
2878
|
+
const [name, setName] = useState4("");
|
|
2879
|
+
const [passphrase, setPassphrase] = useState4("");
|
|
2880
|
+
const [email, setEmail] = useState4("");
|
|
2881
|
+
const [members, setMembers] = useState4([]);
|
|
2882
|
+
const [profile, setProfile] = useState4(null);
|
|
2883
|
+
const [deliveries, setDeliveries] = useState4({});
|
|
2884
|
+
const [invite, setInvite] = useState4(null);
|
|
2885
|
+
const [capturing, setCapturing] = useState4(false);
|
|
2886
|
+
const [inviteLink, setInviteLink] = useState4(null);
|
|
2887
|
+
const [renaming, setRenaming] = useState4(null);
|
|
2888
|
+
const [confirmReset, setConfirmReset] = useState4(false);
|
|
2889
|
+
const [error, setError] = useState4("");
|
|
2890
|
+
const [toast, setToast] = useState4("");
|
|
2891
|
+
const [busy, setBusy] = useState4(false);
|
|
2892
|
+
const [prefs, setPrefs] = useState4({});
|
|
2893
|
+
const [portal, setPortal] = useState4(null);
|
|
2894
|
+
const [mounted, setMounted] = useState4(false);
|
|
2895
|
+
const [highlight, setHighlight] = useState4(null);
|
|
2896
|
+
const [flash, setFlash] = useState4(null);
|
|
2897
|
+
const [placed, setPlaced] = useState4([]);
|
|
2898
|
+
const [systemDark, setSystemDark] = useState4(false);
|
|
2899
|
+
const [settings, setSettings] = useState4(null);
|
|
2900
|
+
const [draftDetails, setDraftDetails] = useState4(noDetails);
|
|
2901
|
+
const [editingId, setEditingId] = useState4(null);
|
|
2753
2902
|
const threadsRef = useRef3(threads);
|
|
2754
2903
|
threadsRef.current = threads;
|
|
2755
2904
|
const modeRef = useRef3(mode);
|
|
@@ -2759,6 +2908,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
2759
2908
|
const draftShot = useRef3(null);
|
|
2760
2909
|
const replyShot = useRef3(null);
|
|
2761
2910
|
const pendingThread = useRef3(null);
|
|
2911
|
+
const arrival = useRef3(null);
|
|
2762
2912
|
const draftFiles = useAttachments(session, member, setError);
|
|
2763
2913
|
const replyFiles = useAttachments(session, member, setError);
|
|
2764
2914
|
const resetReplyFiles = replyFiles.reset;
|
|
@@ -2858,7 +3008,9 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
2858
3008
|
const refresh = useCallback2(async () => {
|
|
2859
3009
|
if (!session || !member)
|
|
2860
3010
|
return;
|
|
2861
|
-
|
|
3011
|
+
const next = await listThreads(session, member);
|
|
3012
|
+
setThreads(next.threads);
|
|
3013
|
+
setSettings(next.settings);
|
|
2862
3014
|
}, [member, session]);
|
|
2863
3015
|
useEffect2(() => {
|
|
2864
3016
|
if (!session || !member)
|
|
@@ -2866,8 +3018,10 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
2866
3018
|
let stop = false;
|
|
2867
3019
|
const tick = () => {
|
|
2868
3020
|
listThreads(session, member).then((next) => {
|
|
2869
|
-
if (
|
|
2870
|
-
|
|
3021
|
+
if (stop)
|
|
3022
|
+
return;
|
|
3023
|
+
setThreads(next.threads);
|
|
3024
|
+
setSettings(next.settings);
|
|
2871
3025
|
}).catch((caught) => {
|
|
2872
3026
|
if (caught instanceof ApiError && caught.status === 401) {
|
|
2873
3027
|
localStorage.removeItem(storageKey(session));
|
|
@@ -3048,7 +3202,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3048
3202
|
const box = placeAgainst(element, target);
|
|
3049
3203
|
if (modeRef.current === "reattach" && reattachId && member) {
|
|
3050
3204
|
setBusy(true);
|
|
3051
|
-
updateThread(session, member, reattachId, { anchor }).then(() => refresh()).then(() => {
|
|
3205
|
+
updateThread(session, member, reattachId, { anchor, url: pageHref() }).then(() => refresh()).then(() => {
|
|
3052
3206
|
setMode("browse");
|
|
3053
3207
|
setReattachId(null);
|
|
3054
3208
|
setSurface("inbox");
|
|
@@ -3069,6 +3223,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3069
3223
|
draftShot.current = null;
|
|
3070
3224
|
draftFiles.reset();
|
|
3071
3225
|
setBody("");
|
|
3226
|
+
setDraftDetails(noDetails);
|
|
3072
3227
|
setMode("browse");
|
|
3073
3228
|
setError("");
|
|
3074
3229
|
};
|
|
@@ -3141,13 +3296,63 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3141
3296
|
setSurface("inbox");
|
|
3142
3297
|
if (item.status === "pinned" || item.status === "drifted")
|
|
3143
3298
|
showOnPage(item.thread);
|
|
3299
|
+
else
|
|
3300
|
+
arrival.current = { id, until: Date.now() + 6e3 };
|
|
3144
3301
|
}, [member, placed, route, showOnPage]);
|
|
3302
|
+
useEffect2(() => {
|
|
3303
|
+
const pending = arrival.current;
|
|
3304
|
+
if (!pending)
|
|
3305
|
+
return;
|
|
3306
|
+
if (Date.now() > pending.until) {
|
|
3307
|
+
arrival.current = null;
|
|
3308
|
+
return;
|
|
3309
|
+
}
|
|
3310
|
+
const item = placed.find((candidate) => candidate.thread.id === pending.id);
|
|
3311
|
+
if (!item || item.status !== "pinned" && item.status !== "drifted")
|
|
3312
|
+
return;
|
|
3313
|
+
arrival.current = null;
|
|
3314
|
+
showOnPage(item.thread);
|
|
3315
|
+
}, [placed, showOnPage]);
|
|
3316
|
+
const select = useCallback2((item) => {
|
|
3317
|
+
const { thread } = item;
|
|
3318
|
+
if (activeId === thread.id) {
|
|
3319
|
+
setActiveId(null);
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3322
|
+
setActiveId(thread.id);
|
|
3323
|
+
setEditingId(null);
|
|
3324
|
+
const onPage = item.status === "pinned" || item.status === "drifted";
|
|
3325
|
+
const target = thread.url || thread.route;
|
|
3326
|
+
const legacyHere = thread.url === thread.route && thread.route === route;
|
|
3327
|
+
if (target === pageHref() || onPage && legacyHere)
|
|
3328
|
+
return;
|
|
3329
|
+
if (navigate) {
|
|
3330
|
+
arrival.current = { id: thread.id, until: Date.now() + 6e3 };
|
|
3331
|
+
navigate(target);
|
|
3332
|
+
return;
|
|
3333
|
+
}
|
|
3334
|
+
const url = new URL(target, window.location.origin);
|
|
3335
|
+
url.searchParams.set("cs-thread", thread.id);
|
|
3336
|
+
window.location.assign(url.toString());
|
|
3337
|
+
}, [activeId, navigate, route]);
|
|
3145
3338
|
if (!mounted || phase !== "ready" || !session)
|
|
3146
3339
|
return null;
|
|
3147
3340
|
const memberName = member ? readJwt(member)?.name ?? name : "";
|
|
3148
3341
|
const host = portal ?? document.body;
|
|
3149
3342
|
const inside = host !== document.body;
|
|
3150
3343
|
const peek = peekId ? placed.find((item) => item.thread.id === peekId && item.top != null) : void 0;
|
|
3344
|
+
const canPost = Boolean(body.trim() || draftFiles.ids.length || settings?.fields.enabled && hasFieldValues(draftDetails));
|
|
3345
|
+
const changeSettings = (next) => {
|
|
3346
|
+
if (!member)
|
|
3347
|
+
return;
|
|
3348
|
+
const previous = settings;
|
|
3349
|
+
setSettings(next);
|
|
3350
|
+
setError("");
|
|
3351
|
+
saveSettings(session, member, next).then(setSettings).catch((caught) => {
|
|
3352
|
+
setSettings(previous);
|
|
3353
|
+
setError(caught instanceof Error ? caught.message : "Couldn\u2019t save.");
|
|
3354
|
+
});
|
|
3355
|
+
};
|
|
3151
3356
|
const lock = () => {
|
|
3152
3357
|
localStorage.removeItem(storageKey(session));
|
|
3153
3358
|
setMember(null);
|
|
@@ -3173,22 +3378,22 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3173
3378
|
setDraft(null);
|
|
3174
3379
|
setSurface("unlock");
|
|
3175
3380
|
};
|
|
3176
|
-
const bar =
|
|
3381
|
+
const bar = _jsx5("div", { className: "cs-bar cs-hit", "data-cs-ui": "bar", role: "toolbar", "aria-label": "Comments toolbar", children: !member ? _jsxs5("button", { className: "cs-bar-lock", "data-cs-ui": "launcher", type: "button", "aria-label": "Comments", onClick: () => {
|
|
3177
3382
|
setError("");
|
|
3178
3383
|
setSurface(surface === "unlock" ? "none" : "unlock");
|
|
3179
|
-
}, children: [
|
|
3384
|
+
}, children: [_jsx5(Lock, {}), _jsx5("span", { children: "Comments" })] }) : _jsxs5(_Fragment3, { children: [_jsx5("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: _jsx5(AddComment, {}) }), _jsxs5("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
3385
|
setError("");
|
|
3181
3386
|
setMode("browse");
|
|
3182
3387
|
setSurface(surface === "inbox" ? "none" : "inbox");
|
|
3183
|
-
}, children: [
|
|
3388
|
+
}, children: [_jsx5(Inbox, {}), counts.page > 0 ? _jsx5("span", { className: "cs-badge", children: counts.page }) : null] }), _jsx5("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 ? _jsx5(EyeOff, {}) : _jsx5(Eye, {}) }), _jsx5("span", { className: "cs-divider" }), _jsx5("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
3389
|
setConfirmReset(false);
|
|
3185
3390
|
setRenaming(null);
|
|
3186
3391
|
if (surface !== "menu" && member) {
|
|
3187
3392
|
getProfile(session, member).then(setProfile).catch(() => void 0);
|
|
3188
3393
|
}
|
|
3189
3394
|
setSurface(surface === "menu" ? "none" : "menu");
|
|
3190
|
-
}, children:
|
|
3191
|
-
const unlockCard =
|
|
3395
|
+
}, children: _jsx5(Avatar, { name: memberName, size: 24 }) })] }) });
|
|
3396
|
+
const unlockCard = _jsxs5("section", { className: "cs-card cs-unlock cs-hit", "data-cs-ui": "panel", "aria-label": "Unlock comments", children: [_jsx5("div", { className: "cs-unlock-icon", children: _jsx5(Lock, { size: 18 }) }), _jsx5("h2", { children: "Comments" }), _jsx5("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." }), _jsxs5("form", { onSubmit: (event) => {
|
|
3192
3397
|
event.preventDefault();
|
|
3193
3398
|
setBusy(true);
|
|
3194
3399
|
setError("");
|
|
@@ -3209,8 +3414,8 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3209
3414
|
setInvite(null);
|
|
3210
3415
|
setError(caught instanceof Error ? caught.message : "Couldn\u2019t unlock.");
|
|
3211
3416
|
}).finally(() => setBusy(false));
|
|
3212
|
-
}, children: [
|
|
3213
|
-
const menuCard =
|
|
3417
|
+
}, children: [_jsxs5("label", { className: "cs-field", children: [_jsx5("span", { children: "Your name" }), _jsx5("input", { "data-cs-ui": "unlock-name", value: name, onChange: (event) => setName(event.target.value), autoComplete: "name", required: true })] }), invite ? null : _jsxs5("label", { className: "cs-field", children: [_jsx5("span", { children: "Passphrase" }), _jsx5("input", { "data-cs-ui": "unlock-passphrase", type: "password", value: passphrase, onChange: (event) => setPassphrase(event.target.value), autoComplete: "current-password", required: true })] }), _jsxs5("label", { className: "cs-field", children: [_jsx5("span", { children: "Email" }), _jsx5("input", { "data-cs-ui": "unlock-email", type: "email", value: email, onChange: (event) => setEmail(event.target.value), autoComplete: "email", required: true }), _jsx5("small", { children: "So teammates can @mention you." })] }), error ? _jsx5("p", { className: "cs-error", "data-cs-ui": "error", children: error }) : null, _jsx5("button", { className: "cs-button is-primary is-wide", "data-cs-ui": "unlock-submit", type: "submit", disabled: busy, children: busy ? "Unlocking\u2026" : invite ? "Join" : "Unlock" })] })] });
|
|
3418
|
+
const menuCard = _jsx5("section", { className: "cs-card cs-menu cs-hit", "data-cs-ui": "menu", "aria-label": "Your comment settings", children: confirmReset ? _jsxs5("div", { className: "cs-confirm", "data-cs-ui": "reset-confirm", children: [_jsx5("h2", { children: "Reset on this device?" }), _jsx5("p", { className: "cs-sub", children: "You\u2019ll be signed out and your name and preferences are cleared. Comments aren\u2019t affected." }), _jsxs5("div", { className: "cs-row-end", children: [_jsx5("button", { className: "cs-button", type: "button", onClick: () => setConfirmReset(false), children: "Cancel" }), _jsx5("button", { className: "cs-button is-danger", "data-cs-ui": "reset-submit", type: "button", onClick: reset, children: "Reset" })] })] }) : _jsxs5(_Fragment3, { children: [_jsxs5("div", { className: "cs-me-row", children: [_jsx5(Avatar, { name: renaming ?? memberName, size: 36 }), renaming != null ? _jsxs5("form", { className: "cs-rename", onSubmit: (event) => {
|
|
3214
3419
|
event.preventDefault();
|
|
3215
3420
|
const next = renaming.trim();
|
|
3216
3421
|
if (!next || next === memberName || !member) {
|
|
@@ -3226,15 +3431,15 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3226
3431
|
setRenaming(null);
|
|
3227
3432
|
say("Name updated");
|
|
3228
3433
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t rename.")).finally(() => setBusy(false));
|
|
3229
|
-
}, children: [
|
|
3434
|
+
}, children: [_jsx5("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
3435
|
if (event.key === "Escape") {
|
|
3231
3436
|
event.preventDefault();
|
|
3232
3437
|
setRenaming(null);
|
|
3233
3438
|
}
|
|
3234
|
-
} }),
|
|
3439
|
+
} }), _jsx5("button", { className: "cs-icon-button", type: "submit", "aria-label": "Save name", disabled: busy, children: _jsx5(Check, {}) })] }) : _jsxs5("div", { className: "cs-me-name", children: [_jsxs5("span", { className: "cs-me-lines", children: [_jsx5("strong", { children: memberName }), _jsx5("small", { children: profile?.email ?? readJwt(member ?? "")?.email ?? "" })] }), _jsx5("button", { className: "cs-icon-button", "data-cs-ui": "rename", type: "button", "aria-label": "Change name", onClick: () => {
|
|
3235
3440
|
setError("");
|
|
3236
3441
|
setRenaming(memberName);
|
|
3237
|
-
}, children:
|
|
3442
|
+
}, children: _jsx5(Pencil, {}) })] })] }), error ? _jsx5("p", { className: "cs-error", children: error }) : null, _jsx5("div", { className: "cs-sep" }), _jsxs5("button", { className: "cs-item", type: "button", role: "switch", "aria-checked": !pinsHidden, onClick: togglePins, children: [_jsx5("span", { children: "Show pins" }), _jsx5("span", { className: pinsHidden ? "cs-switch" : "cs-switch is-on" })] }), _jsxs5("button", { className: "cs-item", "data-cs-ui": "mention-emails", type: "button", role: "switch", "aria-checked": profile?.mentionEmails ?? true, disabled: !profile, onClick: () => {
|
|
3238
3443
|
if (!member || !profile)
|
|
3239
3444
|
return;
|
|
3240
3445
|
updateProfile(session, member, { mentionEmails: !profile.mentionEmails }).then((updated) => {
|
|
@@ -3243,7 +3448,10 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3243
3448
|
setProfile(updated);
|
|
3244
3449
|
say(updated.mentionEmails ? "Mention emails on" : "Mention emails off");
|
|
3245
3450
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t update."));
|
|
3246
|
-
}, children: [
|
|
3451
|
+
}, children: [_jsx5("span", { children: "Email me when mentioned" }), _jsx5("span", { className: profile?.mentionEmails ?? true ? "cs-switch is-on" : "cs-switch" })] }), _jsxs5("div", { className: "cs-item is-static", children: [_jsx5("span", { children: "Position" }), _jsx5("span", { className: "cs-segment", role: "radiogroup", "aria-label": "Toolbar position", children: positions.map((option) => _jsx5("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)) })] }), _jsxs5("button", { className: "cs-item", "data-cs-ui": "settings-open", type: "button", onClick: () => {
|
|
3452
|
+
setError("");
|
|
3453
|
+
setSurface("settings");
|
|
3454
|
+
}, children: [_jsx5("span", { children: "Comment fields" }), _jsx5("span", { className: "cs-hint", children: detailsEnabled(settings) ? "On" : "Off" })] }), _jsx5("div", { className: "cs-sep" }), _jsx5("button", { className: "cs-item", "data-cs-ui": "invite", type: "button", disabled: busy, onClick: () => {
|
|
3247
3455
|
if (!member)
|
|
3248
3456
|
return;
|
|
3249
3457
|
setBusy(true);
|
|
@@ -3256,23 +3464,23 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3256
3464
|
setInviteLink(link);
|
|
3257
3465
|
return navigator.clipboard.writeText(link).then(() => say("Invite link copied")).catch(() => say("Invite link ready"));
|
|
3258
3466
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t create an invite.")).finally(() => setBusy(false));
|
|
3259
|
-
}, children:
|
|
3260
|
-
const inbox =
|
|
3467
|
+
}, children: _jsx5("span", { children: "Invite someone" }) }), inviteLink ? _jsxs5("div", { className: "cs-invite", children: [_jsx5("input", { "data-cs-ui": "invite-link", "aria-label": "Invite link", readOnly: true, value: inviteLink, onFocus: (event) => event.currentTarget.select() }), _jsx5("p", { className: "cs-hint", children: "Anyone with this link can join for 7 days. Changing the passphrase cancels it." })] }) : null, _jsx5("button", { className: "cs-item", "data-cs-ui": "lock", type: "button", onClick: lock, children: _jsx5("span", { children: "Lock" }) }), _jsx5("button", { className: "cs-item is-danger", "data-cs-ui": "reset", type: "button", onClick: () => setConfirmReset(true), children: _jsx5("span", { children: "Reset on this device\u2026" }) }), _jsx5("p", { className: "cs-hint", children: "C comment \xB7 I list \xB7 H pins" })] }) });
|
|
3468
|
+
const inbox = _jsxs5("section", { className: "cs-card cs-sheet cs-hit", "data-cs-ui": "panel", "aria-label": "Comments", children: [_jsxs5("header", { className: "cs-sheet-head", children: [_jsx5("h2", { children: "Comments" }), _jsx5("button", { className: "cs-icon-button", type: "button", "aria-label": "Close comments", onClick: () => setSurface("none"), children: _jsx5(Close, {}) })] }), _jsx5("div", { className: "cs-tabs", role: "tablist", children: [
|
|
3261
3469
|
["page", "This page", counts.page, "scope-page"],
|
|
3262
3470
|
["all", "All pages", counts.all, "scope-all"],
|
|
3263
3471
|
["resolved", "Resolved", counts.resolved, "scope-resolved"]
|
|
3264
|
-
].map(([value, label, count, hook]) =>
|
|
3472
|
+
].map(([value, label, count, hook]) => _jsxs5("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 ? _jsx5("span", { className: "cs-count", children: count }) : null] }, value)) }), error ? _jsx5("p", { className: "cs-error cs-pad", "data-cs-ui": "error", children: error }) : null, _jsxs5("div", { className: "cs-list", children: [listed.length === 0 ? _jsxs5("div", { className: "cs-empty", children: [_jsx5("p", { children: tab === "resolved" ? "Nothing resolved yet." : tab === "page" ? "No comments on this page." : "No open comments." }), tab !== "resolved" ? _jsxs5("button", { className: "cs-button", type: "button", onClick: startComment, children: [_jsx5(AddComment, {}), " Add a comment"] }) : null] }) : null, groups.map((group) => _jsxs5("div", { children: [group.route ? _jsx5("div", { className: "cs-group", children: group.route === route ? "This page" : group.route }) : null, group.items.map((item) => {
|
|
3265
3473
|
const { thread } = item;
|
|
3266
3474
|
const expanded = activeId === thread.id;
|
|
3267
3475
|
const first = thread.messages[0];
|
|
3268
3476
|
const note = statusNote(item.status, thread.anchor, thread.route === route);
|
|
3269
3477
|
const onPage = item.status === "pinned" || item.status === "drifted";
|
|
3270
|
-
return
|
|
3478
|
+
return _jsxs5("article", { className: expanded ? "cs-thread is-open" : "cs-thread", "data-cs-ui": "thread", "data-thread-id": thread.id, "data-cs-status": item.status, children: [_jsxs5("div", { className: "cs-thread-main", role: "button", tabIndex: 0, "aria-expanded": expanded, "aria-label": `${thread.createdBy}: ${first?.body ?? ""}`, onClick: () => select(item), onKeyDown: (event) => {
|
|
3271
3479
|
if (event.key === "Enter" || event.key === " ") {
|
|
3272
3480
|
event.preventDefault();
|
|
3273
|
-
|
|
3481
|
+
select(item);
|
|
3274
3482
|
}
|
|
3275
|
-
}, onMouseEnter: () => setPeekId(null), children: [
|
|
3483
|
+
}, onMouseEnter: () => setPeekId(null), children: [_jsx5(Avatar, { name: thread.createdBy }), _jsxs5("div", { className: "cs-thread-body", children: [_jsxs5("div", { className: "cs-meta", children: [_jsx5("strong", { children: thread.createdBy }), _jsx5("time", { dateTime: thread.createdAt, children: ago(thread.createdAt) }), _jsx5(RoleBadge, { role: thread.role })] }), first?.body ? _jsx5("p", { className: "cs-text", children: _jsx5(MessageText, { body: first.body, mentions: first.mentions }) }) : null, _jsx5(FieldValues, { fields: thread.fields, clamp: !expanded }), _jsx5(MessageAttachments, { attachments: first?.attachments }), first && deliveryText(deliveries[first.id]) ? _jsx5("div", { className: "cs-delivery", "data-cs-ui": "delivery", children: deliveryText(deliveries[first.id]) }) : null, _jsxs5("div", { className: "cs-target", children: [_jsx5("span", { title: thread.anchor.component?.file ?? void 0, children: targetName(thread.anchor) }), note ? _jsx5("span", { className: `cs-note is-${note.tone}`, children: note.text }) : null] }), !expanded && thread.messages.length > 1 ? _jsxs5("div", { className: "cs-replies", children: [thread.messages.length - 1, " ", thread.messages.length === 2 ? "reply" : "replies"] }) : null] })] }), expanded ? _jsxs5("div", { className: "cs-thread-more", children: [editingId === thread.id && settings ? _jsx5(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) => _jsxs5("div", { className: "cs-message", children: [_jsx5(Avatar, { name: message.author, size: 20 }), _jsxs5("div", { children: [_jsxs5("div", { className: "cs-meta", children: [_jsx5("strong", { children: message.author }), _jsx5("time", { dateTime: message.createdAt, children: ago(message.createdAt) })] }), _jsx5("p", { className: "cs-text", children: _jsx5(MessageText, { body: message.body, mentions: message.mentions }) }), _jsx5(MessageAttachments, { attachments: message.attachments }), deliveryText(deliveries[message.id]) ? _jsx5("div", { className: "cs-delivery", "data-cs-ui": "delivery", children: deliveryText(deliveries[message.id]) }) : null] })] }, message.id)), _jsxs5("form", { className: "cs-reply", onSubmit: async (event) => {
|
|
3276
3484
|
event.preventDefault();
|
|
3277
3485
|
if (!member || !reply.trim() && !replyFiles.ids.length || replyFiles.uploading)
|
|
3278
3486
|
return;
|
|
@@ -3298,7 +3506,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3298
3506
|
}
|
|
3299
3507
|
await refresh();
|
|
3300
3508
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t reply.")).finally(() => setBusy(false));
|
|
3301
|
-
}, children: [
|
|
3509
|
+
}, children: [_jsx5(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
3510
|
replyPicked.current.set(person.id, person);
|
|
3303
3511
|
const element = thread.route === route ? resolveInDocument(thread.anchor, route).element : null;
|
|
3304
3512
|
if (element && !replyShot.current)
|
|
@@ -3309,25 +3517,25 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3309
3517
|
event.preventDefault();
|
|
3310
3518
|
replyFiles.add(files);
|
|
3311
3519
|
}
|
|
3312
|
-
} }),
|
|
3520
|
+
} }), _jsx5(AttachButtons, { onFiles: replyFiles.add, capturing, onSnapshot: () => snapshot(thread.route === route ? resolveInDocument(thread.anchor, route).element : null, replyFiles.add) }), _jsx5("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: _jsx5(Send, {}) })] }), _jsx5(AttachmentTray, { items: replyFiles.items, onRemove: replyFiles.remove }), _jsxs5("div", { className: "cs-actions", children: [_jsxs5("button", { className: "cs-button", "data-cs-ui": "resolve", type: "button", onClick: () => {
|
|
3313
3521
|
if (!member)
|
|
3314
3522
|
return;
|
|
3315
3523
|
const next = thread.status === "resolved" ? "open" : "resolved";
|
|
3316
3524
|
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" ?
|
|
3525
|
+
}, children: [thread.status === "resolved" ? _jsx5(Undo, {}) : _jsx5(Check, {}), thread.status === "resolved" ? "Reopen" : "Resolve"] }), onPage && thread.status === "open" ? _jsxs5("button", { className: "cs-button", type: "button", onClick: () => showOnPage(thread), children: [_jsx5(Target, {}), " Show"] }) : null, detailsEnabled(settings) && editingId !== thread.id ? _jsxs5("button", { className: "cs-button", "data-cs-ui": "details-edit", type: "button", onClick: () => setEditingId(thread.id), children: [_jsx5(Pencil, {}), " Details"] }) : null, !onPage && thread.status === "open" && thread.route === route ? _jsxs5("button", { className: "cs-button", "data-cs-ui": "reattach", type: "button", onClick: () => {
|
|
3318
3526
|
setReattachId(thread.id);
|
|
3319
3527
|
setMode("reattach");
|
|
3320
3528
|
setSurface("none");
|
|
3321
|
-
}, children: [
|
|
3529
|
+
}, children: [_jsx5(Relink, {}), " Reattach"] }) : null] })] }) : null] }, thread.id);
|
|
3322
3530
|
})] }, group.route ?? "all"))] })] });
|
|
3323
|
-
const ui =
|
|
3531
|
+
const ui = _jsxs5("div", { id: "cs-root", className: [
|
|
3324
3532
|
inside ? "cs-inside" : "cs-fixed",
|
|
3325
3533
|
`cs-at-${barPosition.replace("bottom-", "")}`,
|
|
3326
3534
|
theme === "dark" || theme === "system" && systemDark ? "cs-dark" : ""
|
|
3327
|
-
].join(" "), children: [highlight ?
|
|
3535
|
+
].join(" "), children: [highlight ? _jsxs5(_Fragment3, { children: [_jsx5("div", { className: "cs-outline", style: { top: highlight.top, left: highlight.left, width: highlight.width, height: highlight.height } }), _jsx5("div", { className: "cs-outline-label", style: { top: Math.max(4, highlight.top - 26), left: highlight.left }, children: highlight.label })] }) : null, flash ? _jsx5("div", { className: "cs-outline is-flash", style: { top: flash.top, left: flash.left, width: flash.width, height: flash.height } }) : null, mode !== "browse" ? _jsxs5("div", { className: "cs-banner cs-hit", "data-cs-ui": "mode", children: [_jsx5(Cursor, {}), _jsx5("span", { children: mode === "reattach" ? "Click the element this comment belongs to" : "Click anything to comment" }), _jsx5("button", { type: "button", onClick: () => {
|
|
3328
3536
|
setMode("browse");
|
|
3329
3537
|
setReattachId(null);
|
|
3330
|
-
}, children: "Cancel" })] }) : null, pinsHidden ? null : placed.map((item) => item.top == null || item.left == null || item.status !== "pinned" && item.status !== "drifted" ? null :
|
|
3538
|
+
}, children: "Cancel" })] }) : null, pinsHidden ? null : placed.map((item) => item.top == null || item.left == null || item.status !== "pinned" && item.status !== "drifted" ? null : _jsxs5("button", { type: "button", className: [
|
|
3331
3539
|
"cs-pin",
|
|
3332
3540
|
item.status === "drifted" ? "is-drifted" : "",
|
|
3333
3541
|
activeId === item.thread.id && surface === "inbox" ? "is-active" : ""
|
|
@@ -3336,9 +3544,9 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3336
3544
|
setTab("page");
|
|
3337
3545
|
setActiveId(item.thread.id);
|
|
3338
3546
|
setSurface("inbox");
|
|
3339
|
-
}, children: [
|
|
3547
|
+
}, children: [_jsx5(Avatar, { name: item.thread.createdBy, size: 22 }), item.thread.messages.length > 1 ? _jsx5("span", { className: "cs-pin-count", children: item.thread.messages.length }) : null] }, item.thread.id)), peek && !pinsHidden && peek.top != null && peek.left != null ? _jsxs5("div", { className: "cs-peek", style: { top: peek.top + 18, left: Math.max(8, peek.left - 12) }, children: [_jsxs5("div", { className: "cs-meta", children: [_jsx5(Avatar, { name: peek.thread.createdBy, size: 18 }), _jsx5("strong", { children: peek.thread.createdBy }), _jsx5("time", { children: ago(peek.thread.createdAt) })] }), _jsx5("p", { className: "cs-text", children: _jsx5(MessageText, { body: peek.thread.messages[0]?.body ?? "", mentions: peek.thread.messages[0]?.mentions }) }), peek.thread.messages.length > 1 ? _jsxs5("div", { className: "cs-replies", children: [peek.thread.messages.length - 1, " ", peek.thread.messages.length === 2 ? "reply" : "replies"] }) : null] }) : null, draft ? _jsxs5("form", { className: "cs-card cs-composer cs-hit", "data-cs-ui": "composer", style: { top: draft.top, left: draft.left }, onSubmit: async (event) => {
|
|
3340
3548
|
event.preventDefault();
|
|
3341
|
-
if (!member || !
|
|
3549
|
+
if (!member || !canPost || draftFiles.uploading)
|
|
3342
3550
|
return;
|
|
3343
3551
|
setBusy(true);
|
|
3344
3552
|
const picked = mentionsIn(body, draftPicked.current);
|
|
@@ -3346,10 +3554,13 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3346
3554
|
createThread(session, member, draft.anchor, body, {
|
|
3347
3555
|
mentions: picked.map((person) => person.id),
|
|
3348
3556
|
screenshot,
|
|
3349
|
-
attachments: draftFiles.ids
|
|
3557
|
+
attachments: draftFiles.ids,
|
|
3558
|
+
url: pageHref(),
|
|
3559
|
+
...detailsPayload(settings, draftDetails)
|
|
3350
3560
|
}).then(async ({ thread, delivery }) => {
|
|
3351
3561
|
setDraft(null);
|
|
3352
3562
|
setBody("");
|
|
3563
|
+
setDraftDetails(noDetails);
|
|
3353
3564
|
draftFiles.reset();
|
|
3354
3565
|
setError("");
|
|
3355
3566
|
draftPicked.current = /* @__PURE__ */ new Map();
|
|
@@ -3367,7 +3578,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3367
3578
|
setSurface("inbox");
|
|
3368
3579
|
await refresh();
|
|
3369
3580
|
}).catch((caught) => setError(caught instanceof Error ? caught.message : "Couldn\u2019t post.")).finally(() => setBusy(false));
|
|
3370
|
-
}, children: [
|
|
3581
|
+
}, children: [_jsxs5("div", { className: "cs-composer-target", children: [_jsx5(Target, {}), _jsx5("span", { className: "cs-composer-name", children: targetName(draft.anchor) }), draft.anchor.component?.file ? _jsx5("code", { children: draft.anchor.component.file }) : null, _jsx5("button", { className: "cs-icon-button", type: "button", "aria-label": "Discard", onClick: () => setDraft(null), children: _jsx5(Close, {}) })] }), _jsxs5("div", { className: "cs-composer-body", children: [_jsx5(Avatar, { name: memberName, size: 24 }), _jsx5(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
3582
|
draftPicked.current.set(person.id, person);
|
|
3372
3583
|
if (!draftShot.current)
|
|
3373
3584
|
draftShot.current = captureAround(draft.element);
|
|
@@ -3377,7 +3588,7 @@ function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs =
|
|
|
3377
3588
|
event.preventDefault();
|
|
3378
3589
|
draftFiles.add(files);
|
|
3379
3590
|
}
|
|
3380
|
-
} })] }),
|
|
3591
|
+
} })] }), _jsx5(DetailsInput, { settings, value: draftDetails, onChange: setDraftDetails, onKeyDown: submitOnCmdEnter }), _jsx5(AttachmentTray, { items: draftFiles.items, onRemove: draftFiles.remove }), error ? _jsx5("p", { className: "cs-error cs-pad", children: error }) : null, _jsxs5("div", { className: "cs-composer-foot", children: [_jsx5(AttachButtons, { onFiles: draftFiles.add, capturing, onSnapshot: () => snapshot(draft.element, draftFiles.add) }), _jsx5("span", { className: "cs-hint", children: capturing ? "Capturing\u2026" : draftFiles.uploading ? "Uploading\u2026" : busy ? "Posting\u2026" : "\u2318\u21B5 to post" }), _jsx5("button", { className: "cs-send", "data-cs-ui": "composer-submit", type: "submit", "aria-label": "Post comment", disabled: busy || draftFiles.uploading || !canPost, children: _jsx5(Send, {}) })] })] }) : null, surface === "unlock" && !member ? unlockCard : null, surface === "menu" && member ? menuCard : null, surface === "settings" && member ? _jsx5(SettingsPanel, { settings, onChange: changeSettings, onBack: () => setSurface("menu"), error }) : null, surface === "inbox" && member ? inbox : null, toast ? _jsx5("div", { className: "cs-toast", role: "status", children: toast }) : null, bar] });
|
|
3381
3592
|
return createPortal2(ui, host);
|
|
3382
3593
|
}
|
|
3383
3594
|
export {
|
|
@@ -47,9 +47,35 @@ export type PostExtras = {
|
|
|
47
47
|
screenshot?: string | null;
|
|
48
48
|
attachments?: string[];
|
|
49
49
|
};
|
|
50
|
+
export type FieldDef = {
|
|
51
|
+
id: string;
|
|
52
|
+
label: string;
|
|
53
|
+
};
|
|
54
|
+
export type FieldValue = FieldDef & {
|
|
55
|
+
value: string;
|
|
56
|
+
};
|
|
57
|
+
export type Settings = {
|
|
58
|
+
roles: {
|
|
59
|
+
enabled: boolean;
|
|
60
|
+
options: string[];
|
|
61
|
+
};
|
|
62
|
+
fields: {
|
|
63
|
+
enabled: boolean;
|
|
64
|
+
items: FieldDef[];
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
/** Role and custom field values for a new or edited comment, keyed by field id. */
|
|
68
|
+
export type Details = {
|
|
69
|
+
role: string | null;
|
|
70
|
+
fields: Record<string, string>;
|
|
71
|
+
};
|
|
50
72
|
export type Thread = {
|
|
51
73
|
id: string;
|
|
52
74
|
route: string;
|
|
75
|
+
/** The page the comment was left on, including query and hash. */
|
|
76
|
+
url: string;
|
|
77
|
+
role: string | null;
|
|
78
|
+
fields: FieldValue[];
|
|
53
79
|
anchor: Anchor;
|
|
54
80
|
status: "open" | "resolved";
|
|
55
81
|
createdBy: string;
|
|
@@ -97,8 +123,14 @@ export declare function updateProfile(session: Session, memberToken: string, pat
|
|
|
97
123
|
}>;
|
|
98
124
|
export declare function uploadAttachment(session: Session, memberToken: string, file: Blob, name: string): Promise<Attachment>;
|
|
99
125
|
export declare function listMembers(session: Session, memberToken: string): Promise<Member[]>;
|
|
100
|
-
export declare function listThreads(session: Session, memberToken: string): Promise<
|
|
101
|
-
|
|
126
|
+
export declare function listThreads(session: Session, memberToken: string): Promise<{
|
|
127
|
+
threads: Thread[];
|
|
128
|
+
settings: Settings;
|
|
129
|
+
}>;
|
|
130
|
+
export declare function saveSettings(session: Session, memberToken: string, settings: Settings): Promise<Settings>;
|
|
131
|
+
export declare function createThread(session: Session, memberToken: string, anchor: Anchor, body: string, extras?: PostExtras & Partial<Details> & {
|
|
132
|
+
url?: string;
|
|
133
|
+
}): Promise<{
|
|
102
134
|
thread: Thread;
|
|
103
135
|
delivery?: Delivery;
|
|
104
136
|
}>;
|
|
@@ -109,6 +141,7 @@ export declare function replyToThread(session: Session, memberToken: string, thr
|
|
|
109
141
|
export declare function updateThread(session: Session, memberToken: string, threadId: string, patch: {
|
|
110
142
|
status?: "open" | "resolved";
|
|
111
143
|
anchor?: Anchor;
|
|
112
|
-
|
|
144
|
+
url?: string;
|
|
145
|
+
} & Partial<Details>): Promise<{
|
|
113
146
|
id: string;
|
|
114
147
|
}>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type KeyboardEvent as ReactKeyboardEvent } from "react";
|
|
2
|
+
import type { Details, FieldValue, Settings, Thread } from "./client.js";
|
|
3
|
+
export declare const noDetails: Details;
|
|
4
|
+
export declare function detailsEnabled(settings: Settings | null): boolean;
|
|
5
|
+
export declare function hasFieldValues(details: Details): boolean;
|
|
6
|
+
/** Only sends what is turned on, so a disabled feature never rejects a post. */
|
|
7
|
+
export declare function detailsPayload(settings: Settings | null, details: Details): Partial<Details>;
|
|
8
|
+
export declare function DetailsInput({ settings, value, onChange, onKeyDown, }: {
|
|
9
|
+
settings: Settings | null;
|
|
10
|
+
value: Details;
|
|
11
|
+
onChange: (next: Details) => void;
|
|
12
|
+
onKeyDown?: (event: ReactKeyboardEvent<HTMLTextAreaElement>) => void;
|
|
13
|
+
}): import("react").JSX.Element | null;
|
|
14
|
+
export declare function RoleBadge({ role }: {
|
|
15
|
+
role: string | null;
|
|
16
|
+
}): import("react").JSX.Element | null;
|
|
17
|
+
export declare function FieldValues({ fields, clamp }: {
|
|
18
|
+
fields: FieldValue[];
|
|
19
|
+
clamp?: boolean;
|
|
20
|
+
}): import("react").JSX.Element | null;
|
|
21
|
+
/** Inline editor for a posted comment's role and fields. Values for fields that were since removed are kept by the server. */
|
|
22
|
+
export declare function DetailsEditor({ settings, thread, onSave, onClose, }: {
|
|
23
|
+
settings: Settings;
|
|
24
|
+
thread: Thread;
|
|
25
|
+
onSave: (patch: Partial<Details>) => Promise<void>;
|
|
26
|
+
onClose: () => void;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
/** Shared per environment: anyone signed in can change it, and existing comments keep their values. */
|
|
29
|
+
export declare function SettingsPanel({ settings, onChange, onBack, error, }: {
|
|
30
|
+
settings: Settings | null;
|
|
31
|
+
onChange: (next: Settings) => void;
|
|
32
|
+
onBack: () => void;
|
|
33
|
+
error: string;
|
|
34
|
+
}): import("react").JSX.Element;
|
|
@@ -16,3 +16,4 @@ export declare const Cursor: () => import("react").JSX.Element;
|
|
|
16
16
|
export declare const Paperclip: () => import("react").JSX.Element;
|
|
17
17
|
export declare const Camera: () => import("react").JSX.Element;
|
|
18
18
|
export declare const FileIcon: () => import("react").JSX.Element;
|
|
19
|
+
export declare const Back: () => import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const toolbarCss = "\n#cs-root {\n --cs-text: #1d1d1f;\n --cs-text-2: #6e6e73;\n --cs-text-3: #a1a1a6;\n --cs-glass: rgba(251, 251, 253, 0.8);\n --cs-card: rgba(255, 255, 255, 0.94);\n --cs-fill: rgba(0, 0, 0, 0.05);\n --cs-fill-2: rgba(0, 0, 0, 0.08);\n --cs-line: rgba(0, 0, 0, 0.08);\n --cs-edge: rgba(0, 0, 0, 0.1);\n --cs-accent: #007aff;\n --cs-accent-soft: rgba(0, 122, 255, 0.12);\n --cs-danger: #ff3b30;\n --cs-warn: #ff9500;\n --cs-shadow: 0 0 0 0.5px var(--cs-edge), 0 10px 30px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n pointer-events: none;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--cs-text);\n font: 13px/1.4 -apple-system, BlinkMacSystemFont, \"SF Pro Text\", \"Helvetica Neue\", Inter, system-ui, sans-serif;\n letter-spacing: -0.005em;\n -webkit-font-smoothing: antialiased;\n text-align: left;\n}\n#cs-root.cs-dark {\n --cs-text: #f5f5f7;\n --cs-text-2: #a1a1a6;\n --cs-text-3: #6e6e73;\n --cs-glass: rgba(40, 40, 44, 0.78);\n --cs-card: rgba(36, 36, 40, 0.96);\n --cs-fill: rgba(255, 255, 255, 0.07);\n --cs-fill-2: rgba(255, 255, 255, 0.12);\n --cs-line: rgba(255, 255, 255, 0.08);\n --cs-edge: rgba(255, 255, 255, 0.12);\n --cs-accent: #0a84ff;\n --cs-accent-soft: rgba(10, 132, 255, 0.2);\n --cs-shadow: 0 0 0 0.5px var(--cs-edge), 0 10px 30px rgba(0, 0, 0, 0.45);\n}\n#cs-root.cs-inside { position: absolute; }\n#cs-root, #cs-root * { box-sizing: border-box; }\n#cs-root h2, #cs-root p { margin: 0; }\n#cs-root button, #cs-root input, #cs-root textarea {\n font: inherit; color: inherit; letter-spacing: inherit; margin: 0;\n}\n#cs-root button { background: none; border: 0; padding: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; }\n#cs-root button:disabled { cursor: default; opacity: 0.45; }\n#cs-root button:focus-visible, #cs-root [role=button]:focus-visible { outline: 2px solid var(--cs-accent); outline-offset: 2px; }\n#cs-root .cs-hit { pointer-events: auto; }\n#cs-root .cs-icon { display: block; flex: none; }\n\n/* Bar */\n#cs-root .cs-bar {\n position: absolute;\n bottom: 16px;\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 4px;\n border-radius: 999px;\n background: var(--cs-glass);\n -webkit-backdrop-filter: saturate(180%) blur(20px);\n backdrop-filter: saturate(180%) blur(20px);\n box-shadow: var(--cs-shadow);\n}\n#cs-root.cs-at-center .cs-bar { left: 50%; transform: translateX(-50%); }\n#cs-root.cs-at-left .cs-bar { left: 16px; }\n#cs-root.cs-at-right .cs-bar { right: 16px; }\n#cs-root .cs-tool {\n position: relative;\n width: 34px;\n height: 34px;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--cs-text);\n transition: background-color 0.15s ease, color 0.15s ease;\n}\n#cs-root .cs-tool:hover { background: var(--cs-fill); }\n#cs-root .cs-tool.is-on { background: var(--cs-fill-2); }\n#cs-root .cs-tool.is-accent { background: var(--cs-accent); color: #fff; }\n#cs-root .cs-tool[data-tip]::after {\n content: attr(data-tip);\n position: absolute;\n bottom: calc(100% + 10px);\n left: 50%;\n transform: translate(-50%, 2px);\n white-space: pre;\n padding: 4px 8px;\n border-radius: 7px;\n background: rgba(29, 29, 31, 0.92);\n color: #fff;\n font-size: 11.5px;\n font-weight: 500;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.12s ease, transform 0.12s ease;\n}\n#cs-root .cs-tool[data-tip]:hover::after { opacity: 1; transform: translate(-50%, 0); transition-delay: 0.35s; }\n#cs-root .cs-me { width: 34px; }\n#cs-root .cs-divider { width: 1px; height: 18px; background: var(--cs-line); margin: 0 3px; }\n#cs-root .cs-badge {\n position: absolute;\n top: 3px;\n right: 2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n border-radius: 999px;\n background: var(--cs-accent);\n color: #fff;\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n box-shadow: 0 0 0 2px var(--cs-glass);\n}\n#cs-root .cs-bar-lock {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n height: 34px;\n padding: 0 14px 0 12px;\n border-radius: 999px;\n font-weight: 500;\n}\n#cs-root .cs-bar-lock:hover { background: var(--cs-fill); }\n#cs-root .cs-avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n border-radius: 999px;\n color: #fff;\n font-weight: 600;\n letter-spacing: 0;\n line-height: 1;\n user-select: none;\n}\n\n/* Cards */\n#cs-root .cs-card {\n position: absolute;\n background: var(--cs-card);\n -webkit-backdrop-filter: saturate(180%) blur(24px);\n backdrop-filter: saturate(180%) blur(24px);\n border-radius: 14px;\n box-shadow: var(--cs-shadow);\n}\n#cs-root .cs-unlock, #cs-root .cs-menu { bottom: 64px; }\n#cs-root.cs-at-center .cs-unlock, #cs-root.cs-at-center .cs-menu { left: 50%; transform: translateX(-50%); }\n#cs-root.cs-at-left .cs-unlock, #cs-root.cs-at-left .cs-menu { left: 16px; }\n#cs-root.cs-at-right .cs-unlock, #cs-root.cs-at-right .cs-menu { right: 16px; }\n#cs-root .cs-unlock { width: 280px; padding: 20px; text-align: center; }\n#cs-root .cs-unlock-icon {\n width: 40px; height: 40px; margin: 0 auto 10px; border-radius: 12px;\n display: flex; align-items: center; justify-content: center; background: var(--cs-fill); color: var(--cs-text-2);\n}\n#cs-root .cs-unlock h2 { font-size: 15px; font-weight: 600; }\n#cs-root .cs-sub { color: var(--cs-text-2); margin-top: 4px; }\n#cs-root .cs-unlock form { margin-top: 14px; display: grid; gap: 8px; text-align: left; }\n#cs-root .cs-field { display: grid; gap: 4px; }\n#cs-root .cs-field span { font-size: 12px; color: var(--cs-text-2); padding-left: 2px; }\n#cs-root input, #cs-root textarea {\n width: 100%;\n border: 0;\n border-radius: 9px;\n background: var(--cs-fill);\n padding: 8px 10px;\n outline: none;\n transition: box-shadow 0.15s ease, background-color 0.15s ease;\n}\n#cs-root input:focus, #cs-root textarea:focus { background: transparent; box-shadow: 0 0 0 1px var(--cs-accent), 0 0 0 4px var(--cs-accent-soft); }\n#cs-root textarea { resize: none; }\n#cs-root .cs-error { color: var(--cs-danger); font-size: 12px; }\n#cs-root .cs-pad { padding: 6px 14px 0; }\n#cs-root .cs-hint { color: var(--cs-text-3); font-size: 11.5px; }\n\n/* Buttons */\n#cs-root .cs-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n height: 28px;\n padding: 0 11px;\n border-radius: 8px;\n background: var(--cs-fill);\n font-size: 12.5px;\n font-weight: 500;\n transition: background-color 0.15s ease;\n}\n#cs-root .cs-button:hover { background: var(--cs-fill-2); }\n#cs-root .cs-button.is-primary { background: var(--cs-accent); color: #fff; }\n#cs-root .cs-button.is-primary:hover { filter: brightness(1.06); }\n#cs-root .cs-button.is-danger { background: var(--cs-danger); color: #fff; }\n#cs-root .cs-button.is-wide { width: 100%; height: 32px; margin-top: 4px; }\n#cs-root .cs-icon-button {\n width: 26px; height: 26px; border-radius: 999px; flex: none;\n display: inline-flex; align-items: center; justify-content: center; color: var(--cs-text-2);\n}\n#cs-root .cs-icon-button:hover { background: var(--cs-fill); color: var(--cs-text); }\n#cs-root .cs-send {\n width: 26px; height: 26px; border-radius: 999px; flex: none;\n display: inline-flex; align-items: center; justify-content: center; background: var(--cs-accent); color: #fff;\n}\n#cs-root .cs-send:disabled { background: var(--cs-fill-2); color: var(--cs-text-3); opacity: 1; }\n\n/* Menu */\n#cs-root .cs-menu { width: 264px; padding: 6px; }\n#cs-root .cs-me-row { display: flex; align-items: center; gap: 10px; padding: 8px 8px 10px; }\n#cs-root .cs-me-name { display: flex; align-items: center; gap: 4px; min-width: 0; }\n#cs-root .cs-me-name strong { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-rename { display: flex; align-items: center; gap: 4px; flex: 1; }\n#cs-root .cs-rename input { padding: 5px 8px; }\n#cs-root .cs-sep { height: 1px; background: var(--cs-line); margin: 4px 8px; }\n#cs-root .cs-item {\n display: flex; align-items: center; justify-content: space-between; gap: 10px;\n width: 100%; min-height: 32px; padding: 0 8px; border-radius: 8px; text-align: left;\n}\n#cs-root button.cs-item:hover { background: var(--cs-fill); }\n#cs-root .cs-item.is-danger { color: var(--cs-danger); }\n#cs-root .cs-switch { position: relative; width: 32px; height: 20px; border-radius: 999px; background: var(--cs-fill-2); transition: background-color 0.2s ease; flex: none; }\n#cs-root .cs-switch::after {\n content: \"\"; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 999px;\n background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); transition: transform 0.2s ease;\n}\n#cs-root .cs-switch.is-on { background: #34c759; }\n#cs-root .cs-switch.is-on::after { transform: translateX(12px); }\n#cs-root .cs-segment { display: inline-flex; padding: 2px; border-radius: 8px; background: var(--cs-fill); }\n#cs-root .cs-segment button { padding: 2px 8px; border-radius: 6px; font-size: 12px; color: var(--cs-text-2); }\n#cs-root .cs-segment button.is-on { background: var(--cs-card); color: var(--cs-text); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12); font-weight: 500; }\n#cs-root .cs-menu .cs-hint { padding: 6px 8px 4px; }\n#cs-root .cs-invite { padding: 2px 8px 6px; }\n#cs-root .cs-invite input { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; padding: 6px 8px; }\n#cs-root .cs-invite .cs-hint { padding: 4px 0 0; }\n#cs-root .cs-confirm { padding: 10px; }\n#cs-root .cs-confirm h2 { font-size: 14px; font-weight: 600; }\n#cs-root .cs-row-end { display: flex; justify-content: flex-end; gap: 6px; margin-top: 12px; }\n\n/* Sheet */\n#cs-root .cs-sheet {\n top: 12px; right: 12px; max-height: calc(100% - 88px); width: min(360px, calc(100vw - 24px));\n display: flex; flex-direction: column; overflow: hidden;\n}\n#cs-root .cs-sheet-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 10px 4px 16px; }\n#cs-root .cs-sheet-head h2 { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }\n#cs-root .cs-tabs { display: flex; gap: 2px; margin: 4px 12px 6px; padding: 2px; border-radius: 9px; background: var(--cs-fill); }\n#cs-root .cs-tabs button {\n flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px;\n height: 26px; border-radius: 7px; font-size: 12px; font-weight: 500; color: var(--cs-text-2);\n}\n#cs-root .cs-tabs button.is-on { background: var(--cs-card); color: var(--cs-text); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12); }\n#cs-root .cs-count { font-size: 11px; color: var(--cs-text-3); font-variant-numeric: tabular-nums; }\n#cs-root .cs-list { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 2px 6px 8px; overscroll-behavior: contain; }\n#cs-root .cs-group { padding: 10px 10px 4px; font-size: 11px; font-weight: 600; color: var(--cs-text-3); text-transform: uppercase; letter-spacing: 0.04em; }\n#cs-root .cs-empty { padding: 36px 16px; text-align: center; color: var(--cs-text-2); display: grid; gap: 12px; justify-items: center; }\n#cs-root .cs-thread { border-radius: 10px; }\n#cs-root .cs-thread + .cs-thread { margin-top: 1px; }\n#cs-root .cs-thread.is-open { background: var(--cs-fill); }\n#cs-root .cs-thread-main { display: flex; gap: 10px; padding: 10px; border-radius: 10px; cursor: pointer; }\n#cs-root .cs-thread:not(.is-open) .cs-thread-main:hover { background: var(--cs-fill); }\n#cs-root .cs-thread-body { min-width: 0; flex: 1; }\n#cs-root .cs-meta { display: flex; align-items: center; gap: 6px; font-size: 12.5px; }\n#cs-root .cs-meta strong { font-weight: 600; }\n#cs-root .cs-meta time { color: var(--cs-text-3); font-size: 12px; }\n#cs-root .cs-text { margin-top: 2px; white-space: pre-wrap; overflow-wrap: anywhere; }\n#cs-root .cs-target { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin-top: 4px; font-size: 11.5px; color: var(--cs-text-3); }\n#cs-root .cs-note { display: inline-flex; align-items: center; gap: 5px; font-weight: 500; }\n#cs-root .cs-note::before { content: \"\"; width: 6px; height: 6px; border-radius: 999px; background: currentColor; }\n#cs-root .cs-note.is-quiet { color: var(--cs-text-2); }\n#cs-root .cs-note.is-warn { color: var(--cs-warn); }\n#cs-root .cs-note.is-bad { color: var(--cs-danger); }\n#cs-root .cs-replies { margin-top: 4px; font-size: 12px; color: var(--cs-accent); font-weight: 500; }\n#cs-root .cs-thread-more { padding: 0 10px 10px 44px; display: grid; gap: 10px; }\n#cs-root .cs-message { display: flex; gap: 8px; }\n#cs-root .cs-reply { display: flex; align-items: flex-end; gap: 6px; padding: 4px 4px 4px 0; border-radius: 10px; }\n#cs-root .cs-reply textarea { min-height: 32px; max-height: 120px; field-sizing: content; }\n#cs-root .cs-thread.is-open .cs-reply textarea { background: var(--cs-card); }\n#cs-root .cs-actions { display: flex; flex-wrap: wrap; gap: 6px; }\n#cs-root .cs-thread.is-open .cs-button { background: var(--cs-card); box-shadow: 0 0 0 0.5px var(--cs-edge); }\n#cs-root .cs-thread.is-open .cs-button:hover { background: var(--cs-fill-2); }\n\n/* Mentions */\n#cs-root .cs-mention { color: var(--cs-accent); background: var(--cs-accent-soft); border-radius: 4px; padding: 0 3px; font-weight: 500; }\n#cs-root .cs-delivery { margin-top: 4px; font-size: 11.5px; color: var(--cs-text-3); display: flex; align-items: center; gap: 5px; }\n#cs-root .cs-delivery::before { content: \"\"; width: 6px; height: 6px; border-radius: 999px; background: #34c759; }\n#cs-root .cs-people {\n position: fixed; z-index: 1; width: 264px; padding: 5px; border-radius: 12px;\n background: var(--cs-card); box-shadow: var(--cs-shadow); pointer-events: auto;\n}\n#cs-root .cs-person { display: flex; align-items: center; gap: 9px; padding: 6px 8px; border-radius: 8px; cursor: pointer; }\n#cs-root .cs-person.is-on { background: var(--cs-accent); color: #fff; }\n#cs-root .cs-person-text { display: grid; line-height: 1.25; min-width: 0; }\n#cs-root .cs-person-text strong { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-person-text small { font-size: 11.5px; color: var(--cs-text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-person.is-on small { color: rgba(255, 255, 255, 0.8); }\n#cs-root .cs-field small { font-size: 11.5px; color: var(--cs-text-3); padding-left: 2px; }\n#cs-root .cs-me-lines { display: grid; line-height: 1.25; min-width: 0; }\n#cs-root .cs-me-lines small { font-size: 11.5px; color: var(--cs-text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n\n/* Composer */\n#cs-root .cs-composer { width: min(320px, calc(100% - 16px)); }\n#cs-root .cs-composer-target {\n display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 12px;\n border-bottom: 1px solid var(--cs-line); font-size: 12px; color: var(--cs-text-2);\n}\n#cs-root .cs-composer-name { font-weight: 500; color: var(--cs-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n#cs-root .cs-composer-target code {\n margin-left: auto; font: 11px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--cs-text-3);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; min-width: 0; max-width: 45%;\n}\n#cs-root .cs-composer-body { display: flex; gap: 10px; padding: 12px 12px 4px; }\n#cs-root .cs-composer-body textarea { background: transparent; padding: 3px 0; min-height: 56px; box-shadow: none; border-radius: 0; }\n#cs-root .cs-composer-foot { display: flex; align-items: center; gap: 6px; padding: 6px 8px 8px 40px; }\n#cs-root .cs-composer-foot .cs-hint { margin-left: auto; }\n\n/* Attachments */\n#cs-root .cs-attach { display: inline-flex; gap: 0; flex: none; }\n#cs-root .cs-tray { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 12px 2px 46px; }\n#cs-root .cs-thread-more .cs-tray { padding: 0; }\n#cs-root .cs-pending {\n position: relative; display: inline-flex; align-items: center; gap: 6px; max-width: 170px; height: 34px;\n padding: 0 24px 0 4px; border-radius: 8px; background: var(--cs-fill); font-size: 11.5px; color: var(--cs-text-2);\n}\n#cs-root .cs-pending img { width: 26px; height: 26px; border-radius: 5px; object-fit: cover; flex: none; }\n#cs-root .cs-pending .cs-icon { margin-left: 4px; }\n#cs-root .cs-pending-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-pending.is-uploading { opacity: 0.6; }\n#cs-root .cs-pending.is-failed { box-shadow: inset 0 0 0 1px var(--cs-danger); }\n#cs-root .cs-pending-remove {\n position: absolute; right: 3px; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; border-radius: 999px;\n display: inline-flex; align-items: center; justify-content: center; color: var(--cs-text-3);\n}\n#cs-root .cs-pending-remove:hover { background: var(--cs-fill-2); color: var(--cs-text); }\n#cs-root .cs-pending-remove .cs-icon { width: 11px; height: 11px; margin: 0; }\n#cs-root .cs-files { display: grid; gap: 6px; margin-top: 6px; }\n#cs-root .cs-thumbs { display: flex; flex-wrap: wrap; gap: 6px; }\n#cs-root .cs-thumb { display: block; width: 112px; height: 72px; border-radius: 8px; overflow: hidden; box-shadow: 0 0 0 0.5px var(--cs-edge); background: var(--cs-fill); }\n#cs-root .cs-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }\n#cs-root .cs-file {\n display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 6px 10px; border-radius: 8px;\n background: var(--cs-fill); color: var(--cs-text); text-decoration: none; font-size: 12px; justify-self: start;\n}\n#cs-root .cs-file:hover { background: var(--cs-fill-2); }\n#cs-root .cs-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-file-size { color: var(--cs-text-3); flex: none; }\n\n/* Page overlays */\n#cs-root .cs-pin {\n position: absolute;\n transform: translate(-50%, -50%);\n padding: 2px;\n border-radius: 999px;\n background: var(--cs-card);\n box-shadow: 0 0 0 0.5px var(--cs-edge), 0 3px 10px rgba(0, 0, 0, 0.18);\n pointer-events: auto;\n transition: transform 0.15s ease;\n}\n#cs-root .cs-pin:hover { transform: translate(-50%, -50%) scale(1.08); }\n#cs-root .cs-pin.is-active { box-shadow: 0 0 0 2px var(--cs-accent), 0 3px 10px rgba(0, 0, 0, 0.18); }\n#cs-root .cs-pin.is-drifted { box-shadow: 0 0 0 1.5px var(--cs-warn), 0 3px 10px rgba(0, 0, 0, 0.18); }\n#cs-root .cs-pin-count {\n position: absolute; top: -5px; right: -6px; min-width: 15px; height: 15px; padding: 0 4px;\n border-radius: 999px; background: var(--cs-text); color: var(--cs-card);\n font-size: 9.5px; font-weight: 700; line-height: 15px; text-align: center;\n}\n#cs-root .cs-peek {\n position: absolute; width: 240px; padding: 10px 12px; border-radius: 12px;\n background: var(--cs-card); box-shadow: var(--cs-shadow); pointer-events: none;\n}\n#cs-root .cs-peek .cs-text { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }\n#cs-root .cs-outline {\n position: absolute; border-radius: 8px; pointer-events: none;\n box-shadow: 0 0 0 2px var(--cs-accent); background: var(--cs-accent-soft);\n}\n#cs-root .cs-outline.is-flash { animation: cs-flash 1.4s ease forwards; }\n@keyframes cs-flash { 0%, 60% { opacity: 1; } 100% { opacity: 0; } }\n#cs-root .cs-outline-label {\n position: absolute; max-width: 360px; padding: 3px 8px; border-radius: 6px; pointer-events: none;\n background: var(--cs-accent); color: #fff; font-size: 11.5px; font-weight: 500;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n#cs-root .cs-banner {\n position: absolute; top: 14px; left: 50%; transform: translateX(-50%);\n display: flex; align-items: center; gap: 8px; padding: 5px 5px 5px 12px; border-radius: 999px;\n background: var(--cs-glass); -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px);\n box-shadow: var(--cs-shadow); font-weight: 500; white-space: nowrap;\n}\n#cs-root .cs-banner button { padding: 3px 10px; border-radius: 999px; background: var(--cs-fill); font-size: 12px; }\n#cs-root .cs-banner button:hover { background: var(--cs-fill-2); }\n#cs-root .cs-toast {\n position: absolute; top: 14px; left: 50%; transform: translateX(-50%); padding: 6px 12px; border-radius: 999px;\n background: rgba(29, 29, 31, 0.9); color: #fff; font-size: 12px; font-weight: 500;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); pointer-events: none; white-space: nowrap;\n}\n\n/* Docked inside an open <dialog> */\n#cs-root.cs-inside .cs-bar { bottom: 8px; }\n#cs-root.cs-inside.cs-at-left .cs-bar { left: 8px; }\n#cs-root.cs-inside.cs-at-right .cs-bar { right: 8px; }\n#cs-root.cs-inside .cs-sheet { top: 8px; right: 8px; bottom: auto; max-height: 46%; width: min(300px, calc(100% - 16px)); }\n#cs-root.cs-inside .cs-unlock, #cs-root.cs-inside .cs-menu { bottom: 52px; }\n#cs-root.cs-inside .cs-toast { top: 8px; }\n";
|
|
1
|
+
export declare const toolbarCss = "\n#cs-root {\n --cs-text: #1d1d1f;\n --cs-text-2: #6e6e73;\n --cs-text-3: #a1a1a6;\n --cs-glass: rgba(251, 251, 253, 0.8);\n --cs-card: rgba(255, 255, 255, 0.94);\n --cs-fill: rgba(0, 0, 0, 0.05);\n --cs-fill-2: rgba(0, 0, 0, 0.08);\n --cs-line: rgba(0, 0, 0, 0.08);\n --cs-edge: rgba(0, 0, 0, 0.1);\n --cs-accent: #007aff;\n --cs-accent-soft: rgba(0, 122, 255, 0.12);\n --cs-danger: #ff3b30;\n --cs-warn: #ff9500;\n --cs-shadow: 0 0 0 0.5px var(--cs-edge), 0 10px 30px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n pointer-events: none;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--cs-text);\n font: 13px/1.4 -apple-system, BlinkMacSystemFont, \"SF Pro Text\", \"Helvetica Neue\", Inter, system-ui, sans-serif;\n letter-spacing: -0.005em;\n -webkit-font-smoothing: antialiased;\n text-align: left;\n}\n#cs-root.cs-dark {\n --cs-text: #f5f5f7;\n --cs-text-2: #a1a1a6;\n --cs-text-3: #6e6e73;\n --cs-glass: rgba(40, 40, 44, 0.78);\n --cs-card: rgba(36, 36, 40, 0.96);\n --cs-fill: rgba(255, 255, 255, 0.07);\n --cs-fill-2: rgba(255, 255, 255, 0.12);\n --cs-line: rgba(255, 255, 255, 0.08);\n --cs-edge: rgba(255, 255, 255, 0.12);\n --cs-accent: #0a84ff;\n --cs-accent-soft: rgba(10, 132, 255, 0.2);\n --cs-shadow: 0 0 0 0.5px var(--cs-edge), 0 10px 30px rgba(0, 0, 0, 0.45);\n}\n#cs-root.cs-inside { position: absolute; }\n#cs-root, #cs-root * { box-sizing: border-box; }\n#cs-root h2, #cs-root p { margin: 0; }\n#cs-root button, #cs-root input, #cs-root textarea {\n font: inherit; color: inherit; letter-spacing: inherit; margin: 0;\n}\n#cs-root button { background: none; border: 0; padding: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; }\n#cs-root button:disabled { cursor: default; opacity: 0.45; }\n#cs-root button:focus-visible, #cs-root [role=button]:focus-visible { outline: 2px solid var(--cs-accent); outline-offset: 2px; }\n#cs-root .cs-hit { pointer-events: auto; }\n#cs-root .cs-icon { display: block; flex: none; }\n\n/* Bar */\n#cs-root .cs-bar {\n position: absolute;\n bottom: 16px;\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 4px;\n border-radius: 999px;\n background: var(--cs-glass);\n -webkit-backdrop-filter: saturate(180%) blur(20px);\n backdrop-filter: saturate(180%) blur(20px);\n box-shadow: var(--cs-shadow);\n}\n#cs-root.cs-at-center .cs-bar { left: 50%; transform: translateX(-50%); }\n#cs-root.cs-at-left .cs-bar { left: 16px; }\n#cs-root.cs-at-right .cs-bar { right: 16px; }\n#cs-root .cs-tool {\n position: relative;\n width: 34px;\n height: 34px;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--cs-text);\n transition: background-color 0.15s ease, color 0.15s ease;\n}\n#cs-root .cs-tool:hover { background: var(--cs-fill); }\n#cs-root .cs-tool.is-on { background: var(--cs-fill-2); }\n#cs-root .cs-tool.is-accent { background: var(--cs-accent); color: #fff; }\n#cs-root .cs-tool[data-tip]::after {\n content: attr(data-tip);\n position: absolute;\n bottom: calc(100% + 10px);\n left: 50%;\n transform: translate(-50%, 2px);\n white-space: pre;\n padding: 4px 8px;\n border-radius: 7px;\n background: rgba(29, 29, 31, 0.92);\n color: #fff;\n font-size: 11.5px;\n font-weight: 500;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.12s ease, transform 0.12s ease;\n}\n#cs-root .cs-tool[data-tip]:hover::after { opacity: 1; transform: translate(-50%, 0); transition-delay: 0.35s; }\n#cs-root .cs-me { width: 34px; }\n#cs-root .cs-divider { width: 1px; height: 18px; background: var(--cs-line); margin: 0 3px; }\n#cs-root .cs-badge {\n position: absolute;\n top: 3px;\n right: 2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n border-radius: 999px;\n background: var(--cs-accent);\n color: #fff;\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n box-shadow: 0 0 0 2px var(--cs-glass);\n}\n#cs-root .cs-bar-lock {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n height: 34px;\n padding: 0 14px 0 12px;\n border-radius: 999px;\n font-weight: 500;\n}\n#cs-root .cs-bar-lock:hover { background: var(--cs-fill); }\n#cs-root .cs-avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n border-radius: 999px;\n color: #fff;\n font-weight: 600;\n letter-spacing: 0;\n line-height: 1;\n user-select: none;\n}\n\n/* Cards */\n#cs-root .cs-card {\n position: absolute;\n background: var(--cs-card);\n -webkit-backdrop-filter: saturate(180%) blur(24px);\n backdrop-filter: saturate(180%) blur(24px);\n border-radius: 14px;\n box-shadow: var(--cs-shadow);\n}\n#cs-root .cs-unlock, #cs-root .cs-menu { bottom: 64px; }\n#cs-root.cs-at-center .cs-unlock, #cs-root.cs-at-center .cs-menu { left: 50%; transform: translateX(-50%); }\n#cs-root.cs-at-left .cs-unlock, #cs-root.cs-at-left .cs-menu { left: 16px; }\n#cs-root.cs-at-right .cs-unlock, #cs-root.cs-at-right .cs-menu { right: 16px; }\n#cs-root .cs-unlock { width: 280px; padding: 20px; text-align: center; }\n#cs-root .cs-unlock-icon {\n width: 40px; height: 40px; margin: 0 auto 10px; border-radius: 12px;\n display: flex; align-items: center; justify-content: center; background: var(--cs-fill); color: var(--cs-text-2);\n}\n#cs-root .cs-unlock h2 { font-size: 15px; font-weight: 600; }\n#cs-root .cs-sub { color: var(--cs-text-2); margin-top: 4px; }\n#cs-root .cs-unlock form { margin-top: 14px; display: grid; gap: 8px; text-align: left; }\n#cs-root .cs-field { display: grid; gap: 4px; }\n#cs-root .cs-field span { font-size: 12px; color: var(--cs-text-2); padding-left: 2px; }\n#cs-root input, #cs-root textarea {\n width: 100%;\n border: 0;\n border-radius: 9px;\n background: var(--cs-fill);\n padding: 8px 10px;\n outline: none;\n transition: box-shadow 0.15s ease, background-color 0.15s ease;\n}\n#cs-root input:focus, #cs-root textarea:focus { background: transparent; box-shadow: 0 0 0 1px var(--cs-accent), 0 0 0 4px var(--cs-accent-soft); }\n#cs-root textarea { resize: none; }\n#cs-root .cs-error { color: var(--cs-danger); font-size: 12px; }\n#cs-root .cs-pad { padding: 6px 14px 0; }\n#cs-root .cs-hint { color: var(--cs-text-3); font-size: 11.5px; }\n\n/* Buttons */\n#cs-root .cs-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n height: 28px;\n padding: 0 11px;\n border-radius: 8px;\n background: var(--cs-fill);\n font-size: 12.5px;\n font-weight: 500;\n transition: background-color 0.15s ease;\n}\n#cs-root .cs-button:hover { background: var(--cs-fill-2); }\n#cs-root .cs-button.is-primary { background: var(--cs-accent); color: #fff; }\n#cs-root .cs-button.is-primary:hover { filter: brightness(1.06); }\n#cs-root .cs-button.is-danger { background: var(--cs-danger); color: #fff; }\n#cs-root .cs-button.is-wide { width: 100%; height: 32px; margin-top: 4px; }\n#cs-root .cs-icon-button {\n width: 26px; height: 26px; border-radius: 999px; flex: none;\n display: inline-flex; align-items: center; justify-content: center; color: var(--cs-text-2);\n}\n#cs-root .cs-icon-button:hover { background: var(--cs-fill); color: var(--cs-text); }\n#cs-root .cs-send {\n width: 26px; height: 26px; border-radius: 999px; flex: none;\n display: inline-flex; align-items: center; justify-content: center; background: var(--cs-accent); color: #fff;\n}\n#cs-root .cs-send:disabled { background: var(--cs-fill-2); color: var(--cs-text-3); opacity: 1; }\n\n/* Menu */\n#cs-root .cs-menu { width: 264px; padding: 6px; }\n#cs-root .cs-me-row { display: flex; align-items: center; gap: 10px; padding: 8px 8px 10px; }\n#cs-root .cs-me-name { display: flex; align-items: center; gap: 4px; min-width: 0; }\n#cs-root .cs-me-name strong { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-rename { display: flex; align-items: center; gap: 4px; flex: 1; }\n#cs-root .cs-rename input { padding: 5px 8px; }\n#cs-root .cs-sep { height: 1px; background: var(--cs-line); margin: 4px 8px; }\n#cs-root .cs-item {\n display: flex; align-items: center; justify-content: space-between; gap: 10px;\n width: 100%; min-height: 32px; padding: 0 8px; border-radius: 8px; text-align: left;\n}\n#cs-root button.cs-item:hover { background: var(--cs-fill); }\n#cs-root .cs-item.is-danger { color: var(--cs-danger); }\n#cs-root .cs-switch { position: relative; width: 32px; height: 20px; border-radius: 999px; background: var(--cs-fill-2); transition: background-color 0.2s ease; flex: none; }\n#cs-root .cs-switch::after {\n content: \"\"; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 999px;\n background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); transition: transform 0.2s ease;\n}\n#cs-root .cs-switch.is-on { background: #34c759; }\n#cs-root .cs-switch.is-on::after { transform: translateX(12px); }\n#cs-root .cs-segment { display: inline-flex; padding: 2px; border-radius: 8px; background: var(--cs-fill); }\n#cs-root .cs-segment button { padding: 2px 8px; border-radius: 6px; font-size: 12px; color: var(--cs-text-2); }\n#cs-root .cs-segment button.is-on { background: var(--cs-card); color: var(--cs-text); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12); font-weight: 500; }\n#cs-root .cs-menu .cs-hint { padding: 6px 8px 4px; }\n#cs-root .cs-invite { padding: 2px 8px 6px; }\n#cs-root .cs-invite input { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; padding: 6px 8px; }\n#cs-root .cs-invite .cs-hint { padding: 4px 0 0; }\n#cs-root .cs-confirm { padding: 10px; }\n#cs-root .cs-confirm h2 { font-size: 14px; font-weight: 600; }\n#cs-root .cs-row-end { display: flex; justify-content: flex-end; gap: 6px; margin-top: 12px; }\n\n/* Sheet */\n#cs-root .cs-sheet {\n top: 12px; right: 12px; max-height: calc(100% - 88px); width: min(360px, calc(100vw - 24px));\n display: flex; flex-direction: column; overflow: hidden;\n}\n#cs-root .cs-sheet-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 10px 4px 16px; }\n#cs-root .cs-sheet-head h2 { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }\n#cs-root .cs-tabs { display: flex; gap: 2px; margin: 4px 12px 6px; padding: 2px; border-radius: 9px; background: var(--cs-fill); }\n#cs-root .cs-tabs button {\n flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px;\n height: 26px; border-radius: 7px; font-size: 12px; font-weight: 500; color: var(--cs-text-2);\n}\n#cs-root .cs-tabs button.is-on { background: var(--cs-card); color: var(--cs-text); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12); }\n#cs-root .cs-count { font-size: 11px; color: var(--cs-text-3); font-variant-numeric: tabular-nums; }\n#cs-root .cs-list { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 2px 6px 8px; overscroll-behavior: contain; }\n#cs-root .cs-group { padding: 10px 10px 4px; font-size: 11px; font-weight: 600; color: var(--cs-text-3); text-transform: uppercase; letter-spacing: 0.04em; }\n#cs-root .cs-empty { padding: 36px 16px; text-align: center; color: var(--cs-text-2); display: grid; gap: 12px; justify-items: center; }\n#cs-root .cs-thread { border-radius: 10px; }\n#cs-root .cs-thread + .cs-thread { margin-top: 1px; }\n#cs-root .cs-thread.is-open { background: var(--cs-fill); }\n#cs-root .cs-thread-main { display: flex; gap: 10px; padding: 10px; border-radius: 10px; cursor: pointer; }\n#cs-root .cs-thread:not(.is-open) .cs-thread-main:hover { background: var(--cs-fill); }\n#cs-root .cs-thread-body { min-width: 0; flex: 1; }\n#cs-root .cs-meta { display: flex; align-items: center; gap: 6px; font-size: 12.5px; }\n#cs-root .cs-meta strong { font-weight: 600; }\n#cs-root .cs-meta time { color: var(--cs-text-3); font-size: 12px; }\n#cs-root .cs-text { margin-top: 2px; white-space: pre-wrap; overflow-wrap: anywhere; }\n#cs-root .cs-target { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin-top: 4px; font-size: 11.5px; color: var(--cs-text-3); }\n#cs-root .cs-note { display: inline-flex; align-items: center; gap: 5px; font-weight: 500; }\n#cs-root .cs-note::before { content: \"\"; width: 6px; height: 6px; border-radius: 999px; background: currentColor; }\n#cs-root .cs-note.is-quiet { color: var(--cs-text-2); }\n#cs-root .cs-note.is-warn { color: var(--cs-warn); }\n#cs-root .cs-note.is-bad { color: var(--cs-danger); }\n#cs-root .cs-replies { margin-top: 4px; font-size: 12px; color: var(--cs-accent); font-weight: 500; }\n#cs-root .cs-thread-more { padding: 0 10px 10px 44px; display: grid; gap: 10px; }\n#cs-root .cs-message { display: flex; gap: 8px; }\n#cs-root .cs-reply { display: flex; align-items: flex-end; gap: 6px; padding: 4px 4px 4px 0; border-radius: 10px; }\n#cs-root .cs-reply textarea { min-height: 32px; max-height: 120px; field-sizing: content; }\n#cs-root .cs-thread.is-open .cs-reply textarea { background: var(--cs-card); }\n#cs-root .cs-actions { display: flex; flex-wrap: wrap; gap: 6px; }\n#cs-root .cs-thread.is-open .cs-button { background: var(--cs-card); box-shadow: 0 0 0 0.5px var(--cs-edge); }\n#cs-root .cs-thread.is-open .cs-button:hover { background: var(--cs-fill-2); }\n\n/* Mentions */\n#cs-root .cs-mention { color: var(--cs-accent); background: var(--cs-accent-soft); border-radius: 4px; padding: 0 3px; font-weight: 500; }\n#cs-root .cs-delivery { margin-top: 4px; font-size: 11.5px; color: var(--cs-text-3); display: flex; align-items: center; gap: 5px; }\n#cs-root .cs-delivery::before { content: \"\"; width: 6px; height: 6px; border-radius: 999px; background: #34c759; }\n#cs-root .cs-people {\n position: fixed; z-index: 1; width: 264px; padding: 5px; border-radius: 12px;\n background: var(--cs-card); box-shadow: var(--cs-shadow); pointer-events: auto;\n}\n#cs-root .cs-person { display: flex; align-items: center; gap: 9px; padding: 6px 8px; border-radius: 8px; cursor: pointer; }\n#cs-root .cs-person.is-on { background: var(--cs-accent); color: #fff; }\n#cs-root .cs-person-text { display: grid; line-height: 1.25; min-width: 0; }\n#cs-root .cs-person-text strong { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-person-text small { font-size: 11.5px; color: var(--cs-text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-person.is-on small { color: rgba(255, 255, 255, 0.8); }\n#cs-root .cs-field small { font-size: 11.5px; color: var(--cs-text-3); padding-left: 2px; }\n#cs-root .cs-me-lines { display: grid; line-height: 1.25; min-width: 0; }\n#cs-root .cs-me-lines small { font-size: 11.5px; color: var(--cs-text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n\n/* Composer */\n#cs-root .cs-composer { width: min(320px, calc(100% - 16px)); }\n#cs-root .cs-composer-target {\n display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 12px;\n border-bottom: 1px solid var(--cs-line); font-size: 12px; color: var(--cs-text-2);\n}\n#cs-root .cs-composer-name { font-weight: 500; color: var(--cs-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n#cs-root .cs-composer-target code {\n margin-left: auto; font: 11px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--cs-text-3);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; min-width: 0; max-width: 45%;\n}\n#cs-root .cs-composer-body { display: flex; gap: 10px; padding: 12px 12px 4px; }\n#cs-root .cs-composer-body textarea { background: transparent; padding: 3px 0; min-height: 56px; box-shadow: none; border-radius: 0; }\n#cs-root .cs-composer-foot { display: flex; align-items: center; gap: 6px; padding: 6px 8px 8px 40px; }\n#cs-root .cs-composer-foot .cs-hint { margin-left: auto; }\n\n/* Roles and fields */\n#cs-root .cs-details-input { display: grid; gap: 8px; padding: 2px 12px 6px 46px; }\n#cs-root .cs-details-edit .cs-details-input { padding: 0; }\n#cs-root .cs-detail-field { display: grid; gap: 3px; }\n#cs-root .cs-detail-field span, #cs-root .cs-roles-label, #cs-root .cs-detail-list dt {\n font-size: 11px; font-weight: 600; color: var(--cs-text-3); text-transform: uppercase; letter-spacing: 0.03em;\n}\n#cs-root .cs-detail-field textarea { padding: 6px 8px; min-height: 30px; max-height: 120px; field-sizing: content; }\n#cs-root .cs-thread.is-open .cs-detail-field textarea { background: var(--cs-card); }\n#cs-root .cs-roles { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }\n#cs-root .cs-roles-label { margin-right: 4px; }\n#cs-root .cs-chip {\n display: inline-flex; align-items: center; gap: 2px; height: 24px; padding: 0 9px; border-radius: 999px;\n background: var(--cs-fill); font-size: 12px; font-weight: 500; color: var(--cs-text-2);\n}\n#cs-root button.cs-chip:hover { background: var(--cs-fill-2); }\n#cs-root .cs-chip.is-on, #cs-root button.cs-chip.is-on:hover { background: var(--cs-accent); color: #fff; }\n#cs-root .cs-chip.is-static { padding-right: 3px; color: var(--cs-text); }\n#cs-root .cs-chip.is-static button {\n width: 18px; height: 18px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; color: var(--cs-text-3);\n}\n#cs-root .cs-chip.is-static button:hover { background: var(--cs-fill-2); color: var(--cs-text); }\n#cs-root .cs-chip.is-static .cs-icon { width: 11px; height: 11px; }\n#cs-root .cs-chips { display: flex; flex-wrap: wrap; gap: 4px; }\n#cs-root .cs-role {\n display: inline-flex; align-items: center; height: 18px; padding: 0 7px; border-radius: 999px;\n background: var(--cs-accent-soft); color: var(--cs-accent); font-size: 11px; font-weight: 600;\n}\n#cs-root .cs-detail-list { display: grid; gap: 6px; margin: 6px 0 0; }\n#cs-root .cs-detail-list > div { padding: 6px 8px; border-radius: 8px; background: var(--cs-fill); }\n#cs-root .cs-thread.is-open .cs-detail-list > div { background: var(--cs-card); }\n#cs-root .cs-detail-list dd { margin: 1px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }\n#cs-root .cs-detail-list.is-clamped dd { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }\n#cs-root .cs-details-edit { display: grid; gap: 8px; }\n#cs-root .cs-details-edit .cs-row-end { margin-top: 0; }\n#cs-root .cs-settings { width: 280px; }\n#cs-root .cs-settings-head { display: flex; align-items: center; gap: 4px; padding: 4px 4px 6px; }\n#cs-root .cs-settings-head h2 { font-size: 14px; font-weight: 600; }\n#cs-root .cs-settings-body { display: grid; gap: 6px; padding: 2px 8px 8px; }\n#cs-root .cs-field-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; min-height: 28px; padding-left: 2px; }\n#cs-root .cs-add-row { display: flex; align-items: center; gap: 4px; }\n#cs-root .cs-add-row input { padding: 5px 8px; }\n#cs-root .cs-settings .cs-hint { padding: 6px 8px 4px; }\n\n/* Attachments */\n#cs-root .cs-attach { display: inline-flex; gap: 0; flex: none; }\n#cs-root .cs-tray { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 12px 2px 46px; }\n#cs-root .cs-thread-more .cs-tray { padding: 0; }\n#cs-root .cs-pending {\n position: relative; display: inline-flex; align-items: center; gap: 6px; max-width: 170px; height: 34px;\n padding: 0 24px 0 4px; border-radius: 8px; background: var(--cs-fill); font-size: 11.5px; color: var(--cs-text-2);\n}\n#cs-root .cs-pending img { width: 26px; height: 26px; border-radius: 5px; object-fit: cover; flex: none; }\n#cs-root .cs-pending .cs-icon { margin-left: 4px; }\n#cs-root .cs-pending-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-pending.is-uploading { opacity: 0.6; }\n#cs-root .cs-pending.is-failed { box-shadow: inset 0 0 0 1px var(--cs-danger); }\n#cs-root .cs-pending-remove {\n position: absolute; right: 3px; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; border-radius: 999px;\n display: inline-flex; align-items: center; justify-content: center; color: var(--cs-text-3);\n}\n#cs-root .cs-pending-remove:hover { background: var(--cs-fill-2); color: var(--cs-text); }\n#cs-root .cs-pending-remove .cs-icon { width: 11px; height: 11px; margin: 0; }\n#cs-root .cs-files { display: grid; gap: 6px; margin-top: 6px; }\n#cs-root .cs-thumbs { display: flex; flex-wrap: wrap; gap: 6px; }\n#cs-root .cs-thumb { display: block; width: 112px; height: 72px; border-radius: 8px; overflow: hidden; box-shadow: 0 0 0 0.5px var(--cs-edge); background: var(--cs-fill); }\n#cs-root .cs-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }\n#cs-root .cs-file {\n display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 6px 10px; border-radius: 8px;\n background: var(--cs-fill); color: var(--cs-text); text-decoration: none; font-size: 12px; justify-self: start;\n}\n#cs-root .cs-file:hover { background: var(--cs-fill-2); }\n#cs-root .cs-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n#cs-root .cs-file-size { color: var(--cs-text-3); flex: none; }\n\n/* Page overlays */\n#cs-root .cs-pin {\n position: absolute;\n transform: translate(-50%, -50%);\n padding: 2px;\n border-radius: 999px;\n background: var(--cs-card);\n box-shadow: 0 0 0 0.5px var(--cs-edge), 0 3px 10px rgba(0, 0, 0, 0.18);\n pointer-events: auto;\n transition: transform 0.15s ease;\n}\n#cs-root .cs-pin:hover { transform: translate(-50%, -50%) scale(1.08); }\n#cs-root .cs-pin.is-active { box-shadow: 0 0 0 2px var(--cs-accent), 0 3px 10px rgba(0, 0, 0, 0.18); }\n#cs-root .cs-pin.is-drifted { box-shadow: 0 0 0 1.5px var(--cs-warn), 0 3px 10px rgba(0, 0, 0, 0.18); }\n#cs-root .cs-pin-count {\n position: absolute; top: -5px; right: -6px; min-width: 15px; height: 15px; padding: 0 4px;\n border-radius: 999px; background: var(--cs-text); color: var(--cs-card);\n font-size: 9.5px; font-weight: 700; line-height: 15px; text-align: center;\n}\n#cs-root .cs-peek {\n position: absolute; width: 240px; padding: 10px 12px; border-radius: 12px;\n background: var(--cs-card); box-shadow: var(--cs-shadow); pointer-events: none;\n}\n#cs-root .cs-peek .cs-text { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }\n#cs-root .cs-outline {\n position: absolute; border-radius: 8px; pointer-events: none;\n box-shadow: 0 0 0 2px var(--cs-accent); background: var(--cs-accent-soft);\n}\n#cs-root .cs-outline.is-flash { animation: cs-flash 1.4s ease forwards; }\n@keyframes cs-flash { 0%, 60% { opacity: 1; } 100% { opacity: 0; } }\n#cs-root .cs-outline-label {\n position: absolute; max-width: 360px; padding: 3px 8px; border-radius: 6px; pointer-events: none;\n background: var(--cs-accent); color: #fff; font-size: 11.5px; font-weight: 500;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n#cs-root .cs-banner {\n position: absolute; top: 14px; left: 50%; transform: translateX(-50%);\n display: flex; align-items: center; gap: 8px; padding: 5px 5px 5px 12px; border-radius: 999px;\n background: var(--cs-glass); -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px);\n box-shadow: var(--cs-shadow); font-weight: 500; white-space: nowrap;\n}\n#cs-root .cs-banner button { padding: 3px 10px; border-radius: 999px; background: var(--cs-fill); font-size: 12px; }\n#cs-root .cs-banner button:hover { background: var(--cs-fill-2); }\n#cs-root .cs-toast {\n position: absolute; top: 14px; left: 50%; transform: translateX(-50%); padding: 6px 12px; border-radius: 999px;\n background: rgba(29, 29, 31, 0.9); color: #fff; font-size: 12px; font-weight: 500;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); pointer-events: none; white-space: nowrap;\n}\n\n/* Docked inside an open <dialog> */\n#cs-root.cs-inside .cs-bar { bottom: 8px; }\n#cs-root.cs-inside.cs-at-left .cs-bar { left: 8px; }\n#cs-root.cs-inside.cs-at-right .cs-bar { right: 8px; }\n#cs-root.cs-inside .cs-sheet { top: 8px; right: 8px; bottom: auto; max-height: 46%; width: min(300px, calc(100% - 16px)); }\n#cs-root.cs-inside .cs-unlock, #cs-root.cs-inside .cs-menu { bottom: 52px; }\n#cs-root.cs-inside .cs-toast { top: 8px; }\n";
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
export type ToolbarPosition = "bottom-left" | "bottom-center" | "bottom-right";
|
|
2
2
|
export type ToolbarTheme = "light" | "dark" | "system";
|
|
3
|
-
export declare function CommentToolbar({ sessionPath, pollMs, position, theme, }: {
|
|
3
|
+
export declare function CommentToolbar({ sessionPath, pollMs, position, theme, navigate, }: {
|
|
4
4
|
sessionPath?: string;
|
|
5
5
|
pollMs?: number;
|
|
6
6
|
/** Default bar position. Each person can override it from their menu. */
|
|
7
7
|
position?: ToolbarPosition;
|
|
8
8
|
/** "system" follows the OS appearance. Light by default so it matches most host pages. */
|
|
9
9
|
theme?: ToolbarTheme;
|
|
10
|
+
/**
|
|
11
|
+
* Opens another page of the app, for example `(href) => router.navigate({ href })`.
|
|
12
|
+
* Without it, selecting a comment from another page does a full page load.
|
|
13
|
+
*/
|
|
14
|
+
navigate?: (href: string) => void;
|
|
10
15
|
}): import("react").ReactPortal | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devaccel-labs/comment-service",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "In-UI component comments for TanStack Start on Cloudflare. Anchors survive redeploys.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"esbuild": "^0.28.1",
|
|
46
46
|
"@devaccel/comment-core": "0.1.0",
|
|
47
|
+
"@devaccel/comment-react": "0.1.0",
|
|
47
48
|
"@devaccel/comment-compiler": "0.1.0",
|
|
48
49
|
"@devaccel/comment-start": "0.1.0",
|
|
49
|
-
"@devaccel/comment-react": "0.1.0",
|
|
50
50
|
"@devaccel/comment-server": "0.1.0"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|