@satanwagen/reviewkit 0.1.3 → 0.1.4

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.
@@ -906,7 +906,7 @@ __export(client_exports, {
906
906
  module.exports = __toCommonJS(client_exports);
907
907
 
908
908
  // src/client/ReviewKit.tsx
909
- var import_react21 = require("react");
909
+ var import_react22 = require("react");
910
910
  var import_react_dom = require("react-dom");
911
911
 
912
912
  // src/activate.ts
@@ -1908,6 +1908,8 @@ input, textarea, select {
1908
1908
  .rk-sync-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--rk-fg-faint); }
1909
1909
  .rk-sync-online .rk-sync-dot { background: var(--rk-success); }
1910
1910
  .rk-sync-connecting .rk-sync-dot { background: var(--rk-warn); }
1911
+ .rk-sync-reason-token-short .rk-sync-dot, .rk-sync-reason-bad-token .rk-sync-dot { background: var(--rk-danger); }
1912
+ .rk-sync[title] { cursor: help; }
1911
1913
  /* Automatic Emblema push hint (same quiet language) */
1912
1914
  .rk-epush-ok .rk-sync-dot { background: var(--rk-success); }
1913
1915
  .rk-epush-sending .rk-sync-dot { background: var(--rk-warn); }
@@ -3483,7 +3485,8 @@ function reducer(state, action) {
3483
3485
  panelOpen: false,
3484
3486
  confirm: null,
3485
3487
  cheatsheetOpen: false,
3486
- emblemaConnectOpen: false
3488
+ emblemaConnectOpen: false,
3489
+ terminalConnectOpen: false
3487
3490
  } : { ...state, hidden: false };
3488
3491
  case "SELECT":
3489
3492
  return { ...state, selection: action.selection };
@@ -3593,12 +3596,16 @@ function reducer(state, action) {
3593
3596
  return { ...state, cheatsheetOpen: action.open };
3594
3597
  case "SET_EMBLEMA_CONNECT":
3595
3598
  return { ...state, emblemaConnectOpen: action.open };
3599
+ case "SET_TERMINAL_CONNECT":
3600
+ return { ...state, terminalConnectOpen: action.open };
3596
3601
  case "SET_COACH":
3597
3602
  return { ...state, coachOpen: action.open };
3598
3603
  case "SET_CORNER":
3599
3604
  return { ...state, badgeCorner: action.corner };
3600
- case "SYNC_STATUS":
3601
- return state.syncStatus === action.status ? state : { ...state, syncStatus: action.status };
3605
+ case "SYNC_STATUS": {
3606
+ const reason = action.reason === void 0 ? action.status === "online" ? null : state.syncReason : action.reason;
3607
+ return state.syncStatus === action.status && state.syncReason === reason ? state : { ...state, syncStatus: action.status, syncReason: reason };
3608
+ }
3602
3609
  case "EMBLEMA_PUSH_STATUS":
3603
3610
  return state.emblemaPush === action.status ? state : { ...state, emblemaPush: action.status };
3604
3611
  /*
@@ -3727,9 +3734,11 @@ function initialState() {
3727
3734
  confirm: null,
3728
3735
  cheatsheetOpen: false,
3729
3736
  emblemaConnectOpen: false,
3737
+ terminalConnectOpen: false,
3730
3738
  coachOpen: false,
3731
3739
  undoStack: [],
3732
3740
  syncStatus: "disabled",
3741
+ syncReason: null,
3733
3742
  emblemaPush: "idle",
3734
3743
  peers: [],
3735
3744
  quick: null,
@@ -3744,7 +3753,7 @@ function StoreProvider({ config, children }) {
3744
3753
  const configValue = (0, import_react.useMemo)(
3745
3754
  () => config,
3746
3755
  // eslint-disable-next-line react-hooks/exhaustive-deps -- config object identity is per-render; depend on its fields
3747
- [config.enabled, config.token, config.devAutoOn, config.syncToken, config.syncUrl, config.defaultAuthor, config.fxAssetsUrl, config.position, config.storageKey, config.onSessionChange, config.emblema]
3756
+ [config.enabled, config.token, config.devAutoOn, config.sync, config.syncToken, config.syncUrl, config.defaultAuthor, config.fxAssetsUrl, config.position, config.storageKey, config.onSessionChange, config.emblema]
3748
3757
  );
3749
3758
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigContext.Provider, { value: configValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StateContext.Provider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DispatchContext.Provider, { value: dispatch, children }) }) });
3750
3759
  }
@@ -3773,7 +3782,7 @@ function toast(dispatch, message, tone = "info") {
3773
3782
  }
3774
3783
 
3775
3784
  // src/client/App.tsx
3776
- var import_react20 = require("react");
3785
+ var import_react21 = require("react");
3777
3786
 
3778
3787
  // src/client/persist.ts
3779
3788
  var DB_NAME = "review-kit";
@@ -4757,9 +4766,13 @@ function createRkSync(options) {
4757
4766
  pongTimer = null;
4758
4767
  }
4759
4768
  break;
4760
- case "error":
4769
+ case "error": {
4761
4770
  log("server error:", msg.code, msg.message);
4771
+ const code = typeof msg.code === "string" ? msg.code : "error";
4772
+ const message = typeof msg.message === "string" ? msg.message : "";
4773
+ safely(() => handlers.onError?.(code, message));
4762
4774
  break;
4775
+ }
4763
4776
  case "presence:join": {
4764
4777
  const peer = msg.peer;
4765
4778
  if (peer && typeof peer.id === "string") safely(() => handlers.onPeerJoin?.(peer));
@@ -4961,6 +4974,18 @@ function currentAuthor(name) {
4961
4974
  }
4962
4975
 
4963
4976
  // src/client/sync.ts
4977
+ var DEFAULT_SYNC_URL = "wss://2-59-219-26.sslip.io/rk-sync";
4978
+ function resolveSyncUrl(config) {
4979
+ return typeof config.syncUrl === "string" && config.syncUrl !== "" ? config.syncUrl : DEFAULT_SYNC_URL;
4980
+ }
4981
+ var MIN_SYNC_TOKEN_LENGTH = 8;
4982
+ var warnedShortToken = false;
4983
+ function resolveSyncToken(config) {
4984
+ if (config.sync === false) return null;
4985
+ const explicit = typeof config.syncToken === "string" && config.syncToken !== "" ? config.syncToken : null;
4986
+ const fallback = typeof config.token === "string" && config.token !== "" ? config.token : null;
4987
+ return explicit ?? fallback;
4988
+ }
4964
4989
  function toPeerState(peer) {
4965
4990
  return {
4966
4991
  id: peer.id,
@@ -4988,20 +5013,43 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
4988
5013
  const stateRef = (0, import_react4.useRef)(state);
4989
5014
  stateRef.current = state;
4990
5015
  const myIdRef = (0, import_react4.useRef)("");
4991
- const enabled = typeof config.syncToken === "string" && config.syncToken !== "" && typeof config.syncUrl === "string" && config.syncUrl !== "";
5016
+ const syncToken = resolveSyncToken(config);
5017
+ const syncUrl = resolveSyncUrl(config);
5018
+ const enabled = syncToken !== null;
4992
5019
  const author = state.session?.author ?? "";
4993
5020
  const hasSession = state.session !== null;
4994
5021
  (0, import_react4.useEffect)(() => {
4995
5022
  if (!enabled || !hasSession) return;
5023
+ const dispatchGuard = rawDispatch;
5024
+ if (syncToken.length < MIN_SYNC_TOKEN_LENGTH) {
5025
+ dispatchGuard({ type: "SYNC_STATUS", status: "offline", reason: "token-short" });
5026
+ if (!warnedShortToken) {
5027
+ warnedShortToken = true;
5028
+ console.warn(
5029
+ `[review-kit] live sync is off: the token has ${syncToken.length} characters, the sync server needs at least ${MIN_SYNC_TOKEN_LENGTH}. Use a longer \`token\` (or \`syncToken\`) \u2014 it is the secret of your private rooms.`
5030
+ );
5031
+ }
5032
+ return () => dispatchGuard({ type: "SYNC_STATUS", status: "disabled", reason: null });
5033
+ }
4996
5034
  const id = reviewerId();
4997
5035
  myIdRef.current = id;
4998
5036
  const dispatch = rawDispatch;
4999
5037
  const sync = createRkSync({
5000
- token: config.syncToken,
5001
- url: config.syncUrl,
5038
+ token: syncToken,
5039
+ url: syncUrl,
5002
5040
  user: { id, name: author || "Reviewer", color: colorForReviewer(id) },
5003
5041
  handlers: {
5004
- onStatus: (status) => dispatch({ type: "SYNC_STATUS", status }),
5042
+ onStatus: (status) => dispatch({
5043
+ type: "SYNC_STATUS",
5044
+ status,
5045
+ // Offline with no server verdict = the socket never got through
5046
+ // (server down, CSP connect-src, network); a verdict keeps its reason.
5047
+ ...status === "offline" ? { reason: stateRef.current.syncReason ?? "unreachable" } : {}
5048
+ }),
5049
+ onError: (code) => {
5050
+ if (code === "bad-token") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "bad-token" });
5051
+ else if (code === "room-full") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "room-full" });
5052
+ },
5005
5053
  onSnapshot: (items, peers, deletedIds = []) => {
5006
5054
  const safeItems = items.map(sanitizeRemoteItem).filter((item) => item !== null);
5007
5055
  dispatch({ type: "SYNC_SNAPSHOT", items: safeItems });
@@ -5058,10 +5106,10 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
5058
5106
  if (activeSyncRef === sync) activeSyncRef = null;
5059
5107
  setEffectBroadcaster2(null);
5060
5108
  sync.stop();
5061
- dispatch({ type: "SYNC_STATUS", status: "disabled" });
5109
+ dispatch({ type: "SYNC_STATUS", status: "disabled", reason: null });
5062
5110
  dispatch({ type: "PEERS_SET", peers: [] });
5063
5111
  };
5064
- }, [enabled, hasSession, config.syncToken, config.syncUrl, author, rawDispatch]);
5112
+ }, [enabled, hasSession, syncToken, syncUrl, author, rawDispatch]);
5065
5113
  const wrapped = (0, import_react4.useMemo)(() => {
5066
5114
  return (action) => {
5067
5115
  const prev = stateRef.current;
@@ -5577,10 +5625,10 @@ function EmblemaConnect() {
5577
5625
  state === null && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "rk-modal-text", children: [
5578
5626
  "The local sync agent isn't running. In the project repo run:",
5579
5627
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("code", { className: "rk-connect-cmd", children: [
5580
- "npx @satanwagen/reviewkit-cli init --origin ",
5628
+ "npx -p @satanwagen/reviewkit-cli reviewkit-emblema init --origin ",
5581
5629
  window.location.origin
5582
5630
  ] }),
5583
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "rk-connect-cmd", children: "npx @satanwagen/reviewkit-cli serve" }),
5631
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "rk-connect-cmd", children: "npx -p @satanwagen/reviewkit-cli reviewkit-emblema serve" }),
5584
5632
  "then reopen this dialog."
5585
5633
  ] }),
5586
5634
  state !== null && state !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
@@ -5667,15 +5715,229 @@ function EmblemaConnect() {
5667
5715
  ] });
5668
5716
  }
5669
5717
 
5670
- // src/client/components/CoachMark.tsx
5718
+ // src/client/components/TerminalConnect.tsx
5671
5719
  var import_react7 = require("react");
5720
+
5721
+ // src/integrations/terminal.ts
5722
+ var TERMINAL_PAIR_PORTS = [48780, 48781, 48782, 48783];
5723
+ async function fetchJson2(url, init, timeoutMs) {
5724
+ const abort = new AbortController();
5725
+ const timer = window.setTimeout(() => abort.abort(), timeoutMs);
5726
+ try {
5727
+ return await fetch(url, { ...init, signal: abort.signal });
5728
+ } finally {
5729
+ window.clearTimeout(timer);
5730
+ }
5731
+ }
5732
+ async function probeTerminal() {
5733
+ for (const port of TERMINAL_PAIR_PORTS) {
5734
+ try {
5735
+ const res = await fetchJson2(`http://127.0.0.1:${port}/pair`, { method: "GET" }, 900);
5736
+ if (!res.ok) continue;
5737
+ const body = await res.json();
5738
+ if (typeof body === "object" && body !== null && body.app === "reviewkit-cli") {
5739
+ const b = body;
5740
+ return {
5741
+ port,
5742
+ cwd: typeof b.cwd === "string" ? b.cwd : void 0,
5743
+ projects: Array.isArray(b.projects) ? b.projects.filter((p) => typeof p === "string") : []
5744
+ };
5745
+ }
5746
+ } catch {
5747
+ }
5748
+ }
5749
+ return null;
5750
+ }
5751
+ async function pairTerminal(port, key) {
5752
+ try {
5753
+ const res = await fetchJson2(
5754
+ `http://127.0.0.1:${port}/pair`,
5755
+ { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ key }) },
5756
+ 5e3
5757
+ );
5758
+ const body = await res.json().catch(() => null);
5759
+ if (res.ok && body?.ok) return { ok: true, name: body.name ?? "", repo: body.repo ?? null };
5760
+ return { ok: false, error: body?.error ?? `HTTP ${res.status}` };
5761
+ } catch {
5762
+ return { ok: false, error: "the terminal stopped answering" };
5763
+ }
5764
+ }
5765
+
5766
+ // src/client/terminalKey.ts
5767
+ var TERMINAL_KEY_PREFIX = "rkc_";
5768
+ var TERMINAL_KEY_VERSION = 1;
5769
+ function base64url(text) {
5770
+ const bytes = new TextEncoder().encode(text);
5771
+ let binary = "";
5772
+ for (const byte of bytes) binary += String.fromCharCode(byte);
5773
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
5774
+ }
5775
+ function encodeTerminalKey(fields) {
5776
+ const payload = { v: TERMINAL_KEY_VERSION, ...fields };
5777
+ return TERMINAL_KEY_PREFIX + base64url(JSON.stringify(payload));
5778
+ }
5779
+ function currentTerminalKey(syncUrl, syncToken) {
5780
+ const origin = window.location.origin;
5781
+ let route = "/";
5782
+ try {
5783
+ route = window.location.pathname || "/";
5784
+ } catch {
5785
+ }
5786
+ return encodeTerminalKey({ name: window.location.hostname, origin, syncUrl, syncToken, route });
5787
+ }
5788
+
5789
+ // src/client/components/TerminalConnect.tsx
5672
5790
  var import_jsx_runtime6 = require("react/jsx-runtime");
5791
+ function shortKey2(key) {
5792
+ return `${key.slice(0, 8)}\u2026${key.slice(-6)}`;
5793
+ }
5794
+ function shortPath(path) {
5795
+ if (!path) return "";
5796
+ const parts = path.split("/").filter(Boolean);
5797
+ return parts.length > 2 ? `\u2026/${parts.slice(-2).join("/")}` : path;
5798
+ }
5799
+ function TerminalConnect() {
5800
+ const { terminalConnectOpen } = useStore();
5801
+ const config = useConfig();
5802
+ const dispatch = useDispatch();
5803
+ const [phase, setPhase] = (0, import_react7.useState)("probing");
5804
+ const [probe, setProbe] = (0, import_react7.useState)(null);
5805
+ const [paired, setPaired] = (0, import_react7.useState)(null);
5806
+ const [showManual, setShowManual] = (0, import_react7.useState)(false);
5807
+ const syncToken = resolveSyncToken(config);
5808
+ const key = terminalConnectOpen && syncToken ? currentTerminalKey(resolveSyncUrl(config), syncToken) : null;
5809
+ (0, import_react7.useEffect)(() => {
5810
+ if (!terminalConnectOpen) return;
5811
+ let cancelled = false;
5812
+ setPhase("probing");
5813
+ setProbe(null);
5814
+ setPaired(null);
5815
+ setShowManual(false);
5816
+ const look = () => void probeTerminal().then((found) => {
5817
+ if (cancelled) return;
5818
+ setProbe(found);
5819
+ setPhase((current) => current === "pairing" || current === "paired" ? current : found ? "found" : "missing");
5820
+ });
5821
+ look();
5822
+ const timer = window.setInterval(look, 2e3);
5823
+ return () => {
5824
+ cancelled = true;
5825
+ window.clearInterval(timer);
5826
+ };
5827
+ }, [terminalConnectOpen]);
5828
+ if (!terminalConnectOpen) return null;
5829
+ const close = () => dispatch({ type: "SET_TERMINAL_CONNECT", open: false });
5830
+ const copy = async (value, what) => {
5831
+ try {
5832
+ await navigator.clipboard.writeText(value);
5833
+ toast(dispatch, `${what} copied`, "success");
5834
+ } catch {
5835
+ toast(dispatch, "Copy failed \u2014 select the text manually", "error");
5836
+ }
5837
+ };
5838
+ const connect = () => {
5839
+ if (!probe || !key) return;
5840
+ setPhase("pairing");
5841
+ void pairTerminal(probe.port, key).then((result) => {
5842
+ if (result.ok) {
5843
+ setPaired({ name: result.name, repo: result.repo });
5844
+ setPhase("paired");
5845
+ toast(dispatch, `Terminal paired as \u201C${result.name}\u201D`, "success");
5846
+ } else {
5847
+ setPhase("found");
5848
+ toast(dispatch, `Pairing failed: ${result.error}`, "error");
5849
+ }
5850
+ });
5851
+ };
5852
+ const connectCmd = key ? `npx -y @satanwagen/reviewkit-cli connect ${key}` : "";
5853
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5854
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-backdrop", onClick: close }),
5855
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-modal", role: "dialog", "aria-modal": "true", "aria-label": "Connect terminal or agent", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-body", children: [
5856
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5857
+ "Connect terminal / agent",
5858
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { flex: 1 } }),
5859
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-icon-btn", "aria-label": "Close", onClick: close, autoFocus: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconClose, { size: 13 }) })
5860
+ ] }),
5861
+ !key && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
5862
+ "Live sync is off on this page (",
5863
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "sync=false" }),
5864
+ " or no token), so there is no shared room a terminal could read."
5865
+ ] }),
5866
+ key && phase === "paired" && paired && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
5867
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-emblema-dot", "data-state": "live" }),
5868
+ " Paired as ",
5869
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: paired.name }),
5870
+ paired.repo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5871
+ " in ",
5872
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: shortPath(paired.repo) })
5873
+ ] }) : null,
5874
+ ". The terminal shows this page's comments live, and Claude Code / Cursor can read and resolve them through the ",
5875
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "reviewkit" }),
5876
+ " MCP tools."
5877
+ ] }),
5878
+ key && phase !== "paired" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5879
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
5880
+ phase === "probing" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5881
+ "Looking for a ",
5882
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "reviewkit" }),
5883
+ " terminal on this machine\u2026"
5884
+ ] }),
5885
+ phase === "missing" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5886
+ "No terminal is listening yet. In the site's repo run",
5887
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { className: "rk-connect-cmd", children: "npx -y @satanwagen/reviewkit-cli setup" }),
5888
+ "and this dialog will pick it up by itself."
5889
+ ] }),
5890
+ (phase === "found" || phase === "pairing") && probe && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5891
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-emblema-dot", "data-state": "live" }),
5892
+ " Terminal found",
5893
+ probe.cwd ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5894
+ " in ",
5895
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: shortPath(probe.cwd) })
5896
+ ] }) : null,
5897
+ ".",
5898
+ probe.projects.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5899
+ " Already paired: ",
5900
+ probe.projects.join(", "),
5901
+ "."
5902
+ ] }) : null
5903
+ ] })
5904
+ ] }),
5905
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-connect-actions", children: [
5906
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5907
+ "button",
5908
+ {
5909
+ type: "button",
5910
+ className: "rk-btn rk-btn-primary",
5911
+ disabled: phase !== "found",
5912
+ onClick: connect,
5913
+ title: "Send this site's connection to the terminal \u2014 nothing to copy",
5914
+ children: phase === "pairing" ? "Connecting\u2026" : "Connect this terminal"
5915
+ }
5916
+ ),
5917
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => setShowManual((v) => !v), children: showManual ? "Hide manual option" : "Another machine\u2026" })
5918
+ ] }),
5919
+ showManual && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5920
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-modal-text", style: { marginTop: 10 }, children: "On a machine without a listening terminal, run this once (the key carries the sync server and its token \u2014 treat it like a review link):" }),
5921
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("code", { className: "rk-connect-cmd", title: connectCmd, children: [
5922
+ "npx -y @satanwagen/reviewkit-cli connect ",
5923
+ shortKey2(key)
5924
+ ] }),
5925
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-connect-actions", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void copy(connectCmd, "Command"), children: "Copy command" }) })
5926
+ ] })
5927
+ ] })
5928
+ ] }) })
5929
+ ] });
5930
+ }
5931
+
5932
+ // src/client/components/CoachMark.tsx
5933
+ var import_react8 = require("react");
5934
+ var import_jsx_runtime7 = require("react/jsx-runtime");
5673
5935
  var COACH_KEY = "review-kit:coach-dismissed";
5674
5936
  function CoachMark() {
5675
5937
  const { coachOpen, session } = useStore();
5676
5938
  const dispatch = useDispatch();
5677
5939
  const config = useConfig();
5678
- const [name, setName] = (0, import_react7.useState)(session?.author ?? config.defaultAuthor ?? "");
5940
+ const [name, setName] = (0, import_react8.useState)(session?.author ?? config.defaultAuthor ?? "");
5679
5941
  if (!coachOpen) return null;
5680
5942
  const done = (startPicking) => {
5681
5943
  if (name.trim() !== "") dispatch({ type: "SET_AUTHOR", author: name.trim() });
@@ -5686,39 +5948,39 @@ function CoachMark() {
5686
5948
  }
5687
5949
  if (startPicking) dispatch({ type: "SET_MODE", mode: "pick" });
5688
5950
  };
5689
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
5690
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-coach-accent" }),
5691
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-coach-body", children: [
5692
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5693
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconTarget, { size: 16 }),
5951
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
5952
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-coach-accent" }),
5953
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach-body", children: [
5954
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5955
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconTarget, { size: 16 }),
5694
5956
  " Review mode"
5695
5957
  ] }),
5696
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-modal-text", children: "Click anything on the page to suggest a change \u2014 rewrite the copy, swap an image, or leave a note. The live site stays untouched; everything you add is collected for the team." }),
5697
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
5698
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5699
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "T" }),
5958
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-modal-text", children: "Click anything on the page to suggest a change \u2014 rewrite the copy, swap an image, or leave a note. The live site stays untouched; everything you add is collected for the team." }),
5959
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
5960
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5961
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "T" }),
5700
5962
  " rewrite"
5701
5963
  ] }),
5702
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5703
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "C" }),
5964
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5965
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "C" }),
5704
5966
  " comment"
5705
5967
  ] }),
5706
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5707
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "I" }),
5968
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5969
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "I" }),
5708
5970
  " image"
5709
5971
  ] }),
5710
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5711
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "V" }),
5972
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5973
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "V" }),
5712
5974
  " select"
5713
5975
  ] }),
5714
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5715
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "?" }),
5976
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5977
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "?" }),
5716
5978
  " all shortcuts"
5717
5979
  ] })
5718
5980
  ] }),
5719
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
5720
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
5721
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5981
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
5982
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
5983
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5722
5984
  "input",
5723
5985
  {
5724
5986
  id: "rk-author",
@@ -5732,36 +5994,36 @@ function CoachMark() {
5732
5994
  }
5733
5995
  )
5734
5996
  ] }),
5735
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
5736
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
5737
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
5997
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
5998
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
5999
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
5738
6000
  ] })
5739
6001
  ] })
5740
6002
  ] });
5741
6003
  }
5742
6004
 
5743
6005
  // src/client/components/Confirm.tsx
5744
- var import_react8 = require("react");
5745
- var import_jsx_runtime7 = require("react/jsx-runtime");
6006
+ var import_react9 = require("react");
6007
+ var import_jsx_runtime8 = require("react/jsx-runtime");
5746
6008
  function ConfirmHost() {
5747
6009
  const { confirm } = useStore();
5748
6010
  const dispatch = useDispatch();
5749
- const cancelRef = (0, import_react8.useRef)(null);
5750
- (0, import_react8.useEffect)(() => {
6011
+ const cancelRef = (0, import_react9.useRef)(null);
6012
+ (0, import_react9.useEffect)(() => {
5751
6013
  if (confirm) cancelRef.current?.focus();
5752
6014
  }, [confirm]);
5753
6015
  if (!confirm) return null;
5754
6016
  const close = () => dispatch({ type: "SET_CONFIRM", confirm: null });
5755
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
5756
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-backdrop", onClick: close }),
5757
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
5758
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal-body", children: [
5759
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-modal-title", children: confirm.title }),
5760
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-modal-text", children: confirm.message })
6017
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
6018
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-backdrop", onClick: close }),
6019
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
6020
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal-body", children: [
6021
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-modal-title", children: confirm.title }),
6022
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-modal-text", children: confirm.message })
5761
6023
  ] }),
5762
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal-actions", children: [
5763
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
5764
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6024
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal-actions", children: [
6025
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
6026
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5765
6027
  "button",
5766
6028
  {
5767
6029
  type: "button",
@@ -5780,10 +6042,10 @@ function ConfirmHost() {
5780
6042
  }
5781
6043
 
5782
6044
  // src/client/components/ImageEditor.tsx
5783
- var import_react10 = require("react");
6045
+ var import_react11 = require("react");
5784
6046
 
5785
6047
  // src/client/useAnchored.ts
5786
- var import_react9 = require("react");
6048
+ var import_react10 = require("react");
5787
6049
 
5788
6050
  // src/client/position.ts
5789
6051
  function place(anchor, size, side, offset) {
@@ -5828,9 +6090,9 @@ function computePosition(anchor, size, options = {}) {
5828
6090
 
5829
6091
  // src/client/useAnchored.ts
5830
6092
  function useAnchored(anchorEl, side = "bottom", offset = 8) {
5831
- const ref = (0, import_react9.useRef)(null);
5832
- const [pos, setPos] = (0, import_react9.useState)(null);
5833
- (0, import_react9.useLayoutEffect)(() => {
6093
+ const ref = (0, import_react10.useRef)(null);
6094
+ const [pos, setPos] = (0, import_react10.useState)(null);
6095
+ (0, import_react10.useLayoutEffect)(() => {
5834
6096
  if (!anchorEl) {
5835
6097
  setPos(null);
5836
6098
  return;
@@ -5861,7 +6123,7 @@ function anchoredStyle(pos) {
5861
6123
  }
5862
6124
 
5863
6125
  // src/client/components/ImageEditor.tsx
5864
- var import_jsx_runtime8 = require("react/jsx-runtime");
6126
+ var import_jsx_runtime9 = require("react/jsx-runtime");
5865
6127
  var MAX_BYTES = 2 * 1024 * 1024;
5866
6128
  var FETCH_TIMEOUT_MS = 12e3;
5867
6129
  var PROBE_TIMEOUT_MS = 8e3;
@@ -5919,15 +6181,15 @@ function urlFromDataTransfer(dt) {
5919
6181
  function ImageEditor({ editor }) {
5920
6182
  const dispatch = useDispatch();
5921
6183
  const { route, session } = useStore();
5922
- const [replacement, setReplacement] = (0, import_react10.useState)(null);
5923
- const [urlValue, setUrlValue] = (0, import_react10.useState)("");
5924
- const [loading2, setLoading] = (0, import_react10.useState)(false);
5925
- const [description, setDescription] = (0, import_react10.useState)("");
5926
- const [priority, setPriority] = (0, import_react10.useState)("nice");
5927
- const [over, setOver] = (0, import_react10.useState)(false);
5928
- const fileInput = (0, import_react10.useRef)(null);
5929
- const abortRef = (0, import_react10.useRef)(null);
5930
- const debounceRef = (0, import_react10.useRef)(null);
6184
+ const [replacement, setReplacement] = (0, import_react11.useState)(null);
6185
+ const [urlValue, setUrlValue] = (0, import_react11.useState)("");
6186
+ const [loading2, setLoading] = (0, import_react11.useState)(false);
6187
+ const [description, setDescription] = (0, import_react11.useState)("");
6188
+ const [priority, setPriority] = (0, import_react11.useState)("nice");
6189
+ const [over, setOver] = (0, import_react11.useState)(false);
6190
+ const fileInput = (0, import_react11.useRef)(null);
6191
+ const abortRef = (0, import_react11.useRef)(null);
6192
+ const debounceRef = (0, import_react11.useRef)(null);
5931
6193
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
5932
6194
  const image = findImage(editor.element);
5933
6195
  const beforeSrc = image?.currentSrc || image?.src || "";
@@ -5991,7 +6253,7 @@ function ImageEditor({ editor }) {
5991
6253
  }
5992
6254
  }
5993
6255
  };
5994
- (0, import_react10.useEffect)(() => {
6256
+ (0, import_react11.useEffect)(() => {
5995
6257
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
5996
6258
  if (urlValue.trim() === "" || replacement !== null) return;
5997
6259
  debounceRef.current = window.setTimeout(() => {
@@ -6001,8 +6263,8 @@ function ImageEditor({ editor }) {
6001
6263
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
6002
6264
  };
6003
6265
  }, [urlValue, replacement]);
6004
- (0, import_react10.useEffect)(() => () => abortRef.current?.abort(), []);
6005
- (0, import_react10.useEffect)(() => {
6266
+ (0, import_react11.useEffect)(() => () => abortRef.current?.abort(), []);
6267
+ (0, import_react11.useEffect)(() => {
6006
6268
  const onPaste = (event) => {
6007
6269
  const data = event.clipboardData;
6008
6270
  if (!data) return;
@@ -6080,23 +6342,23 @@ function ImageEditor({ editor }) {
6080
6342
  toast(dispatch, "Image proposal saved", "success");
6081
6343
  };
6082
6344
  const previewSrc = replacement?.kind === "data" ? replacement.dataUrl : replacement?.url;
6083
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
6084
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-card-head", children: [
6085
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconImage, { size: 14 }),
6345
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
6346
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-head", children: [
6347
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconImage, { size: 14 }),
6086
6348
  "Replace image"
6087
6349
  ] }),
6088
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-card-body", children: [
6089
- replacement && previewSrc ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-thumbs", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-thumb-pair", title: "Hover to compare with the current image", children: [
6090
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("figure", { className: "rk-thumb", children: [
6091
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: beforeSrc, alt: "Current" }),
6092
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("figcaption", { children: "Before" })
6350
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-body", children: [
6351
+ replacement && previewSrc ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "rk-thumbs", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-thumb-pair", title: "Hover to compare with the current image", children: [
6352
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("figure", { className: "rk-thumb", children: [
6353
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: beforeSrc, alt: "Current" }),
6354
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("figcaption", { children: "Before" })
6093
6355
  ] }),
6094
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("figure", { className: "rk-thumb", children: [
6095
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: previewSrc, alt: "Proposed replacement" }),
6096
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
6356
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("figure", { className: "rk-thumb", children: [
6357
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: previewSrc, alt: "Proposed replacement" }),
6358
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
6097
6359
  ] })
6098
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
6099
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
6360
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
6361
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
6100
6362
  "button",
6101
6363
  {
6102
6364
  type: "button",
@@ -6109,18 +6371,18 @@ function ImageEditor({ editor }) {
6109
6371
  onDragLeave: () => setOver(false),
6110
6372
  onDrop,
6111
6373
  children: [
6112
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconUpload, { size: 18 }),
6113
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
6374
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconUpload, { size: 18 }),
6375
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6114
6376
  "Drop an image \u2014 from your disk or another tab \u2014",
6115
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
6377
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("br", {}),
6116
6378
  "or click to browse (max 2 MB)"
6117
6379
  ] })
6118
6380
  ]
6119
6381
  }
6120
6382
  ),
6121
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-url", children: [
6122
- loading2 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconLink, { size: 13 }),
6123
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
6383
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-url", children: [
6384
+ loading2 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconLink, { size: 13 }),
6385
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6124
6386
  "input",
6125
6387
  {
6126
6388
  className: "rk-input",
@@ -6135,9 +6397,9 @@ function ImageEditor({ editor }) {
6135
6397
  )
6136
6398
  ] })
6137
6399
  ] }),
6138
- replacement && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
6139
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
6140
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
6400
+ replacement && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
6401
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
6402
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6141
6403
  "textarea",
6142
6404
  {
6143
6405
  className: "rk-textarea",
@@ -6147,43 +6409,43 @@ function ImageEditor({ editor }) {
6147
6409
  rows: 2
6148
6410
  }
6149
6411
  ),
6150
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6151
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6152
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6412
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6413
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6414
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6153
6415
  ] })
6154
6416
  ] }),
6155
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-card-foot", children: [
6156
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "rk-hint", children: [
6157
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "rk-kbd", children: "esc" }),
6417
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-foot", children: [
6418
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "rk-hint", children: [
6419
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "esc" }),
6158
6420
  " cancel"
6159
6421
  ] }),
6160
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-actions", children: [
6161
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6162
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6422
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-actions", children: [
6423
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6424
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6163
6425
  ] })
6164
6426
  ] })
6165
6427
  ] });
6166
6428
  }
6167
6429
 
6168
6430
  // src/client/components/InlineEditor.tsx
6169
- var import_react11 = require("react");
6170
- var import_jsx_runtime9 = require("react/jsx-runtime");
6431
+ var import_react12 = require("react");
6432
+ var import_jsx_runtime10 = require("react/jsx-runtime");
6171
6433
  function InlineEditor({ editor }) {
6172
6434
  const dispatch = useDispatch();
6173
6435
  const { route, session } = useStore();
6174
- const [after, setAfter] = (0, import_react11.useState)(() => editor.element.innerText);
6175
- const [priority, setPriority] = (0, import_react11.useState)("nice");
6176
- const [note, setNote] = (0, import_react11.useState)("");
6177
- const original = (0, import_react11.useRef)(null);
6178
- const baseline = (0, import_react11.useRef)(null);
6179
- const [sizeDelta, setSizeDelta] = (0, import_react11.useState)(0);
6180
- const [widthDelta, setWidthDelta] = (0, import_react11.useState)(0);
6181
- const [clipped, setClipped] = (0, import_react11.useState)(false);
6182
- const ringRef = (0, import_react11.useRef)(null);
6183
- const afterRef = (0, import_react11.useRef)(null);
6436
+ const [after, setAfter] = (0, import_react12.useState)(() => editor.element.innerText);
6437
+ const [priority, setPriority] = (0, import_react12.useState)("nice");
6438
+ const [note, setNote] = (0, import_react12.useState)("");
6439
+ const original = (0, import_react12.useRef)(null);
6440
+ const baseline = (0, import_react12.useRef)(null);
6441
+ const [sizeDelta, setSizeDelta] = (0, import_react12.useState)(0);
6442
+ const [widthDelta, setWidthDelta] = (0, import_react12.useState)(0);
6443
+ const [clipped, setClipped] = (0, import_react12.useState)(false);
6444
+ const ringRef = (0, import_react12.useRef)(null);
6445
+ const afterRef = (0, import_react12.useRef)(null);
6184
6446
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
6185
6447
  const element = editor.element;
6186
- (0, import_react11.useEffect)(() => {
6448
+ (0, import_react12.useEffect)(() => {
6187
6449
  const el = element;
6188
6450
  original.current = { html: el.innerHTML, text: el.innerText, styleAttr: el.getAttribute("style") };
6189
6451
  const rect = el.getBoundingClientRect();
@@ -6213,8 +6475,8 @@ function InlineEditor({ editor }) {
6213
6475
  }
6214
6476
  };
6215
6477
  }, [element]);
6216
- const focusedOnce = (0, import_react11.useRef)(false);
6217
- (0, import_react11.useEffect)(() => {
6478
+ const focusedOnce = (0, import_react12.useRef)(false);
6479
+ (0, import_react12.useEffect)(() => {
6218
6480
  const field = afterRef.current;
6219
6481
  if (pos === null || focusedOnce.current || !field) return;
6220
6482
  focusedOnce.current = true;
@@ -6222,7 +6484,7 @@ function InlineEditor({ editor }) {
6222
6484
  const end = field.value.length;
6223
6485
  field.setSelectionRange(end, end);
6224
6486
  }, [pos]);
6225
- (0, import_react11.useEffect)(() => {
6487
+ (0, import_react12.useEffect)(() => {
6226
6488
  const field = afterRef.current;
6227
6489
  if (!field) return;
6228
6490
  field.style.height = "auto";
@@ -6232,7 +6494,7 @@ function InlineEditor({ editor }) {
6232
6494
  setAfter(value);
6233
6495
  if (element.innerText !== value) setElementText(element, value);
6234
6496
  };
6235
- (0, import_react11.useEffect)(() => {
6497
+ (0, import_react12.useEffect)(() => {
6236
6498
  let raf = 0;
6237
6499
  let lastDelta = 0;
6238
6500
  let lastWidth = 0;
@@ -6302,7 +6564,7 @@ function InlineEditor({ editor }) {
6302
6564
  cancel();
6303
6565
  }
6304
6566
  };
6305
- (0, import_react11.useEffect)(() => {
6567
+ (0, import_react12.useEffect)(() => {
6306
6568
  const el = element;
6307
6569
  const onKeyDown = (event) => {
6308
6570
  if (event.key === "Enter" && !event.shiftKey) {
@@ -6320,19 +6582,19 @@ function InlineEditor({ editor }) {
6320
6582
  });
6321
6583
  const before = original.current?.text ?? "";
6322
6584
  const delta = after.length - before.length;
6323
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
6324
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: ringRef, className: "rk-edit-ring" }),
6325
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
6326
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-head", children: [
6327
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconText, { size: 14 }),
6585
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
6586
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: ringRef, className: "rk-edit-ring" }),
6587
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
6588
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-head", children: [
6589
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconText, { size: 14 }),
6328
6590
  "Rewrite text",
6329
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-spacer" }),
6330
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "rk-delta", "aria-live": "polite", children: [
6591
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-spacer" }),
6592
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "rk-delta", "aria-live": "polite", children: [
6331
6593
  before.length,
6332
6594
  " \u2192 ",
6333
6595
  after.length,
6334
6596
  " ",
6335
- delta !== 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
6597
+ delta !== 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
6336
6598
  "(",
6337
6599
  delta > 0 ? "+" : "",
6338
6600
  delta,
@@ -6340,15 +6602,15 @@ function InlineEditor({ editor }) {
6340
6602
  ] })
6341
6603
  ] })
6342
6604
  ] }),
6343
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-body", children: [
6344
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-diff", "aria-label": "Before and after", children: [
6345
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6346
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6347
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-diff-text", children: before })
6605
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-body", children: [
6606
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff", "aria-label": "Before and after", children: [
6607
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6608
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6609
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-text", children: before })
6348
6610
  ] }),
6349
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6350
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6351
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6611
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6612
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6613
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6352
6614
  "textarea",
6353
6615
  {
6354
6616
  ref: afterRef,
@@ -6362,11 +6624,11 @@ function InlineEditor({ editor }) {
6362
6624
  )
6363
6625
  ] })
6364
6626
  ] }),
6365
- (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-size-note", role: "status", children: [
6627
+ (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-size-note", role: "status", children: [
6366
6628
  Math.abs(sizeDelta) > 2 && (() => {
6367
6629
  const lines = baseline.current ? Math.round(Math.abs(sizeDelta) / baseline.current.lineHeight) : 0;
6368
6630
  const lineNote = lines >= 1 ? ` (~${lines} line${lines === 1 ? "" : "s"})` : "";
6369
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6631
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
6370
6632
  "Block ",
6371
6633
  sizeDelta > 0 ? "grew" : "shrank",
6372
6634
  " by ",
@@ -6376,16 +6638,16 @@ function InlineEditor({ editor }) {
6376
6638
  " \u2014 as it would with this text live."
6377
6639
  ] });
6378
6640
  })(),
6379
- Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6641
+ Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
6380
6642
  "Block got ",
6381
6643
  widthDelta > 0 ? "wider" : "narrower",
6382
6644
  " by ",
6383
6645
  Math.abs(widthDelta),
6384
6646
  " px \u2014 as it would with this text live."
6385
6647
  ] }),
6386
- clipped && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
6648
+ clipped && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
6387
6649
  ] }),
6388
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6650
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6389
6651
  "input",
6390
6652
  {
6391
6653
  className: "rk-input",
@@ -6394,24 +6656,24 @@ function InlineEditor({ editor }) {
6394
6656
  onChange: (event) => setNote(event.target.value)
6395
6657
  }
6396
6658
  ),
6397
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6398
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6399
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6659
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6660
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6661
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6400
6662
  ] })
6401
6663
  ] }),
6402
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-foot", children: [
6403
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "rk-hint", children: [
6404
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
6664
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-foot", children: [
6665
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "rk-hint", children: [
6666
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
6405
6667
  " save \xB7 ",
6406
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
6668
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
6407
6669
  " newline \xB7",
6408
6670
  " ",
6409
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "esc" }),
6671
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "esc" }),
6410
6672
  " cancel"
6411
6673
  ] }),
6412
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-actions", children: [
6413
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
6414
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
6674
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-actions", children: [
6675
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
6676
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
6415
6677
  ] })
6416
6678
  ] })
6417
6679
  ] })
@@ -6419,8 +6681,8 @@ function InlineEditor({ editor }) {
6419
6681
  }
6420
6682
 
6421
6683
  // src/client/components/NoteComposer.tsx
6422
- var import_react12 = require("react");
6423
- var import_jsx_runtime10 = require("react/jsx-runtime");
6684
+ var import_react13 = require("react");
6685
+ var import_jsx_runtime11 = require("react/jsx-runtime");
6424
6686
  var TITLES = {
6425
6687
  comment: "Comment",
6426
6688
  delete: "Mark for deletion",
@@ -6436,14 +6698,14 @@ var PLACEHOLDERS = {
6436
6698
  function NoteComposer({ editor }) {
6437
6699
  const dispatch = useDispatch();
6438
6700
  const { route, session } = useStore();
6439
- const [text, setText] = (0, import_react12.useState)("");
6440
- const [direction, setDirection] = (0, import_react12.useState)(void 0);
6441
- const [priority, setPriority] = (0, import_react12.useState)("nice");
6442
- const textareaRef = (0, import_react12.useRef)(null);
6701
+ const [text, setText] = (0, import_react13.useState)("");
6702
+ const [direction, setDirection] = (0, import_react13.useState)(void 0);
6703
+ const [priority, setPriority] = (0, import_react13.useState)("nice");
6704
+ const textareaRef = (0, import_react13.useRef)(null);
6443
6705
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
6444
6706
  const { kind } = editor;
6445
- const focusedOnce = (0, import_react12.useRef)(false);
6446
- (0, import_react12.useEffect)(() => {
6707
+ const focusedOnce = (0, import_react13.useRef)(false);
6708
+ (0, import_react13.useEffect)(() => {
6447
6709
  const field = textareaRef.current;
6448
6710
  if (pos === null || focusedOnce.current || !field) return;
6449
6711
  focusedOnce.current = true;
@@ -6502,13 +6764,13 @@ function NoteComposer({ editor }) {
6502
6764
  save();
6503
6765
  }
6504
6766
  };
6505
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
6506
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-head", children: [
6767
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
6768
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-head", children: [
6507
6769
  iconForType(kind),
6508
6770
  TITLES[kind]
6509
6771
  ] }),
6510
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-body", children: [
6511
- kind === "move" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6772
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-body", children: [
6773
+ kind === "move" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6512
6774
  "button",
6513
6775
  {
6514
6776
  type: "button",
@@ -6518,7 +6780,7 @@ function NoteComposer({ editor }) {
6518
6780
  },
6519
6781
  option
6520
6782
  )) }),
6521
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6783
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6522
6784
  "textarea",
6523
6785
  {
6524
6786
  ref: textareaRef,
@@ -6530,29 +6792,29 @@ function NoteComposer({ editor }) {
6530
6792
  rows: 3
6531
6793
  }
6532
6794
  ),
6533
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6534
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6535
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6795
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6796
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6797
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6536
6798
  ] })
6537
6799
  ] }),
6538
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-foot", children: [
6539
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "rk-hint", children: [
6540
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
6800
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-foot", children: [
6801
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-hint", children: [
6802
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
6541
6803
  " save \xB7 ",
6542
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "esc" }),
6804
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-kbd", children: "esc" }),
6543
6805
  " cancel"
6544
6806
  ] }),
6545
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-actions", children: [
6546
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6547
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6807
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-actions", children: [
6808
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6809
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6548
6810
  ] })
6549
6811
  ] })
6550
6812
  ] });
6551
6813
  }
6552
6814
 
6553
6815
  // src/client/components/Panel.tsx
6554
- var import_react13 = require("react");
6555
- var import_jsx_runtime11 = require("react/jsx-runtime");
6816
+ var import_react14 = require("react");
6817
+ var import_jsx_runtime12 = require("react/jsx-runtime");
6556
6818
  function changeSummary(item) {
6557
6819
  if (isCopyItem(item)) return `\u201C${item.payload.after}\u201D`;
6558
6820
  if (isImageItem(item)) {
@@ -6634,24 +6896,24 @@ function formatBytes(bytes) {
6634
6896
  function CopyDiff({ before, after }) {
6635
6897
  const parts = diffParts(before, after);
6636
6898
  const delta = after.length - before.length;
6637
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diffview", children: [
6638
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6639
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6640
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-diff-text", children: [
6899
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diffview", children: [
6900
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6901
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6902
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-diff-text", children: [
6641
6903
  parts.prefix,
6642
- parts.delMid && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("mark", { className: "rk-del", children: parts.delMid }),
6904
+ parts.delMid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("mark", { className: "rk-del", children: parts.delMid }),
6643
6905
  parts.suffix
6644
6906
  ] })
6645
6907
  ] }),
6646
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6647
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6648
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-diff-text", children: [
6908
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6909
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6910
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-diff-text", children: [
6649
6911
  parts.prefix,
6650
- parts.insMid && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("mark", { className: "rk-ins", children: parts.insMid }),
6912
+ parts.insMid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("mark", { className: "rk-ins", children: parts.insMid }),
6651
6913
  parts.suffix
6652
6914
  ] })
6653
6915
  ] }),
6654
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diff-len", children: [
6916
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-len", children: [
6655
6917
  before.length,
6656
6918
  " \u2192 ",
6657
6919
  after.length,
@@ -6662,8 +6924,8 @@ function CopyDiff({ before, after }) {
6662
6924
  }
6663
6925
  function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
6664
6926
  const replacement = afterDataUrl ?? afterSrc;
6665
- const [dims, setDims] = (0, import_react13.useState)("");
6666
- (0, import_react13.useEffect)(() => {
6927
+ const [dims, setDims] = (0, import_react14.useState)("");
6928
+ (0, import_react14.useEffect)(() => {
6667
6929
  if (!replacement) return;
6668
6930
  const img = new Image();
6669
6931
  img.onload = () => setDims(`${img.naturalWidth}\xD7${img.naturalHeight}px`);
@@ -6688,55 +6950,67 @@ function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
6688
6950
  window.open(replacement, "_blank", "noopener");
6689
6951
  }
6690
6952
  };
6691
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-imgdetail", children: [
6692
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-thumbs", children: [
6693
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "rk-thumb", children: [
6694
- beforeSrc ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-thumb-missing", children: "no image" }),
6695
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { children: "Before" })
6953
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail", children: [
6954
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-thumbs", children: [
6955
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "rk-thumb", children: [
6956
+ beforeSrc ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-thumb-missing", children: "no image" }),
6957
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { children: "Before" })
6696
6958
  ] }),
6697
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "rk-thumb", children: [
6698
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: replacement, alt: "Proposed replacement" }),
6699
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figcaption", { children: [
6959
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "rk-thumb", children: [
6960
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: replacement, alt: "Proposed replacement" }),
6961
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figcaption", { children: [
6700
6962
  "After",
6701
6963
  embedded ? "" : " \xB7 linked"
6702
6964
  ] })
6703
6965
  ] })
6704
6966
  ] }),
6705
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-imgdetail-meta", children: [
6967
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail-meta", children: [
6706
6968
  embedded ? "Embedded image" : "Linked image",
6707
6969
  dims ? ` \xB7 ${dims}` : "",
6708
6970
  bytes !== null ? ` \xB7 ${formatBytes(bytes)}` : ""
6709
6971
  ] }),
6710
- !embedded && afterSrc && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
6711
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-imgdetail-actions", children: [
6712
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
6713
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconDownload, { size: 12 }),
6972
+ !embedded && afterSrc && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
6973
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail-actions", children: [
6974
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
6975
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconDownload, { size: 12 }),
6714
6976
  " Download"
6715
6977
  ] }),
6716
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
6978
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
6717
6979
  ] })
6718
6980
  ] });
6719
6981
  }
6982
+ var SYNC_REASON_LABEL = {
6983
+ "token-short": "token too short",
6984
+ "bad-token": "token rejected",
6985
+ "room-full": "room full",
6986
+ unreachable: "server unreachable"
6987
+ };
6988
+ var SYNC_REASON_HELP = {
6989
+ "token-short": "The sync server needs a token of at least 8 characters. Use a longer `token` (or `syncToken`) in <ReviewKit /> and everyone with the link shares items live.",
6990
+ "bad-token": "The sync server rejected the token. Check `syncToken` / `token` in <ReviewKit />.",
6991
+ "room-full": "This page already has the maximum number of reviewers online (16). Try again later.",
6992
+ unreachable: "Could not reach the sync server. Usually the page's Content-Security-Policy is missing the server in connect-src, or the server is down. Items stay on this device meanwhile."
6993
+ };
6720
6994
  function Panel() {
6721
- const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, emblemaPush, peers } = useStore();
6995
+ const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, syncReason, emblemaPush, peers } = useStore();
6722
6996
  const dispatch = useDispatch();
6723
6997
  const config = useConfig();
6724
- const [sticky, setStickyState] = (0, import_react13.useState)(() => isStickyEnabled(config.token));
6725
- const [query, setQuery] = (0, import_react13.useState)("");
6726
- const [typeFilter, setTypeFilter] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
6727
- const [prioFilter, setPrioFilter] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
6728
- const [statusFilter, setStatusFilter] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
6729
- const [filterOpen, setFilterOpen] = (0, import_react13.useState)(false);
6730
- const [settingsOpen, setSettingsOpen] = (0, import_react13.useState)(false);
6731
- const [emblemaAlive, setEmblemaAlive] = (0, import_react13.useState)(null);
6732
- const [emblemaSending, setEmblemaSending] = (0, import_react13.useState)(false);
6733
- const [emblemaSync, setEmblemaSync] = (0, import_react13.useState)("unknown");
6734
- const panelRef = (0, import_react13.useRef)(null);
6735
- const searchRef = (0, import_react13.useRef)(null);
6736
- const importRef = (0, import_react13.useRef)(null);
6737
- const sheetDrag = (0, import_react13.useRef)(null);
6738
- const items = (0, import_react13.useMemo)(() => session?.items ?? [], [session]);
6739
- const { orphanIds, hiddenIds } = (0, import_react13.useMemo)(() => {
6998
+ const [sticky, setStickyState] = (0, import_react14.useState)(() => isStickyEnabled(config.token));
6999
+ const [query, setQuery] = (0, import_react14.useState)("");
7000
+ const [typeFilter, setTypeFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
7001
+ const [prioFilter, setPrioFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
7002
+ const [statusFilter, setStatusFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
7003
+ const [filterOpen, setFilterOpen] = (0, import_react14.useState)(false);
7004
+ const [settingsOpen, setSettingsOpen] = (0, import_react14.useState)(false);
7005
+ const [emblemaAlive, setEmblemaAlive] = (0, import_react14.useState)(null);
7006
+ const [emblemaSending, setEmblemaSending] = (0, import_react14.useState)(false);
7007
+ const [emblemaSync, setEmblemaSync] = (0, import_react14.useState)("unknown");
7008
+ const panelRef = (0, import_react14.useRef)(null);
7009
+ const searchRef = (0, import_react14.useRef)(null);
7010
+ const importRef = (0, import_react14.useRef)(null);
7011
+ const sheetDrag = (0, import_react14.useRef)(null);
7012
+ const items = (0, import_react14.useMemo)(() => session?.items ?? [], [session]);
7013
+ const { orphanIds, hiddenIds } = (0, import_react14.useMemo)(() => {
6740
7014
  const orphans = /* @__PURE__ */ new Set();
6741
7015
  const hiddens = /* @__PURE__ */ new Set();
6742
7016
  for (const item of items) {
@@ -6747,15 +7021,15 @@ function Panel() {
6747
7021
  }
6748
7022
  return { orphanIds: orphans, hiddenIds: hiddens };
6749
7023
  }, [items, panelOpen]);
6750
- const activeFilter = (0, import_react13.useMemo)(
7024
+ const activeFilter = (0, import_react14.useMemo)(
6751
7025
  () => typeFilter.size + prioFilter.size + statusFilter.size > 0 ? { types: [...typeFilter], priorities: [...prioFilter], statuses: [...statusFilter] } : null,
6752
7026
  [typeFilter, prioFilter, statusFilter]
6753
7027
  );
6754
- (0, import_react13.useEffect)(() => {
7028
+ (0, import_react14.useEffect)(() => {
6755
7029
  dispatch({ type: "SET_FILTER", filter: activeFilter });
6756
7030
  return () => dispatch({ type: "SET_FILTER", filter: null });
6757
7031
  }, [activeFilter, dispatch]);
6758
- const filtered = (0, import_react13.useMemo)(() => {
7032
+ const filtered = (0, import_react14.useMemo)(() => {
6759
7033
  const q = query.trim().toLowerCase();
6760
7034
  return items.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => {
6761
7035
  if (!itemMatchesFilter(item, activeFilter)) return false;
@@ -6764,22 +7038,22 @@ function Panel() {
6764
7038
  return haystack.includes(q);
6765
7039
  });
6766
7040
  }, [items, query, activeFilter]);
6767
- const typeCounts = (0, import_react13.useMemo)(() => {
7041
+ const typeCounts = (0, import_react14.useMemo)(() => {
6768
7042
  const counts = /* @__PURE__ */ new Map();
6769
7043
  for (const item of items) counts.set(item.type, (counts.get(item.type) ?? 0) + 1);
6770
7044
  return counts;
6771
7045
  }, [items]);
6772
- const prioCounts = (0, import_react13.useMemo)(() => {
7046
+ const prioCounts = (0, import_react14.useMemo)(() => {
6773
7047
  const counts = /* @__PURE__ */ new Map();
6774
7048
  for (const item of items) counts.set(item.priority, (counts.get(item.priority) ?? 0) + 1);
6775
7049
  return counts;
6776
7050
  }, [items]);
6777
- const statusCounts = (0, import_react13.useMemo)(() => {
7051
+ const statusCounts = (0, import_react14.useMemo)(() => {
6778
7052
  const counts = /* @__PURE__ */ new Map();
6779
7053
  for (const item of items) counts.set(item.status, (counts.get(item.status) ?? 0) + 1);
6780
7054
  return counts;
6781
7055
  }, [items]);
6782
- const groups = (0, import_react13.useMemo)(() => {
7056
+ const groups = (0, import_react14.useMemo)(() => {
6783
7057
  const map = /* @__PURE__ */ new Map();
6784
7058
  for (const entry of filtered) {
6785
7059
  const list = map.get(entry.item.route) ?? [];
@@ -6788,7 +7062,7 @@ function Panel() {
6788
7062
  }
6789
7063
  return Array.from(map.entries());
6790
7064
  }, [filtered]);
6791
- (0, import_react13.useEffect)(() => {
7065
+ (0, import_react14.useEffect)(() => {
6792
7066
  if (!filterOpen && !settingsOpen) return;
6793
7067
  const onDown = (event) => {
6794
7068
  const path = event.composedPath();
@@ -6799,7 +7073,7 @@ function Panel() {
6799
7073
  document.addEventListener("pointerdown", onDown, true);
6800
7074
  return () => document.removeEventListener("pointerdown", onDown, true);
6801
7075
  }, [filterOpen, settingsOpen]);
6802
- (0, import_react13.useEffect)(() => {
7076
+ (0, import_react14.useEffect)(() => {
6803
7077
  if (!settingsOpen) return;
6804
7078
  let alive = true;
6805
7079
  const probe = () => {
@@ -6823,7 +7097,7 @@ function Panel() {
6823
7097
  live: "paired",
6824
7098
  off: "not paired"
6825
7099
  }[emblemaPairState];
6826
- (0, import_react13.useEffect)(() => {
7100
+ (0, import_react14.useEffect)(() => {
6827
7101
  if (!panelOpen || activeItemId === null) return;
6828
7102
  const row = panelRef.current?.querySelector(`[data-item-id="${activeItemId}"]`);
6829
7103
  row?.scrollIntoView({ block: "nearest" });
@@ -6939,7 +7213,7 @@ function Panel() {
6939
7213
  panel.style.transform = "";
6940
7214
  if (dy > 110) dispatch({ type: "SET_PANEL", open: false });
6941
7215
  };
6942
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7216
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6943
7217
  "div",
6944
7218
  {
6945
7219
  ref: panelRef,
@@ -6949,7 +7223,7 @@ function Panel() {
6949
7223
  "aria-label": "Review feedback panel",
6950
7224
  onKeyDown,
6951
7225
  children: [
6952
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7226
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6953
7227
  "div",
6954
7228
  {
6955
7229
  className: "rk-sheet-handle",
@@ -6960,34 +7234,41 @@ function Panel() {
6960
7234
  onPointerCancel: onSheetHandleUp
6961
7235
  }
6962
7236
  ),
6963
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-head", children: [
6964
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
6965
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-panel-title", children: "Feedback" }),
6966
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-sub", children: [
7237
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-head", children: [
7238
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7239
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-panel-title", children: "Feedback" }),
7240
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-sub", children: [
6967
7241
  items.length,
6968
7242
  " item",
6969
7243
  items.length === 1 ? "" : "s",
6970
7244
  session.author ? ` \xB7 ${session.author}` : "",
6971
- syncStatus !== "disabled" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: `rk-sync rk-sync-${syncStatus}`, children: [
6972
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-sync-dot" }),
6973
- syncStatus === "online" ? `live${peers.length > 0 ? ` \xB7 ${peers.length} other${peers.length === 1 ? "" : "s"}` : ""}` : syncStatus === "connecting" ? "connecting\u2026" : "local only"
6974
- ] }),
6975
- config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7245
+ syncStatus !== "disabled" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7246
+ "span",
7247
+ {
7248
+ className: `rk-sync rk-sync-${syncStatus}${syncReason ? ` rk-sync-reason-${syncReason}` : ""}`,
7249
+ title: syncStatus === "online" ? "Live: everyone with this review link shares these items in real time" : syncStatus === "connecting" ? "Connecting to the sync server\u2026" : SYNC_REASON_HELP[syncReason ?? "unreachable"],
7250
+ children: [
7251
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-sync-dot" }),
7252
+ syncStatus === "online" ? `live${peers.length > 0 ? ` \xB7 ${peers.length} other${peers.length === 1 ? "" : "s"}` : ""}` : syncStatus === "connecting" ? "connecting\u2026" : `local only \xB7 ${SYNC_REASON_LABEL[syncReason ?? "unreachable"]}`
7253
+ ]
7254
+ }
7255
+ ),
7256
+ config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6976
7257
  "span",
6977
7258
  {
6978
7259
  className: `rk-sync rk-epush-${emblemaPush}`,
6979
7260
  title: emblemaPush === "ok" ? "Review items stream to Emblema automatically" : emblemaPush === "sending" ? "Sending items to Emblema\u2026" : "Emblema isn't reachable \u2014 items resend on the next change, or use Resend all in the menu",
6980
7261
  children: [
6981
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-sync-dot" }),
7262
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-sync-dot" }),
6982
7263
  emblemaPush === "ok" ? "emblema" : emblemaPush === "sending" ? "emblema\u2026" : emblemaPush === "rejected" ? "emblema rejected" : "emblema offline"
6983
7264
  ]
6984
7265
  }
6985
7266
  )
6986
7267
  ] })
6987
7268
  ] }),
6988
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-spacer" }),
6989
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
6990
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7269
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-spacer" }),
7270
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7271
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6991
7272
  "button",
6992
7273
  {
6993
7274
  type: "button",
@@ -7003,11 +7284,11 @@ function Panel() {
7003
7284
  void pingEmblema(config.emblema).then((port) => setEmblemaAlive(port !== null));
7004
7285
  }
7005
7286
  },
7006
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconGear, { size: 15 })
7287
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconGear, { size: 15 })
7007
7288
  }
7008
7289
  ),
7009
- settingsOpen && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
7010
- config.token && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7290
+ settingsOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
7291
+ config.token && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7011
7292
  "button",
7012
7293
  {
7013
7294
  type: "button",
@@ -7022,12 +7303,12 @@ function Panel() {
7022
7303
  toast(dispatch, next ? "Review stays on in this tab" : "Review active only via the link", "info");
7023
7304
  },
7024
7305
  children: [
7025
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
7306
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
7026
7307
  "Keep review on in this tab"
7027
7308
  ]
7028
7309
  }
7029
7310
  ),
7030
- config.emblema && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7311
+ config.emblema && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7031
7312
  "button",
7032
7313
  {
7033
7314
  type: "button",
@@ -7057,7 +7338,7 @@ function Panel() {
7057
7338
  }).finally(() => setEmblemaSending(false));
7058
7339
  },
7059
7340
  children: [
7060
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7341
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7061
7342
  "span",
7062
7343
  {
7063
7344
  className: "rk-emblema-dot",
@@ -7068,25 +7349,40 @@ function Panel() {
7068
7349
  ]
7069
7350
  }
7070
7351
  ),
7071
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7352
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7072
7353
  "button",
7073
7354
  {
7074
7355
  type: "button",
7075
7356
  role: "menuitem",
7076
7357
  className: "rk-menu-item",
7077
- title: emblemaPairState === "stale" ? "The local sync agent is not answering \u2014 run `npx @satanwagen/reviewkit-cli serve` in the project" : "Pair this project with the Emblema app via a sync key",
7358
+ title: emblemaPairState === "stale" ? "The local sync agent is not answering \u2014 run `npx -p @satanwagen/reviewkit-cli reviewkit-emblema serve` in the project" : "Pair this project with the Emblema app via a sync key",
7078
7359
  onClick: () => {
7079
7360
  setSettingsOpen(false);
7080
7361
  dispatch({ type: "SET_EMBLEMA_CONNECT", open: true });
7081
7362
  },
7082
7363
  children: [
7083
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
7364
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
7084
7365
  "Connect to Emblema\u2026",
7085
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: emblemaPairLabel })
7366
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: emblemaPairLabel })
7086
7367
  ]
7087
7368
  }
7088
7369
  ),
7089
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7370
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7371
+ "button",
7372
+ {
7373
+ type: "button",
7374
+ role: "menuitem",
7375
+ className: "rk-menu-item",
7376
+ title: resolveSyncToken(config) ? "Pair the reviewkit terminal CLI / Claude Code MCP bridge with this site" : "Needs live sync (sync is off on this page) \u2014 the terminal reads the shared room",
7377
+ disabled: !resolveSyncToken(config),
7378
+ onClick: () => {
7379
+ setSettingsOpen(false);
7380
+ dispatch({ type: "SET_TERMINAL_CONNECT", open: true });
7381
+ },
7382
+ children: "Connect terminal / agent\u2026"
7383
+ }
7384
+ ),
7385
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7090
7386
  "button",
7091
7387
  {
7092
7388
  type: "button",
@@ -7099,31 +7395,31 @@ function Panel() {
7099
7395
  )
7100
7396
  ] })
7101
7397
  ] }),
7102
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7398
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7103
7399
  "button",
7104
7400
  {
7105
7401
  type: "button",
7106
7402
  className: "rk-icon-btn",
7107
7403
  "aria-label": "Keyboard shortcuts",
7108
7404
  onClick: () => dispatch({ type: "SET_CHEATSHEET", open: true }),
7109
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconKeyboard, { size: 15 })
7405
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconKeyboard, { size: 15 })
7110
7406
  }
7111
7407
  ),
7112
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7408
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7113
7409
  "button",
7114
7410
  {
7115
7411
  type: "button",
7116
7412
  className: "rk-icon-btn",
7117
7413
  "aria-label": "Close panel",
7118
7414
  onClick: () => dispatch({ type: "SET_PANEL", open: false }),
7119
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconClose, { size: 14 })
7415
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconClose, { size: 14 })
7120
7416
  }
