@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 _jsx4, jsxs as _jsxs4, Fragment as _Fragment2 } from "react/jsx-runtime";
358
- import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
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
- const payload = await request(`${session.apiUrl}/v1/threads`, memberToken);
449
- return payload.threads;
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 _jsx2, jsxs as _jsxs2 } from "react/jsx-runtime";
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] = useState([]);
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 _jsx2("div", { className: "cs-tray", "data-cs-ui": "attachment-tray", children: items.map((item) => _jsxs2("div", { className: `cs-pending is-${item.status}`, "data-cs-ui": "pending-file", "data-status": item.status, title: item.error ?? item.name, children: [item.preview ? _jsx2("img", { src: item.preview, alt: "" }) : _jsx2(FileIcon, {}), _jsx2("span", { className: "cs-pending-name", children: item.name }), _jsx2("button", { type: "button", className: "cs-pending-remove", "aria-label": `Remove ${item.name}`, onClick: () => onRemove(item.key), children: _jsx2(Close, {}) })] }, item.key)) });
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 _jsxs2("span", { className: "cs-attach", children: [_jsx2("input", { ref: input, type: "file", multiple: true, accept: ACCEPT, hidden: true, "data-cs-ui": "file-input", onChange: (event) => {
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
- } }), _jsx2("button", { type: "button", className: "cs-icon-button", "data-cs-ui": "attach", "aria-label": "Attach files", onClick: () => input.current?.click(), children: _jsx2(Paperclip, {}) }), _jsx2("button", { type: "button", className: "cs-icon-button", "data-cs-ui": "snapshot", "aria-label": "Attach a screenshot of the page", disabled: capturing, onClick: onSnapshot, children: _jsx2(Camera, {}) })] });
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 _jsxs2("div", { className: "cs-files", "data-cs-ui": "attachments", children: [images.length ? _jsx2("div", { className: "cs-thumbs", children: images.map((file) => _jsx2("a", { href: file.url, target: "_blank", rel: "noreferrer noopener", className: "cs-thumb", title: file.name, "data-cs-ui": "attachment", children: _jsx2("img", { src: file.url, alt: file.name, loading: "lazy" }) }, file.id)) }) : null, others.map((file) => _jsxs2("a", { href: file.url, target: "_blank", rel: "noreferrer noopener", className: "cs-file", "data-cs-ui": "attachment", children: [_jsx2(FileIcon, {}), _jsx2("span", { className: "cs-file-name", children: file.name }), _jsx2("span", { className: "cs-file-size", children: formatSize(file.size) })] }, file.id))] });
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 _Fragment, jsx as _jsx3, jsxs as _jsxs3 } from "react/jsx-runtime";
655
- import { useMemo, useRef as useRef2, useState as useState2 } from "react";
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 _jsx3(_Fragment, { children: body });
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 ? _jsx3("span", { className: "cs-mention", children: part }, index) : part);
672
- return _jsx3(_Fragment, { children: parts });
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] = useState2(0);
677
- const [index, setIndex] = useState2(0);
678
- const [dismissed, setDismissed] = useState2(null);
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 _jsxs3(_Fragment, { children: [_jsx3("textarea", { ref, ...rest, value, "aria-autocomplete": "list", "aria-expanded": open, onChange: (event) => {
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(_jsx3("div", { className: "cs-people cs-hit", role: "listbox", "data-cs-ui": "mention-list", style: {
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) => _jsxs3("div", { role: "option", "aria-selected": member === selected, "data-cs-ui": "mention-option", className: member === selected ? "cs-person is-on" : "cs-person", onMouseDown: (event) => {
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: [_jsx3("span", { className: "cs-avatar", style: { width: 24, height: 24, fontSize: 10, background: colorFor(member.name) }, children: initials(member.name) }), _jsxs3("span", { className: "cs-person-text", children: [_jsx3("strong", { children: member.name }), _jsx3("small", { children: member.email })] })] }, member.id)) }), root) : null] });
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 _jsx4("span", { className: "cs-avatar", style: { width: size, height: size, fontSize: size * 0.4, background: colorFor(name) }, children: initials(name) });
2716
- }
2717
- function CommentToolbar({ sessionPath = "/api/comment-service/session", pollMs = 4e3, position = "bottom-center", theme = "light" }) {
2718
- const [phase, setPhase] = useState3("boot");
2719
- const [session, setSession] = useState3(null);
2720
- const [member, setMember] = useState3(null);
2721
- const [threads, setThreads] = useState3([]);
2722
- const [route, setRoute] = useState3("/");
2723
- const [surface, setSurface] = useState3("none");
2724
- const [tab, setTab] = useState3("page");
2725
- const [mode, setMode] = useState3("browse");
2726
- const [reattachId, setReattachId] = useState3(null);
2727
- const [activeId, setActiveId] = useState3(null);
2728
- const [peekId, setPeekId] = useState3(null);
2729
- const [draft, setDraft] = useState3(null);
2730
- const [body, setBody] = useState3("");
2731
- const [reply, setReply] = useState3("");
2732
- const [name, setName] = useState3("");
2733
- const [passphrase, setPassphrase] = useState3("");
2734
- const [email, setEmail] = useState3("");
2735
- const [members, setMembers] = useState3([]);
2736
- const [profile, setProfile] = useState3(null);
2737
- const [deliveries, setDeliveries] = useState3({});
2738
- const [invite, setInvite] = useState3(null);
2739
- const [capturing, setCapturing] = useState3(false);
2740
- const [inviteLink, setInviteLink] = useState3(null);
2741
- const [renaming, setRenaming] = useState3(null);
2742
- const [confirmReset, setConfirmReset] = useState3(false);
2743
- const [error, setError] = useState3("");
2744
- const [toast, setToast] = useState3("");
2745
- const [busy, setBusy] = useState3(false);
2746
- const [prefs, setPrefs] = useState3({});
2747
- const [portal, setPortal] = useState3(null);
2748
- const [mounted, setMounted] = useState3(false);
2749
- const [highlight, setHighlight] = useState3(null);
2750
- const [flash, setFlash] = useState3(null);
2751
- const [placed, setPlaced] = useState3([]);
2752
- const [systemDark, setSystemDark] = useState3(false);
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
- setThreads(await listThreads(session, member));
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 (!stop)
2870
- setThreads(next);
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 = _jsx4("div", { className: "cs-bar cs-hit", "data-cs-ui": "bar", role: "toolbar", "aria-label": "Comments toolbar", children: !member ? _jsxs4("button", { className: "cs-bar-lock", "data-cs-ui": "launcher", type: "button", "aria-label": "Comments", onClick: () => {
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: [_jsx4(Lock, {}), _jsx4("span", { children: "Comments" })] }) : _jsxs4(_Fragment2, { children: [_jsx4("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: _jsx4(AddComment, {}) }), _jsxs4("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: () => {
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: [_jsx4(Inbox, {}), counts.page > 0 ? _jsx4("span", { className: "cs-badge", children: counts.page }) : null] }), _jsx4("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 ? _jsx4(EyeOff, {}) : _jsx4(Eye, {}) }), _jsx4("span", { className: "cs-divider" }), _jsx4("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: () => {
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: _jsx4(Avatar, { name: memberName, size: 24 }) })] }) });
3191
- const unlockCard = _jsxs4("section", { className: "cs-card cs-unlock cs-hit", "data-cs-ui": "panel", "aria-label": "Unlock comments", children: [_jsx4("div", { className: "cs-unlock-icon", children: _jsx4(Lock, { size: 18 }) }), _jsx4("h2", { children: "Comments" }), _jsx4("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." }), _jsxs4("form", { onSubmit: (event) => {
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: [_jsxs4("label", { className: "cs-field", children: [_jsx4("span", { children: "Your name" }), _jsx4("input", { "data-cs-ui": "unlock-name", value: name, onChange: (event) => setName(event.target.value), autoComplete: "name", required: true })] }), invite ? null : _jsxs4("label", { className: "cs-field", children: [_jsx4("span", { children: "Passphrase" }), _jsx4("input", { "data-cs-ui": "unlock-passphrase", type: "password", value: passphrase, onChange: (event) => setPassphrase(event.target.value), autoComplete: "current-password", required: true })] }), _jsxs4("label", { className: "cs-field", children: [_jsx4("span", { children: "Email" }), _jsx4("input", { "data-cs-ui": "unlock-email", type: "email", value: email, onChange: (event) => setEmail(event.target.value), autoComplete: "email", required: true }), _jsx4("small", { children: "So teammates can @mention you." })] }), error ? _jsx4("p", { className: "cs-error", "data-cs-ui": "error", children: error }) : null, _jsx4("button", { className: "cs-button is-primary is-wide", "data-cs-ui": "unlock-submit", type: "submit", disabled: busy, children: busy ? "Unlocking\u2026" : invite ? "Join" : "Unlock" })] })] });
3213
- const menuCard = _jsx4("section", { className: "cs-card cs-menu cs-hit", "data-cs-ui": "menu", "aria-label": "Your comment settings", children: confirmReset ? _jsxs4("div", { className: "cs-confirm", "data-cs-ui": "reset-confirm", children: [_jsx4("h2", { children: "Reset on this device?" }), _jsx4("p", { className: "cs-sub", children: "You\u2019ll be signed out and your name and preferences are cleared. Comments aren\u2019t affected." }), _jsxs4("div", { className: "cs-row-end", children: [_jsx4("button", { className: "cs-button", type: "button", onClick: () => setConfirmReset(false), children: "Cancel" }), _jsx4("button", { className: "cs-button is-danger", "data-cs-ui": "reset-submit", type: "button", onClick: reset, children: "Reset" })] })] }) : _jsxs4(_Fragment2, { children: [_jsxs4("div", { className: "cs-me-row", children: [_jsx4(Avatar, { name: renaming ?? memberName, size: 36 }), renaming != null ? _jsxs4("form", { className: "cs-rename", onSubmit: (event) => {
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: [_jsx4("input", { "data-cs-ui": "rename-input", "aria-label": "Your name", value: renaming, maxLength: 40, autoFocus: true, onChange: (event) => setRenaming(event.target.value), onKeyDown: (event) => {
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
- } }), _jsx4("button", { className: "cs-icon-button", type: "submit", "aria-label": "Save name", disabled: busy, children: _jsx4(Check, {}) })] }) : _jsxs4("div", { className: "cs-me-name", children: [_jsxs4("span", { className: "cs-me-lines", children: [_jsx4("strong", { children: memberName }), _jsx4("small", { children: profile?.email ?? readJwt(member ?? "")?.email ?? "" })] }), _jsx4("button", { className: "cs-icon-button", "data-cs-ui": "rename", type: "button", "aria-label": "Change name", onClick: () => {
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: _jsx4(Pencil, {}) })] })] }), error ? _jsx4("p", { className: "cs-error", children: error }) : null, _jsx4("div", { className: "cs-sep" }), _jsxs4("button", { className: "cs-item", type: "button", role: "switch", "aria-checked": !pinsHidden, onClick: togglePins, children: [_jsx4("span", { children: "Show pins" }), _jsx4("span", { className: pinsHidden ? "cs-switch" : "cs-switch is-on" })] }), _jsxs4("button", { className: "cs-item", "data-cs-ui": "mention-emails", type: "button", role: "switch", "aria-checked": profile?.mentionEmails ?? true, disabled: !profile, onClick: () => {
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: [_jsx4("span", { children: "Email me when mentioned" }), _jsx4("span", { className: profile?.mentionEmails ?? true ? "cs-switch is-on" : "cs-switch" })] }), _jsxs4("div", { className: "cs-item is-static", children: [_jsx4("span", { children: "Position" }), _jsx4("span", { className: "cs-segment", role: "radiogroup", "aria-label": "Toolbar position", children: positions.map((option) => _jsx4("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)) })] }), _jsx4("div", { className: "cs-sep" }), _jsx4("button", { className: "cs-item", "data-cs-ui": "invite", type: "button", disabled: busy, onClick: () => {
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: _jsx4("span", { children: "Invite someone" }) }), inviteLink ? _jsxs4("div", { className: "cs-invite", children: [_jsx4("input", { "data-cs-ui": "invite-link", "aria-label": "Invite link", readOnly: true, value: inviteLink, onFocus: (event) => event.currentTarget.select() }), _jsx4("p", { className: "cs-hint", children: "Anyone with this link can join for 7 days. Changing the passphrase cancels it." })] }) : null, _jsx4("button", { className: "cs-item", "data-cs-ui": "lock", type: "button", onClick: lock, children: _jsx4("span", { children: "Lock" }) }), _jsx4("button", { className: "cs-item is-danger", "data-cs-ui": "reset", type: "button", onClick: () => setConfirmReset(true), children: _jsx4("span", { children: "Reset on this device\u2026" }) }), _jsx4("p", { className: "cs-hint", children: "C comment \xB7 I list \xB7 H pins" })] }) });
3260
- const inbox = _jsxs4("section", { className: "cs-card cs-sheet cs-hit", "data-cs-ui": "panel", "aria-label": "Comments", children: [_jsxs4("header", { className: "cs-sheet-head", children: [_jsx4("h2", { children: "Comments" }), _jsx4("button", { className: "cs-icon-button", type: "button", "aria-label": "Close comments", onClick: () => setSurface("none"), children: _jsx4(Close, {}) })] }), _jsx4("div", { className: "cs-tabs", role: "tablist", children: [
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]) => _jsxs4("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 ? _jsx4("span", { className: "cs-count", children: count }) : null] }, value)) }), error ? _jsx4("p", { className: "cs-error cs-pad", "data-cs-ui": "error", children: error }) : null, _jsxs4("div", { className: "cs-list", children: [listed.length === 0 ? _jsxs4("div", { className: "cs-empty", children: [_jsx4("p", { children: tab === "resolved" ? "Nothing resolved yet." : tab === "page" ? "No comments on this page." : "No open comments." }), tab !== "resolved" ? _jsxs4("button", { className: "cs-button", type: "button", onClick: startComment, children: [_jsx4(AddComment, {}), " Add a comment"] }) : null] }) : null, groups.map((group) => _jsxs4("div", { children: [group.route ? _jsx4("div", { className: "cs-group", children: group.route === route ? "This page" : group.route }) : null, group.items.map((item) => {
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 _jsxs4("article", { className: expanded ? "cs-thread is-open" : "cs-thread", "data-cs-ui": "thread", "data-thread-id": thread.id, "data-cs-status": item.status, children: [_jsxs4("div", { className: "cs-thread-main", role: "button", tabIndex: 0, "aria-expanded": expanded, "aria-label": `${thread.createdBy}: ${first?.body ?? ""}`, onClick: () => setActiveId(expanded ? null : thread.id), onKeyDown: (event) => {
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
- setActiveId(expanded ? null : thread.id);
3481
+ select(item);
3274
3482
  }
3275
- }, onMouseEnter: () => setPeekId(null), children: [_jsx4(Avatar, { name: thread.createdBy }), _jsxs4("div", { className: "cs-thread-body", children: [_jsxs4("div", { className: "cs-meta", children: [_jsx4("strong", { children: thread.createdBy }), _jsx4("time", { dateTime: thread.createdAt, children: ago(thread.createdAt) })] }), _jsx4("p", { className: "cs-text", children: _jsx4(MessageText, { body: first?.body ?? "", mentions: first?.mentions }) }), _jsx4(MessageAttachments, { attachments: first?.attachments }), first && deliveryText(deliveries[first.id]) ? _jsx4("div", { className: "cs-delivery", "data-cs-ui": "delivery", children: deliveryText(deliveries[first.id]) }) : null, _jsxs4("div", { className: "cs-target", children: [_jsx4("span", { title: thread.anchor.component?.file ?? void 0, children: targetName(thread.anchor) }), note ? _jsx4("span", { className: `cs-note is-${note.tone}`, children: note.text }) : null] }), !expanded && thread.messages.length > 1 ? _jsxs4("div", { className: "cs-replies", children: [thread.messages.length - 1, " ", thread.messages.length === 2 ? "reply" : "replies"] }) : null] })] }), expanded ? _jsxs4("div", { className: "cs-thread-more", children: [thread.messages.slice(1).map((message) => _jsxs4("div", { className: "cs-message", children: [_jsx4(Avatar, { name: message.author, size: 20 }), _jsxs4("div", { children: [_jsxs4("div", { className: "cs-meta", children: [_jsx4("strong", { children: message.author }), _jsx4("time", { dateTime: message.createdAt, children: ago(message.createdAt) })] }), _jsx4("p", { className: "cs-text", children: _jsx4(MessageText, { body: message.body, mentions: message.mentions }) }), _jsx4(MessageAttachments, { attachments: message.attachments }), deliveryText(deliveries[message.id]) ? _jsx4("div", { className: "cs-delivery", "data-cs-ui": "delivery", children: deliveryText(deliveries[message.id]) }) : null] })] }, message.id)), _jsxs4("form", { className: "cs-reply", onSubmit: async (event) => {
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: [_jsx4(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) => {
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
- } }), _jsx4(AttachButtons, { onFiles: replyFiles.add, capturing, onSnapshot: () => snapshot(thread.route === route ? resolveInDocument(thread.anchor, route).element : null, replyFiles.add) }), _jsx4("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: _jsx4(Send, {}) })] }), _jsx4(AttachmentTray, { items: replyFiles.items, onRemove: replyFiles.remove }), _jsxs4("div", { className: "cs-actions", children: [_jsxs4("button", { className: "cs-button", "data-cs-ui": "resolve", type: "button", onClick: () => {
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" ? _jsx4(Undo, {}) : _jsx4(Check, {}), thread.status === "resolved" ? "Reopen" : "Resolve"] }), onPage && thread.status === "open" ? _jsxs4("button", { className: "cs-button", type: "button", onClick: () => showOnPage(thread), children: [_jsx4(Target, {}), " Show"] }) : null, !onPage && thread.status === "open" && thread.route === route ? _jsxs4("button", { className: "cs-button", "data-cs-ui": "reattach", type: "button", onClick: () => {
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: [_jsx4(Relink, {}), " Reattach"] }) : null] })] }) : null] }, thread.id);
3529
+ }, children: [_jsx5(Relink, {}), " Reattach"] }) : null] })] }) : null] }, thread.id);
3322
3530
  })] }, group.route ?? "all"))] })] });
