@satanwagen/reviewkit 0.1.3 → 0.1.5

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