7121
7417
  )
7122
7418
  ] }),
7123
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-view-row", children: [
7124
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-view-info", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-view-title", children: "Page view" }) }),
7125
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
7126
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7419
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-view-row", children: [
7420
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-view-info", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-view-title", children: "Page view" }) }),
7421
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
7422
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7127
7423
  "button",
7128
7424
  {
7129
7425
  type: "button",
@@ -7133,7 +7429,7 @@ function Panel() {
7133
7429
  children: "Original"
7134
7430
  }
7135
7431
  ),
7136
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7432
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7137
7433
  "button",
7138
7434
  {
7139
7435
  type: "button",
@@ -7145,8 +7441,8 @@ function Panel() {
7145
7441
  )
7146
7442
  ] })
7147
7443
  ] }),
7148
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-tools", children: [
7149
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7444
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-tools", children: [
7445
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7150
7446
  "button",
7151
7447
  {
7152
7448
  type: "button",
@@ -7157,15 +7453,15 @@ function Panel() {
7157
7453
  dispatch({ type: "SET_MODE", mode: "pick" });
7158
7454
  },
7159
7455
  children: [
7160
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconTarget, { size: 14 }),
7456
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTarget, { size: 14 }),
7161
7457
  " Pick an element"
7162
7458
  ]
7163
7459
  }