3323
- const ui = _jsxs4("div", { id: "cs-root", className: [
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 ? _jsxs4(_Fragment2, { children: [_jsx4("div", { className: "cs-outline", style: { top: highlight.top, left: highlight.left, width: highlight.width, height: highlight.height } }), _jsx4("div", { className: "cs-outline-label", style: { top: Math.max(4, highlight.top - 26), left: highlight.left }, children: highlight.label })] }) : null, flash ? _jsx4("div", { className: "cs-outline is-flash", style: { top: flash.top, left: flash.left, width: flash.width, height: flash.height } }) : null, mode !== "browse" ? _jsxs4("div", { className: "cs-banner cs-hit", "data-cs-ui": "mode", children: [_jsx4(Cursor, {}), _jsx4("span", { children: mode === "reattach" ? "Click the element this comment belongs to" : "Click anything to comment" }), _jsx4("button", { type: "button", onClick: () => {
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 : _jsxs4("button", { type: "button", className: [
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: [_jsx4(Avatar, { name: item.thread.createdBy, size: 22 }), item.thread.messages.length > 1 ? _jsx4("span", { className: "cs-pin-count", children: item.thread.messages.length }) : null] }, item.thread.id)), peek && !pinsHidden && peek.top != null && peek.left != null ? _jsxs4("div", { className: "cs-peek", style: { top: peek.top + 18, left: Math.max(8, peek.left - 12) }, children: [_jsxs4("div", { className: "cs-meta", children: [_jsx4(Avatar, { name: peek.thread.createdBy, size: 18 }), _jsx4("strong", { children: peek.thread.createdBy }), _jsx4("time", { children: ago(peek.thread.createdAt) })] }), _jsx4("p", { className: "cs-text", children: _jsx4(MessageText, { body: peek.thread.messages[0]?.body ?? "", mentions: peek.thread.messages[0]?.mentions }) }), peek.thread.messages.length > 1 ? _jsxs4("div", { className: "cs-replies", children: [peek.thread.messages.length - 1, " ", peek.thread.messages.length === 2 ? "reply" : "replies"] }) : null] }) : null, draft ? _jsxs4("form", { className: "cs-card cs-composer cs-hit", "data-cs-ui": "composer", style: { top: draft.top, left: draft.left }, onSubmit: async (event) => {
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 || !body.trim() && !draftFiles.ids.length || draftFiles.uploading)
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: [_jsxs4("div", { className: "cs-composer-target", children: [_jsx4(Target, {}), _jsx4("span", { className: "cs-composer-name", children: targetName(draft.anchor) }), draft.anchor.component?.file ? _jsx4("code", { children: draft.anchor.component.file }) : null, _jsx4("button", { className: "cs-icon-button", type: "button", "aria-label": "Discard", onClick: () => setDraft(null), children: _jsx4(Close, {}) })] }), _jsxs4("div", { className: "cs-composer-body", children: [_jsx4(Avatar, { name: memberName, size: 24 }), _jsx4(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) => {
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
- } })] }), _jsx4(AttachmentTray, { items: draftFiles.items, onRemove: draftFiles.remove }), error ? _jsx4("p", { className: "cs-error cs-pad", children: error }) : null, _jsxs4("div", { className: "cs-composer-foot", children: [_jsx4(AttachButtons, { onFiles: draftFiles.add, capturing, onSnapshot: () => snapshot(draft.element, draftFiles.add) }), _jsx4("span", { className: "cs-hint", children: capturing ? "Capturing\u2026" : draftFiles.uploading ? "Uploading\u2026" : busy ? "Posting\u2026" : "\u2318\u21B5 to post" }), _jsx4("button", { className: "cs-send", "data-cs-ui": "composer-submit", type: "submit", "aria-label": "Post comment", disabled: busy || draftFiles.uploading || !body.trim() && !draftFiles.ids.length, children: _jsx4(Send, {}) })] })] }) : null, surface === "unlock" && !member ? unlockCard : null, surface === "menu" && member ? menuCard : null, surface === "inbox" && member ? inbox : null, toast ? _jsx4("div", { className: "cs-toast", role: "status", children: toast }) : null, bar] });
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<Thread[]>;
101
- export declare function createThread(session: Session, memberToken: string, anchor: Anchor, body: string, extras?: PostExtras): Promise<{
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
- }): Promise<{
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.5.3",
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": {