7164
7460
  ),
7165
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-search-row", children: [
7166
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-search", children: [
7167
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconSearch, { size: 13 }),
7168
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7461
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-search-row", children: [
7462
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-search", children: [
7463
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconSearch, { size: 13 }),
7464
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7169
7465
  "input",
7170
7466
  {
7171
7467
  ref: searchRef,
@@ -7177,8 +7473,8 @@ function Panel() {
7177
7473
  }
7178
7474
  )
7179
7475
  ] }),
7180
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7181
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7476
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7477
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7182
7478
  "button",
7183
7479
  {
7184
7480
  type: "button",
@@ -7190,15 +7486,15 @@ function Panel() {
7190
7486
  setSettingsOpen(false);
7191
7487
  },
7192
7488
  children: [
7193
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconFilter, { size: 13 }),
7489
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFilter, { size: 13 }),
7194
7490
  "Filter",
7195
- typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
7491
+ typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
7196
7492
  ]
7197
7493
  }
7198
7494
  ),
7199
- filterOpen && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
7200
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-menu-title", children: "Type" }),
7201
- TYPE_FILTERS.map((type) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7495
+ filterOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
7496
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Type" }),
7497
+ TYPE_FILTERS.map((type) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7202
7498
  "button",
7203
7499
  {
7204
7500
  type: "button",
@@ -7212,16 +7508,16 @@ function Panel() {
7212
7508
  setTypeFilter(next);
7213
7509
  },
7214
7510
  children: [
7215
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
7511
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
7216
7512
  iconForType(type, 12),
7217
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-label", children: type }),
7218
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
7513
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: type }),
7514
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
7219
7515
  ]
7220
7516
  },
7221
7517
  type
7222
7518
  )),
7223
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-menu-title", children: "Priority" }),
7224
- PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7519
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Priority" }),
7520
+ PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7225
7521
  "button",
7226
7522
  {
7227
7523
  type: "button",
@@ -7235,15 +7531,15 @@ function Panel() {
7235
7531
  setPrioFilter(next);
7236
7532
  },
7237
7533
  children: [
7238
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
7239
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
7240
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
7534
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
7535
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
7536
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
7241
7537
  ]
7242
7538
  },
7243
7539
  priority
7244
7540
  )),
7245
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-menu-title", children: "Status" }),
7246
- STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7541
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Status" }),
7542
+ STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7247
7543
  "button",
7248
7544
  {
7249
7545
  type: "button",
@@ -7257,15 +7553,15 @@ function Panel() {
7257
7553
  setStatusFilter(next);
7258
7554
  },
7259
7555
  children: [
7260
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
7261
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-status-dot rk-st-${status}` }),
7262
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-label", children: status }),
7263
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
7556
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
7557
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-status-dot rk-st-${status}` }),
7558
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: status }),
7559
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
7264
7560
  ]
7265
7561
  },
7266
7562
  status
7267
7563
  )),
7268
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7564
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7269
7565
  "button",
7270
7566
  {
7271
7567
  type: "button",
@@ -7285,24 +7581,24 @@ function Panel() {
7285
7581
  ] })
7286
7582
  ] })
7287
7583
  ] }),
7288
- preview && unapplied > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-unplaced", role: "status", children: [
7584
+ preview && unapplied > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-unplaced", role: "status", children: [
7289
7585
  unapplied,
7290
7586
  " saved change",
7291
7587
  unapplied === 1 ? "" : "s",
7292
7588
  " couldn\u2019t be placed on the current page \u2014 the page has changed since they were captured."
7293
7589
  ] }),
7294
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-empty", children: [
7295
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-empty-art", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconTarget, { size: 22 }) }),
7296
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
7297
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-empty-text", children: items.length === 0 ? "Choose \u201CPick an element\u201D, then click anything on the page. Your suggestions collect here, ready to hand off." : "Try a different search, or clear the filters." })
7298
- ] }) : groups.map(([route, entries]) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7299
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-group-title", children: route }),
7590
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-empty", children: [
7591
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-empty-art", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTarget, { size: 22 }) }),
7592
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
7593
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-empty-text", children: items.length === 0 ? "Choose \u201CPick an element\u201D, then click anything on the page. Your suggestions collect here, ready to hand off." : "Try a different search, or clear the filters." })
7594
+ ] }) : groups.map(([route, entries]) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7595
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-group-title", children: route }),
7300
7596
  entries.map(({ item, number }) => {
7301
7597
  const active2 = item.id === activeItemId;
7302
7598
  const orphan = orphanIds.has(item.id);
7303
7599
  const hiddenEl = hiddenIds.has(item.id);
7304
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7305
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7600
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7601
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7306
7602
  "div",
7307
7603
  {
7308
7604
  "data-item-id": item.id,
@@ -7317,18 +7613,18 @@ function Panel() {
7317
7613
  }
7318
7614
  },
7319
7615
  children: [
7320
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-num", children: number }),
7321
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-icon", children: iconForType(item.type) }),
7322
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-item-body", children: [
7323
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-item-snippet", children: [
7616
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-num", children: number }),
7617
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-icon", children: iconForType(item.type) }),
7618
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-body", children: [
7619
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-snippet", children: [
7324
7620
  item.target.tagName,
7325
7621
  " \xB7 ",
7326
7622
  item.target.textSnippet || item.target.selector
7327
7623
  ] }),
7328
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-change", children: changeSummary(item) }),
7329
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-item-meta", children: [
7330
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
7331
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7624
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-change", children: changeSummary(item) }),
7625
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-meta", children: [
7626
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
7627
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7332
7628
  "span",
7333
7629
  {
7334
7630
  className: "rk-author-dot",
@@ -7336,15 +7632,15 @@ function Panel() {
7336
7632
  }
7337
7633
  ),
7338
7634
  item.author?.name ?? "Unknown",
7339
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-time", children: [
7635
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-time", children: [
7340
7636
  " \xB7 ",
7341
7637
  timeAgo(item.createdAt)
7342
7638
  ] })
7343
7639
  ] }),
7344
- item.priority === "must" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-badge-mini rk-must", children: "must" }),
7345
- item.status !== "open" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
7346
- orphan && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
7347
- hiddenEl && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7640
+ item.priority === "must" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini rk-must", children: "must" }),
7641
+ item.status !== "open" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
7642
+ orphan && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
7643
+ hiddenEl && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7348
7644
  "span",
7349
7645
  {
7350
7646
  className: "rk-badge-mini rk-hidden-el",
@@ -7352,14 +7648,14 @@ function Panel() {
7352
7648
  children: "hidden"
7353
7649
  }
7354
7650
  ),
7355
- item.note && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-badge-mini", children: "note" }),
7356
- peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
7651
+ item.note && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini", children: "note" }),
7652
+ peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
7357
7653
  peer.name || "peer",
7358
7654
  " is writing\u2026"
7359
7655
  ] }, peer.id))
7360
7656
  ] })
7361
7657
  ] }),
7362
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-actions", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7658
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-actions", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7363
7659
  "button",
7364
7660
  {
7365
7661
  type: "button",
@@ -7374,15 +7670,15 @@ function Panel() {
7374
7670
  "info"
7375
7671
  );
7376
7672
  },
7377
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconTrash, { size: 13 })
7673
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTrash, { size: 13 })
7378
7674
  }
7379
7675
  ) })
7380
7676
  ]
7381
7677
  }
7382
7678
  ),
7383
- active2 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-item-edit", children: [
7384
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
7385
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7679
+ active2 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-item-edit", children: [
7680
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
7681
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7386
7682
  "span",
7387
7683
  {
7388
7684
  className: "rk-author-dot",
@@ -7393,8 +7689,8 @@ function Panel() {
7393
7689
  " \xB7 ",
7394
7690
  timeAgo(item.createdAt)
7395
7691
  ] }),
7396
- isCopyItem(item) && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CopyDiff, { before: item.payload.before, after: item.payload.after }),
7397
- isImageItem(item) && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7692
+ isCopyItem(item) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CopyDiff, { before: item.payload.before, after: item.payload.after }),
7693
+ isImageItem(item) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7398
7694
  ImageDetail,
7399
7695
  {
7400
7696
  beforeSrc: item.payload.beforeSrc,
@@ -7402,7 +7698,7 @@ function Panel() {
7402
7698
  afterDataUrl: item.payload.afterDataUrl
7403
7699
  }
7404
7700
  ),
7405
- isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7701
+ isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7406
7702
  "textarea",
7407
7703
  {
7408
7704
  className: "rk-textarea",
@@ -7412,7 +7708,7 @@ function Panel() {
7412
7708
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: withPayloadText(item, event.target.value) })
7413
7709
  }
7414
7710
  ),
7415
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7711
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7416
7712
  "input",
7417
7713
  {
7418
7714
  className: "rk-input",
@@ -7422,9 +7718,9 @@ function Panel() {
7422
7718
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: { note: event.target.value } })
7423
7719
  }
7424
7720
  ),
7425
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
7426
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
7427
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7721
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
7722
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
7723
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7428
7724
  "button",
7429
7725
  {
7430
7726
  type: "button",
@@ -7433,7 +7729,7 @@ function Panel() {
7433
7729
  children: "Must"
7434
7730
  }
7435
7731
  ),
7436
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7732
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7437
7733
  "button",
7438
7734
  {
7439
7735
  type: "button",
@@ -7443,7 +7739,7 @@ function Panel() {
7443
7739
  }
7444
7740
  )
7445
7741
  ] }),
7446
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7742
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7447
7743
  "button",
7448
7744
  {
7449
7745
  type: "button",
@@ -7458,22 +7754,22 @@ function Panel() {
7458
7754
  ] }, item.id);
7459
7755
  })
7460
7756
  ] }, route)) }),
7461
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-foot", children: [
7462
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
7463
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconDownload, { size: 13 }),
7757
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-foot", children: [
7758
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
7759
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconDownload, { size: 13 }),
7464
7760
  " Export"
7465
7761
  ] }),
7466
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
7467
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconCopyClipboard, { size: 13 }),
7762
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
7763
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconCopyClipboard, { size: 13 }),
7468
7764
  " Copy"
7469
7765
  ] }),
7470
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
7471
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconUpload, { size: 13 }),
7766
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
7767
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconUpload, { size: 13 }),
7472
7768
  " Import"
7473
7769
  ] }),
7474
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-spacer" }),
7475
- undoStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-icon-btn", "aria-label": "Undo last change (\u2318Z)", onClick: () => dispatch({ type: "UNDO" }), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconUndo, { size: 14 }) }),
7476
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7770
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-spacer" }),
7771
+ undoStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "rk-icon-btn", "aria-label": "Undo last change (\u2318Z)", onClick: () => dispatch({ type: "UNDO" }), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconUndo, { size: 14 }) }),
7772
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7477
7773
  "button",
7478
7774
  {
7479
7775
  type: "button",
@@ -7484,7 +7780,7 @@ function Panel() {
7484
7780
  children: "Clear"
7485
7781
  }
7486
7782
  ),
7487
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7783
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7488
7784
  "button",
7489
7785
  {
7490
7786
  type: "button",
@@ -7494,7 +7790,7 @@ function Panel() {
7494
7790
  children: "Exit review"
7495
7791
  }
7496
7792
  ),
7497
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7793
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7498
7794
  "input",
7499
7795
  {
7500
7796
  ref: importRef,
@@ -7515,8 +7811,8 @@ function Panel() {
7515
7811
  }
7516
7812
 
7517
7813
  // src/client/components/Presence.tsx
7518
- var import_react14 = require("react");
7519
- var import_jsx_runtime12 = require("react/jsx-runtime");
7814
+ var import_react15 = require("react");
7815
+ var import_jsx_runtime13 = require("react/jsx-runtime");
7520
7816
  function initials(name) {
7521
7817
  const parts = name.trim().split(/\s+/).filter(Boolean);
7522
7818
  if (parts.length === 0) return "\xB7";
@@ -7526,8 +7822,8 @@ function initials(name) {
7526
7822
  function PresenceStrip() {
7527
7823
  const { peers, syncStatus } = useStore();
7528
7824
  if (syncStatus === "disabled" || peers.length === 0) return null;
7529
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
7530
- peers.slice(0, 5).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7825
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
7826
+ peers.slice(0, 5).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7531
7827
  "span",
7532
7828
  {
7533
7829
  className: "rk-avatar",
@@ -7537,7 +7833,7 @@ function PresenceStrip() {
7537
7833
  },
7538
7834
  peer.id
7539
7835
  )),
7540
- peers.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-avatar rk-avatar-more", children: [
7836
+ peers.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "rk-avatar rk-avatar-more", children: [
7541
7837
  "+",
7542
7838
  peers.length - 5
7543
7839
  ] })
@@ -7545,13 +7841,13 @@ function PresenceStrip() {
7545
7841
  }
7546
7842
  function PeerCursors() {
7547
7843
  const { peers } = useStore();
7548
- const ringRefs = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
7549
- const cursorRefs = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
7550
- const motion = (0, import_react14.useRef)(
7844
+ const ringRefs = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
7845
+ const cursorRefs = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
7846
+ const motion = (0, import_react15.useRef)(
7551
7847
  /* @__PURE__ */ new Map()
7552
7848
  );
7553
- const peersRef = (0, import_react14.useRef)(peers);
7554
- const seen = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
7849
+ const peersRef = (0, import_react15.useRef)(peers);
7850
+ const seen = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
7555
7851
  for (const peer of peers) {
7556
7852
  const sig = peer.cursor ? `${peer.cursor.x},${peer.cursor.y},${peer.cursor.ox},${peer.cursor.oy}` : "";
7557
7853
  if (seen.current.get(peer.id) !== sig) {
@@ -7561,7 +7857,7 @@ function PeerCursors() {
7561
7857
  }
7562
7858
  }
7563
7859
  peersRef.current = peers;
7564
- (0, import_react14.useEffect)(() => {
7860
+ (0, import_react15.useEffect)(() => {
7565
7861
  const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
7566
7862
  let raf = 0;
7567
7863
  let last = performance.now();
@@ -7666,8 +7962,8 @@ function PeerCursors() {
7666
7962
  return () => cancelAnimationFrame(raf);
7667
7963
  }, []);
7668
7964
  if (peers.length === 0) return null;
7669
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
7670
- peers.map((peer) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7965
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
7966
+ peers.map((peer) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7671
7967
  "div",
7672
7968
  {
7673
7969
  ref: (el) => {
@@ -7679,7 +7975,7 @@ function PeerCursors() {
7679
7975
  },
7680
7976
  `ring-${peer.id}`
7681
7977
  )),
7682
- peers.map((peer, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7978
+ peers.map((peer, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7683
7979
  "div",
7684
7980
  {
7685
7981
  ref: (el) => {
@@ -7689,8 +7985,8 @@ function PeerCursors() {
7689
7985
  className: "rk-peer-cursor",
7690
7986
  style: { opacity: 0 },
7691
7987
  children: [
7692
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("svg", { width: "14", height: "18", viewBox: "0 0 14 18", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M1 1l12 7.5-5.2 1.2L5 16z", fill: peer.color, stroke: "rgba(0,0,0,0.25)", strokeWidth: "1" }) }),
7693
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7988
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { width: "14", height: "18", viewBox: "0 0 14 18", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M1 1l12 7.5-5.2 1.2L5 16z", fill: peer.color, stroke: "rgba(0,0,0,0.25)", strokeWidth: "1" }) }),
7989
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7694
7990
  "span",
7695
7991
  {
7696
7992
  className: "rk-peer-name",
@@ -7709,19 +8005,19 @@ function PeerCursors() {
7709
8005
  }
7710
8006
 
7711
8007
  // src/client/components/QuickComment.tsx
7712
- var import_react15 = require("react");
7713
- var import_jsx_runtime13 = require("react/jsx-runtime");
8008
+ var import_react16 = require("react");
8009
+ var import_jsx_runtime14 = require("react/jsx-runtime");
7714
8010
  function QuickComment() {
7715
8011
  const { quick } = useStore();
7716
8012
  if (!quick) return null;
7717
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(QuickComposerCard, { quick }, quick.id);
8013
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(QuickComposerCard, { quick }, quick.id);
7718
8014
  }
7719
8015
  function QuickComposerCard({ quick }) {
7720
8016
  const { route, session } = useStore();
7721
8017
  const dispatch = useDispatch();
7722
- const [text, setText] = (0, import_react15.useState)("");
7723
- const fieldRef = (0, import_react15.useRef)(null);
7724
- (0, import_react15.useEffect)(() => {
8018
+ const [text, setText] = (0, import_react16.useState)("");
8019
+ const fieldRef = (0, import_react16.useRef)(null);
8020
+ (0, import_react16.useEffect)(() => {
7725
8021
  const field = fieldRef.current;
7726
8022
  if (!field) return;
7727
8023
  field.focus();
@@ -7774,18 +8070,18 @@ function QuickComposerCard({ quick }) {
7774
8070
  };
7775
8071
  const x = Math.min(Math.max(quick.x, 8), window.innerWidth - 288);
7776
8072
  const y = Math.min(Math.max(quick.y + 12, 8), window.innerHeight - 108);
7777
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
7778
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rk-quick-head", children: [
7779
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconComment, { size: 12 }),
7780
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Comment here" }),
7781
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "rk-hint", children: [
7782
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
8073
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
8074
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rk-quick-head", children: [
8075
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconComment, { size: 12 }),
8076
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Comment here" }),
8077
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "rk-hint", children: [
8078
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
7783
8079
  " save \xB7 ",
7784
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "rk-kbd", children: "esc" }),
8080
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "rk-kbd", children: "esc" }),
7785
8081
  " cancel"
7786
8082
  ] })
7787
8083
  ] }),
7788
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
8084
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7789
8085
  "textarea",
7790
8086
  {
7791
8087
  ref: fieldRef,
@@ -7801,8 +8097,8 @@ function QuickComposerCard({ quick }) {
7801
8097
  }
7802
8098
 
7803
8099
  // src/client/components/PickLayer.tsx
7804
- var import_react16 = require("react");
7805
- var import_jsx_runtime14 = require("react/jsx-runtime");
8100
+ var import_react17 = require("react");
8101
+ var import_jsx_runtime15 = require("react/jsx-runtime");
7806
8102
  function applyRect(el, rect, pad2 = 3) {
7807
8103
  if (!el) return;
7808
8104
  if (!rect) {
@@ -7817,14 +8113,14 @@ function applyRect(el, rect, pad2 = 3) {
7817
8113
  function PickLayer() {
7818
8114
  const { mode, selection, tool } = useStore();
7819
8115
  const dispatch = useDispatch();
7820
- const [hovered, setHovered] = (0, import_react16.useState)(null);
7821
- const outlineRef = (0, import_react16.useRef)(null);
7822
- const labelRef = (0, import_react16.useRef)(null);
7823
- const selectRef = (0, import_react16.useRef)(null);
7824
- const hoveredRef = (0, import_react16.useRef)(null);
8116
+ const [hovered, setHovered] = (0, import_react17.useState)(null);
8117
+ const outlineRef = (0, import_react17.useRef)(null);
8118
+ const labelRef = (0, import_react17.useRef)(null);
8119
+ const selectRef = (0, import_react17.useRef)(null);
8120
+ const hoveredRef = (0, import_react17.useRef)(null);
7825
8121
  hoveredRef.current = hovered;
7826
8122
  const picking = mode === "pick";
7827
- (0, import_react16.useEffect)(() => {
8123
+ (0, import_react17.useEffect)(() => {
7828
8124
  if (!picking) {
7829
8125
  setHovered(null);
7830
8126
  return;
@@ -7868,7 +8164,7 @@ function PickLayer() {
7868
8164
  document.removeEventListener("mousedown", swallow, true);
7869
8165
  };
7870
8166
  }, [picking, tool, dispatch]);
7871
- (0, import_react16.useEffect)(() => {
8167
+ (0, import_react17.useEffect)(() => {
7872
8168
  if (!picking) return;
7873
8169
  const body = document.body;
7874
8170
  const prev = body.style.cursor;
@@ -7878,7 +8174,7 @@ function PickLayer() {
7878
8174
  if (body.getAttribute("style") === "") body.removeAttribute("style");
7879
8175
  };
7880
8176
  }, [picking, tool]);
7881
- (0, import_react16.useEffect)(() => {
8177
+ (0, import_react17.useEffect)(() => {
7882
8178
  let raf = 0;
7883
8179
  const tick = () => {
7884
8180
  const hoverEl = hoveredRef.current;
@@ -7904,21 +8200,21 @@ function PickLayer() {
7904
8200
  raf = requestAnimationFrame(tick);
7905
8201
  return () => cancelAnimationFrame(raf);
7906
8202
  }, [picking, selection]);
7907
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
7908
- picking && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
7909
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
7910
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
7911
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
7912
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
7913
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Pick an element" }) })
8203
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8204
+ picking && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8205
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
8206
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8207
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
8208
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
8209
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Pick an element" }) })
7914
8210
  ] }),
7915
- selection && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
8211
+ selection && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
7916
8212
  ] });
7917
8213
  }
7918
8214
 
7919
8215
  // src/client/components/Pins.tsx
7920
- var import_react17 = require("react");
7921
- var import_jsx_runtime15 = require("react/jsx-runtime");
8216
+ var import_react18 = require("react");
8217
+ var import_jsx_runtime16 = require("react/jsx-runtime");
7922
8218
  var STATUS_LABELS = {
7923
8219
  open: "open",
7924
8220
  accepted: "accepted",
@@ -7930,22 +8226,22 @@ var RERESOLVE_MS = 800;
7930
8226
  function Pins() {
7931
8227
  const { session, route, activeItemId, editor, mode, filter } = useStore();
7932
8228
  const dispatch = useDispatch();
7933
- const [clusters, setClusters] = (0, import_react17.useState)([]);
7934
- const [orphanCount, setOrphanCount] = (0, import_react17.useState)(0);
7935
- const wrapRefs = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
7936
- const visibleCache = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
7937
- const highlightRef = (0, import_react17.useRef)(null);
7938
- const hoveredItems = (0, import_react17.useRef)(null);
7939
- const elementCache = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
7940
- const lastResolve = (0, import_react17.useRef)(0);
7941
- const [newestId, setNewestId] = (0, import_react17.useState)(null);
7942
- const prevCount = (0, import_react17.useRef)(0);
7943
- const items = (0, import_react17.useMemo)(() => {
8229
+ const [clusters, setClusters] = (0, import_react18.useState)([]);
8230
+ const [orphanCount, setOrphanCount] = (0, import_react18.useState)(0);
8231
+ const wrapRefs = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8232
+ const visibleCache = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8233
+ const highlightRef = (0, import_react18.useRef)(null);
8234
+ const hoveredItems = (0, import_react18.useRef)(null);
8235
+ const elementCache = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8236
+ const lastResolve = (0, import_react18.useRef)(0);
8237
+ const [newestId, setNewestId] = (0, import_react18.useState)(null);
8238
+ const prevCount = (0, import_react18.useRef)(0);
8239
+ const items = (0, import_react18.useMemo)(() => {
7944
8240
  const all = session?.items ?? [];
7945
8241
  return all.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => item.route === route);
7946
8242
  }, [session, route]);
7947
8243
  const byId = new Map(items.map(({ item }) => [item.id, item]));
7948
- (0, import_react17.useEffect)(() => {
8244
+ (0, import_react18.useEffect)(() => {
7949
8245
  const count = session?.items.length ?? 0;
7950
8246
  if (count > prevCount.current && session && count > 0) {
7951
8247
  const id = session.items[count - 1].id;
@@ -7956,7 +8252,7 @@ function Pins() {
7956
8252
  }
7957
8253
  prevCount.current = count;
7958
8254
  }, [session]);
7959
- (0, import_react17.useEffect)(() => {
8255
+ (0, import_react18.useEffect)(() => {
7960
8256
  let raf = 0;
7961
8257
  let lastSignature = "";
7962
8258
  const tick = (time) => {
@@ -8040,8 +8336,8 @@ function Pins() {
8040
8336
  dispatch({ type: "SET_ACTIVE_ITEM", id });
8041
8337
  dispatch({ type: "SET_PANEL", open: true });
8042
8338
  };
8043
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8044
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
8339
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
8340
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
8045
8341
  clusters.map((cluster) => {
8046
8342
  const isCluster = cluster.ids.length > 1;
8047
8343
  const isActive = activeItemId !== null && cluster.ids.includes(activeItemId);
@@ -8051,7 +8347,7 @@ function Pins() {
8051
8347
  const status = statuses.size === 1 ? members[0]?.status : void 0;
8052
8348
  const dimmed = filter !== null && members.length > 0 && !members.some((item) => itemMatchesFilter(item, filter));
8053
8349
  const statusHint = isCluster ? status ? ` \xB7 all ${STATUS_LABELS[status] ?? status}` : "" : ` \xB7 ${STATUS_LABELS[members[0]?.status ?? "open"] ?? members[0]?.status}`;
8054
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8350
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8055
8351
  "div",
8056
8352
  {
8057
8353
  ref: (el) => {
@@ -8059,7 +8355,7 @@ function Pins() {
8059
8355
  else wrapRefs.current.delete(cluster.key);
8060
8356
  },
8061
8357
  className: `rk-pin-wrap${picking ? " rk-pin-ghost" : ""}`,
8062
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8358
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8063
8359
  "button",
8064
8360
  {
8065
8361
  type: "button",
@@ -8081,7 +8377,7 @@ function Pins() {
8081
8377
  cluster.key
8082
8378
  );
8083
8379
  }),
8084
- orphanCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8380
+ orphanCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8085
8381
  "button",
8086
8382
  {
8087
8383
  type: "button",
@@ -8089,7 +8385,7 @@ function Pins() {
8089
8385
  style: { bottom: 16, left: "50%", transform: "translateX(-50%)" },
8090
8386
  onClick: () => dispatch({ type: "SET_PANEL", open: true }),
8091
8387
  children: [
8092
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconUnlink, { size: 13 }),
8388
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconUnlink, { size: 13 }),
8093
8389
  orphanCount,
8094
8390
  " unanchored item",
8095
8391
  orphanCount === 1 ? "" : "s"
@@ -8100,13 +8396,13 @@ function Pins() {
8100
8396
  }
8101
8397
 
8102
8398
  // src/client/components/Toolbar.tsx
8103
- var import_react18 = require("react");
8104
- var import_jsx_runtime16 = require("react/jsx-runtime");
8399
+ var import_react19 = require("react");
8400
+ var import_jsx_runtime17 = require("react/jsx-runtime");
8105
8401
  var TOOLS = [
8106
- { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconTarget, { size: n }) },
8107
- { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconText, { size: n }) },
8108
- { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconComment, { size: n }) },
8109
- { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconImage, { size: n }) }
8402
+ { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconTarget, { size: n }) },
8403
+ { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconText, { size: n }) },
8404
+ { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconComment, { size: n }) },
8405
+ { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconImage, { size: n }) }
8110
8406
  ];
8111
8407
  var POS_KEY2 = "review-kit:toolbar-pos";
8112
8408
  var DRAG_THRESHOLD2 = 6;
@@ -8114,24 +8410,24 @@ function Toolbar() {
8114
8410
  const { selection, editor, peers, syncStatus, mode, tool } = useStore();
8115
8411
  const hasPeers = syncStatus !== "disabled" && peers.length > 0;
8116
8412
  const dispatch = useDispatch();
8117
- const ref = (0, import_react18.useRef)(null);
8118
- const [pos, setPos] = (0, import_react18.useState)(() => readStoredPos(POS_KEY2));
8119
- const drag = (0, import_react18.useRef)(null);
8120
- const [dragging, setDragging] = (0, import_react18.useState)(false);
8121
- const justDragged = (0, import_react18.useRef)(false);
8413
+ const ref = (0, import_react19.useRef)(null);
8414
+ const [pos, setPos] = (0, import_react19.useState)(() => readStoredPos(POS_KEY2));
8415
+ const drag = (0, import_react19.useRef)(null);
8416
+ const [dragging, setDragging] = (0, import_react19.useState)(false);
8417
+ const justDragged = (0, import_react19.useRef)(false);
8122
8418
  const visible = editor === null || selection !== null;
8123
8419
  const applyPos = (next, persist) => {
8124
8420
  if (next === null && ref.current) ref.current.style.bottom = "";
8125
8421
  setPos(next);
8126
8422
  if (persist) storePos(POS_KEY2, next);
8127
8423
  };
8128
- (0, import_react18.useLayoutEffect)(() => {
8424
+ (0, import_react19.useLayoutEffect)(() => {
8129
8425
  const el = ref.current;
8130
8426
  if (!el || pos === null) return;
8131
8427
  const clamped = clampPos(pos.x, pos.y, el.offsetWidth, el.offsetHeight);
8132
8428
  if (clamped.x !== pos.x || clamped.y !== pos.y) applyPos(clamped, true);
8133
8429
  }, [pos, selection, hasPeers, visible]);
8134
- (0, import_react18.useEffect)(() => {
8430
+ (0, import_react19.useEffect)(() => {
8135
8431
  const onResize = () => {
8136
8432
  const el = ref.current;
8137
8433
  const current = readStoredPos(POS_KEY2);
@@ -8244,7 +8540,7 @@ function Toolbar() {
8244
8540
  mode === "pick" && selection === null ? "rk-ghost" : ""
8245
8541
  ].filter(Boolean).join(" ");
8246
8542
  const style = pos !== null ? { left: 0, top: 0, translate: `${pos.x}px ${pos.y}px` } : void 0;
8247
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8543
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8248
8544
  "div",
8249
8545
  {
8250
8546
  ref,
@@ -8255,7 +8551,7 @@ function Toolbar() {
8255
8551
  onPointerDown,
8256
8552
  onClickCapture,
8257
8553
  children: [
8258
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8554
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8259
8555
  "button",
8260
8556
  {
8261
8557
  type: "button",
@@ -8263,19 +8559,19 @@ function Toolbar() {
8263
8559
  "aria-label": "Move the toolbar \u2014 drag, or use arrow keys; double-click to reset its position",
8264
8560
  onKeyDown: onGripKeyDown,
8265
8561
  onDoubleClick: () => applyPos(null, true),
8266
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
8267
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "2", cy: "2", r: "1.1" }),
8268
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "6", cy: "2", r: "1.1" }),
8269
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "2", cy: "7", r: "1.1" }),
8270
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "6", cy: "7", r: "1.1" }),
8271
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "2", cy: "12", r: "1.1" }),
8272
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "6", cy: "12", r: "1.1" })
8562
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
8563
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "2", r: "1.1" }),
8564
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "2", r: "1.1" }),
8565
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "7", r: "1.1" }),
8566
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "7", r: "1.1" }),
8567
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "12", r: "1.1" }),
8568
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "12", r: "1.1" })
8273
8569
  ] })
8274
8570
  }
8275
8571
  ),
8276
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PresenceStrip, {}),
8277
- hasPeers && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "rk-toolbar-sep" }),
8278
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8572
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PresenceStrip, {}),
8573
+ hasPeers && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8574
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8279
8575
  "button",
8280
8576
  {
8281
8577
  type: "button",
@@ -8287,9 +8583,9 @@ function Toolbar() {
8287
8583
  },
8288
8584
  entry.tool
8289
8585
  )) }),
8290
- selection && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "rk-toolbar-sep" }),
8291
- selection && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
8292
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8586
+ selection && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8587
+ selection && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
8588
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8293
8589
  "button",
8294
8590
  {
8295
8591
  type: "button",
@@ -8298,12 +8594,12 @@ function Toolbar() {
8298
8594
  title: `Rewrite text${keyHint("tool-copy")}`,
8299
8595
  onClick: () => open("copy"),
8300
8596
  children: [
8301
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconText, { size: 14 }),
8597
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconText, { size: 14 }),
8302
8598
  " Rewrite"
8303
8599
  ]
8304
8600
  }
8305
8601
  ),
8306
- hasImage && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8602
+ hasImage && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8307
8603
  "button",
8308
8604
  {
8309
8605
  type: "button",
@@ -8312,12 +8608,12 @@ function Toolbar() {
8312
8608
  title: `Replace image${keyHint("tool-image")}`,
8313
8609
  onClick: () => open("image"),
8314
8610
  children: [
8315
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconImage, { size: 14 }),
8611
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconImage, { size: 14 }),
8316
8612
  " Image"
8317
8613
  ]
8318
8614
  }
8319
8615
  ),
8320
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8616
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8321
8617
  "button",
8322
8618
  {
8323
8619
  type: "button",
@@ -8326,32 +8622,32 @@ function Toolbar() {
8326
8622
  title: `Comment${keyHint("tool-comment")}`,
8327
8623
  onClick: () => open("comment"),
8328
8624
  children: [
8329
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconComment, { size: 14 }),
8625
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconComment, { size: 14 }),
8330
8626
  " Comment"
8331
8627
  ]
8332
8628
  }
8333
8629
  ),
8334
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
8335
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconStyle, { size: 14 }),
8630
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
8631
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconStyle, { size: 14 }),
8336
8632
  " Style"
8337
8633
  ] }),
8338
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
8339
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconMove, { size: 14 }),
8634
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
8635
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconMove, { size: 14 }),
8340
8636
  " Move"
8341
8637
  ] }),
8342
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
8343
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconTrash, { size: 14 }),
8638
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
8639
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconTrash, { size: 14 }),
8344
8640
  " Delete"
8345
8641
  ] }),
8346
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "rk-toolbar-sep" }),
8347
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8642
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8643
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8348
8644
  "button",
8349
8645
  {
8350
8646
  type: "button",
8351
8647
  className: "rk-icon-btn",
8352
8648
  "aria-label": "Deselect",
8353
8649
  onClick: () => dispatch({ type: "SELECT", selection: null }),
8354
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconClose, { size: 13 })
8650
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconClose, { size: 13 })
8355
8651
  }
8356
8652
  )
8357
8653
  ] })
@@ -8361,40 +8657,40 @@ function Toolbar() {
8361
8657
  }
8362
8658
 
8363
8659
  // src/client/components/Toast.tsx
8364
- var import_react19 = require("react");
8365
- var import_jsx_runtime17 = require("react/jsx-runtime");
8660
+ var import_react20 = require("react");
8661
+ var import_jsx_runtime18 = require("react/jsx-runtime");
8366
8662
  function ToastRow({ toast: toast2 }) {
8367
8663
  const dispatch = useDispatch();
8368
- (0, import_react19.useEffect)(() => {
8664
+ (0, import_react20.useEffect)(() => {
8369
8665
  const timer = window.setTimeout(() => dispatch({ type: "DISMISS_TOAST", id: toast2.id }), 3500);
8370
8666
  return () => window.clearTimeout(timer);
8371
8667
  }, [toast2.id, dispatch]);
8372
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
8373
- toast2.tone === "success" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconWarning, { size: 13 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconComment, { size: 13 }),
8668
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
8669
+ toast2.tone === "success" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconWarning, { size: 13 }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconComment, { size: 13 }),
8374
8670
  toast2.message
8375
8671
  ] });
8376
8672
  }
8377
8673
  function ToastHost() {
8378
8674
  const { toasts } = useStore();
8379
8675
  if (toasts.length === 0) return null;
8380
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ToastRow, { toast: toast2 }, toast2.id)) });
8676
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ToastRow, { toast: toast2 }, toast2.id)) });
8381
8677
  }
8382
8678
 
8383
8679
  // src/client/App.tsx
8384
- var import_jsx_runtime18 = require("react/jsx-runtime");
8680
+ var import_jsx_runtime19 = require("react/jsx-runtime");
8385
8681
  function App() {
8386
8682
  const state = useStore();
8387
8683
  const rawDispatch = useDispatch();
8388
8684
  const config = useConfig();
8389
8685
  const dispatch = useRealtimeSync(state, rawDispatch, config, reducer);
8390
- const saveTimer = (0, import_react20.useRef)(null);
8686
+ const saveTimer = (0, import_react21.useRef)(null);
8391
8687
  const storageKey = config.storageKey ?? defaultStorageKey();
8392
8688
  useAppliedPreview(state, dispatch);
8393
8689
  useEmblemaAutoPush(state, dispatch, config);
8394
- (0, import_react20.useEffect)(() => {
8690
+ (0, import_react21.useEffect)(() => {
8395
8691
  setEffectAssetsUrl(config.fxAssetsUrl);
8396
8692
  }, [config.fxAssetsUrl]);
8397
- (0, import_react20.useEffect)(() => {
8693
+ (0, import_react21.useEffect)(() => {
8398
8694
  let cancelled = false;
8399
8695
  void loadSession(storageKey).then((stored) => {
8400
8696
  if (cancelled) return;
@@ -8412,7 +8708,7 @@ function App() {
8412
8708
  };
8413
8709
  }, [storageKey, config.defaultAuthor, dispatch]);
8414
8710
  const { session } = state;
8415
- (0, import_react20.useEffect)(() => {
8711
+ (0, import_react21.useEffect)(() => {
8416
8712
  if (!session) return;
8417
8713
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
8418
8714
  saveTimer.current = window.setTimeout(() => {
@@ -8423,9 +8719,9 @@ function App() {
8423
8719
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
8424
8720
  };
8425
8721
  }, [session, storageKey, config]);
8426
- (0, import_react20.useEffect)(() => trackPointer(), []);
8427
- const focusHandled = (0, import_react20.useRef)(false);
8428
- (0, import_react20.useEffect)(() => {
8722
+ (0, import_react21.useEffect)(() => trackPointer(), []);
8723
+ const focusHandled = (0, import_react21.useRef)(false);
8724
+ (0, import_react21.useEffect)(() => {
8429
8725
  if (!session || focusHandled.current) return;
8430
8726
  focusHandled.current = true;
8431
8727
  const id = takeFocusItem();
@@ -8450,7 +8746,7 @@ function App() {
8450
8746
  cancelled = true;
8451
8747
  };
8452
8748
  }, [session, dispatch]);
8453
- (0, import_react20.useEffect)(() => {
8749
+ (0, import_react21.useEffect)(() => {
8454
8750
  let last = window.location.pathname;
8455
8751
  const check = () => {
8456
8752
  if (window.location.pathname !== last) {
@@ -8465,9 +8761,9 @@ function App() {
8465
8761
  window.removeEventListener("popstate", check);
8466
8762
  };
8467
8763
  }, [dispatch]);
8468
- const stateRef = (0, import_react20.useRef)(state);
8764
+ const stateRef = (0, import_react21.useRef)(state);
8469
8765
  stateRef.current = state;
8470
- (0, import_react20.useEffect)(() => {
8766
+ (0, import_react21.useEffect)(() => {
8471
8767
  const onKeyDown = (event) => {
8472
8768
  const current = stateRef.current;
8473
8769
  for (const def of SHORTCUTS) {
@@ -8482,7 +8778,7 @@ function App() {
8482
8778
  document.addEventListener("keydown", onKeyDown);
8483
8779
  return () => document.removeEventListener("keydown", onKeyDown);
8484
8780
  }, [dispatch]);
8485
- (0, import_react20.useEffect)(() => {
8781
+ (0, import_react21.useEffect)(() => {
8486
8782
  if (!state.editor) return;
8487
8783
  const editorEl = state.editor.element;
8488
8784
  const onPointerDown = (event) => {
@@ -8495,32 +8791,33 @@ function App() {
8495
8791
  }, [state.editor, dispatch]);
8496
8792
  if (!session) return null;
8497
8793
  if (state.hidden) {
8498
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DispatchContext.Provider, { value: dispatch, children: null });
8794
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DispatchContext.Provider, { value: dispatch, children: null });
8499
8795
  }
8500
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DispatchContext.Provider, { value: dispatch, children: [
8501
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PickLayer, {}),
8502
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PeerCursors, {}),
8503
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Toolbar, {}),
8504
- state.editor?.kind === "copy" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(InlineEditor, { editor: state.editor }),
8505
- state.editor?.kind === "image" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ImageEditor, { editor: state.editor }),
8506
- state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NoteComposer, { editor: state.editor }),
8507
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Pins, {}),
8508
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(QuickComment, {}),
8509
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Panel, {}),
8510
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Badge, {}),
8511
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CoachMark, {}),
8512
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CheatSheet, {}),
8513
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(EmblemaConnect, {}),
8514
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ConfirmHost, {}),
8515
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ToastHost, {})
8796
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DispatchContext.Provider, { value: dispatch, children: [
8797
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PickLayer, {}),
8798
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PeerCursors, {}),
8799
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Toolbar, {}),
8800
+ state.editor?.kind === "copy" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(InlineEditor, { editor: state.editor }),
8801
+ state.editor?.kind === "image" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ImageEditor, { editor: state.editor }),
8802
+ state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(NoteComposer, { editor: state.editor }),
8803
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pins, {}),
8804
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(QuickComment, {}),
8805
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Panel, {}),
8806
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Badge, {}),
8807
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CoachMark, {}),
8808
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CheatSheet, {}),
8809
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(EmblemaConnect, {}),
8810
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TerminalConnect, {}),
8811
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConfirmHost, {}),
8812
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ToastHost, {})
8516
8813
  ] });
8517
8814
  }
8518
8815
 
8519
8816
  // src/client/ReviewKit.tsx
8520
- var import_jsx_runtime19 = require("react/jsx-runtime");
8817
+ var import_jsx_runtime20 = require("react/jsx-runtime");
8521
8818
  function ShadowHost({ config }) {
8522
- const [mount, setMount] = (0, import_react21.useState)(null);
8523
- (0, import_react21.useEffect)(() => {
8819
+ const [mount, setMount] = (0, import_react22.useState)(null);
8820
+ (0, import_react22.useEffect)(() => {
8524
8821
  const host = document.createElement("div");
8525
8822
  host.setAttribute(HOST_ATTR, "");
8526
8823
  host.style.position = "fixed";
@@ -8556,15 +8853,15 @@ function ShadowHost({ config }) {
8556
8853
  }, []);
8557
8854
  if (!mount) return null;
8558
8855
  return (0, import_react_dom.createPortal)(
8559
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StoreProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(App, {}) }),
8856
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StoreProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(App, {}) }),
8560
8857
  mount
8561
8858
  );
8562
8859
  }
8563
8860
  function ReviewKit(props) {
8564
- const [active2, setActive] = (0, import_react21.useState)(false);
8861
+ const [active2, setActive] = (0, import_react22.useState)(false);
8565
8862
  const { enabled, token, allowedHosts, devAutoOn } = props;
8566
8863
  const hostsKey = allowedHosts?.join("\0") ?? "";
8567
- (0, import_react21.useEffect)(() => {
8864
+ (0, import_react22.useEffect)(() => {
8568
8865
  if (enabled === false) {
8569
8866
  setActive(false);
8570
8867
  return;
@@ -8575,7 +8872,7 @@ function ReviewKit(props) {
8575
8872
  return () => window.removeEventListener(DEACTIVATE_EVENT, onDeactivate);
8576
8873
  }, [enabled, token, devAutoOn, hostsKey]);
8577
8874
  if (enabled === false || !active2) return null;
8578
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ShadowHost, { config: props });
8875
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ShadowHost, { config: props });
8579
8876
  }
8580
8877
  // Annotate the CommonJS export names for ESM import in node:
8581
8878
  0 && (module.exports = {