@satanwagen/reviewkit 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,18 @@ 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 resolveSyncUrl(config) {
4972
+ return typeof config.syncUrl === "string" && config.syncUrl !== "" ? config.syncUrl : DEFAULT_SYNC_URL;
4973
+ }
4974
+ var MIN_SYNC_TOKEN_LENGTH = 8;
4975
+ var warnedShortToken = false;
4976
+ function resolveSyncToken(config) {
4977
+ if (config.sync === false) return null;
4978
+ const explicit = typeof config.syncToken === "string" && config.syncToken !== "" ? config.syncToken : null;
4979
+ const fallback = typeof config.token === "string" && config.token !== "" ? config.token : null;
4980
+ return explicit ?? fallback;
4981
+ }
4957
4982
  function toPeerState(peer) {
4958
4983
  return {
4959
4984
  id: peer.id,
@@ -4981,20 +5006,43 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
4981
5006
  const stateRef = (0, import_react4.useRef)(state);
4982
5007
  stateRef.current = state;
4983
5008
  const myIdRef = (0, import_react4.useRef)("");
4984
- const enabled = typeof config.syncToken === "string" && config.syncToken !== "" && typeof config.syncUrl === "string" && config.syncUrl !== "";
5009
+ const syncToken = resolveSyncToken(config);
5010
+ const syncUrl = resolveSyncUrl(config);
5011
+ const enabled = syncToken !== null;
4985
5012
  const author = state.session?.author ?? "";
4986
5013
  const hasSession = state.session !== null;
4987
5014
  (0, import_react4.useEffect)(() => {
4988
5015
  if (!enabled || !hasSession) return;
5016
+ const dispatchGuard = rawDispatch;
5017
+ if (syncToken.length < MIN_SYNC_TOKEN_LENGTH) {
5018
+ dispatchGuard({ type: "SYNC_STATUS", status: "offline", reason: "token-short" });
5019
+ if (!warnedShortToken) {
5020
+ warnedShortToken = true;
5021
+ console.warn(
5022
+ `[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.`
5023
+ );
5024
+ }
5025
+ return () => dispatchGuard({ type: "SYNC_STATUS", status: "disabled", reason: null });
5026
+ }
4989
5027
  const id = reviewerId();
4990
5028
  myIdRef.current = id;
4991
5029
  const dispatch = rawDispatch;
4992
5030
  const sync = createRkSync({
4993
- token: config.syncToken,
4994
- url: config.syncUrl,
5031
+ token: syncToken,
5032
+ url: syncUrl,
4995
5033
  user: { id, name: author || "Reviewer", color: colorForReviewer(id) },
4996
5034
  handlers: {
4997
- onStatus: (status) => dispatch({ type: "SYNC_STATUS", status }),
5035
+ onStatus: (status) => dispatch({
5036
+ type: "SYNC_STATUS",
5037
+ status,
5038
+ // Offline with no server verdict = the socket never got through
5039
+ // (server down, CSP connect-src, network); a verdict keeps its reason.
5040
+ ...status === "offline" ? { reason: stateRef.current.syncReason ?? "unreachable" } : {}
5041
+ }),
5042
+ onError: (code) => {
5043
+ if (code === "bad-token") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "bad-token" });
5044
+ else if (code === "room-full") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "room-full" });
5045
+ },
4998
5046
  onSnapshot: (items, peers, deletedIds = []) => {
4999
5047
  const safeItems = items.map(sanitizeRemoteItem).filter((item) => item !== null);
5000
5048
  dispatch({ type: "SYNC_SNAPSHOT", items: safeItems });
@@ -5051,10 +5099,10 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
5051
5099
  if (activeSyncRef === sync) activeSyncRef = null;
5052
5100
  setEffectBroadcaster2(null);
5053
5101
  sync.stop();
5054
- dispatch({ type: "SYNC_STATUS", status: "disabled" });
5102
+ dispatch({ type: "SYNC_STATUS", status: "disabled", reason: null });
5055
5103
  dispatch({ type: "PEERS_SET", peers: [] });
5056
5104
  };
5057
- }, [enabled, hasSession, config.syncToken, config.syncUrl, author, rawDispatch]);
5105
+ }, [enabled, hasSession, syncToken, syncUrl, author, rawDispatch]);
5058
5106
  const wrapped = (0, import_react4.useMemo)(() => {
5059
5107
  return (action) => {
5060
5108
  const prev = stateRef.current;
@@ -5570,10 +5618,10 @@ function EmblemaConnect() {
5570
5618
  state === null && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "rk-modal-text", children: [
5571
5619
  "The local sync agent isn't running. In the project repo run:",
5572
5620
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("code", { className: "rk-connect-cmd", children: [
5573
- "npx @satanwagen/reviewkit-cli init --origin ",
5621
+ "npx -p @satanwagen/reviewkit-cli reviewkit-emblema init --origin ",
5574
5622
  window.location.origin
5575
5623
  ] }),
5576
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "rk-connect-cmd", children: "npx @satanwagen/reviewkit-cli serve" }),
5624
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "rk-connect-cmd", children: "npx -p @satanwagen/reviewkit-cli reviewkit-emblema serve" }),
5577
5625
  "then reopen this dialog."
5578
5626
  ] }),
5579
5627
  state !== null && state !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
@@ -5660,15 +5708,229 @@ function EmblemaConnect() {
5660
5708
  ] });
5661
5709
  }
5662
5710
 
5663
- // src/client/components/CoachMark.tsx
5711
+ // src/client/components/TerminalConnect.tsx
5664
5712
  var import_react7 = require("react");
5713
+
5714
+ // src/integrations/terminal.ts
5715
+ var TERMINAL_PAIR_PORTS = [48780, 48781, 48782, 48783];
5716
+ async function fetchJson2(url, init, timeoutMs) {
5717
+ const abort = new AbortController();
5718
+ const timer = window.setTimeout(() => abort.abort(), timeoutMs);
5719
+ try {
5720
+ return await fetch(url, { ...init, signal: abort.signal });
5721
+ } finally {
5722
+ window.clearTimeout(timer);
5723
+ }
5724
+ }
5725
+ async function probeTerminal() {
5726
+ for (const port of TERMINAL_PAIR_PORTS) {
5727
+ try {
5728
+ const res = await fetchJson2(`http://127.0.0.1:${port}/pair`, { method: "GET" }, 900);
5729
+ if (!res.ok) continue;
5730
+ const body = await res.json();
5731
+ if (typeof body === "object" && body !== null && body.app === "reviewkit-cli") {
5732
+ const b = body;
5733
+ return {
5734
+ port,
5735
+ cwd: typeof b.cwd === "string" ? b.cwd : void 0,
5736
+ projects: Array.isArray(b.projects) ? b.projects.filter((p) => typeof p === "string") : []
5737
+ };
5738
+ }
5739
+ } catch {
5740
+ }
5741
+ }
5742
+ return null;
5743
+ }
5744
+ async function pairTerminal(port, key) {
5745
+ try {
5746
+ const res = await fetchJson2(
5747
+ `http://127.0.0.1:${port}/pair`,
5748
+ { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ key }) },
5749
+ 5e3
5750
+ );
5751
+ const body = await res.json().catch(() => null);
5752
+ if (res.ok && body?.ok) return { ok: true, name: body.name ?? "", repo: body.repo ?? null };
5753
+ return { ok: false, error: body?.error ?? `HTTP ${res.status}` };
5754
+ } catch {
5755
+ return { ok: false, error: "the terminal stopped answering" };
5756
+ }
5757
+ }
5758
+
5759
+ // src/client/terminalKey.ts
5760
+ var TERMINAL_KEY_PREFIX = "rkc_";
5761
+ var TERMINAL_KEY_VERSION = 1;
5762
+ function base64url(text) {
5763
+ const bytes = new TextEncoder().encode(text);
5764
+ let binary = "";
5765
+ for (const byte of bytes) binary += String.fromCharCode(byte);
5766
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
5767
+ }
5768
+ function encodeTerminalKey(fields) {
5769
+ const payload = { v: TERMINAL_KEY_VERSION, ...fields };
5770
+ return TERMINAL_KEY_PREFIX + base64url(JSON.stringify(payload));
5771
+ }
5772
+ function currentTerminalKey(syncUrl, syncToken) {
5773
+ const origin = window.location.origin;
5774
+ let route = "/";
5775
+ try {
5776
+ route = window.location.pathname || "/";
5777
+ } catch {
5778
+ }
5779
+ return encodeTerminalKey({ name: window.location.hostname, origin, syncUrl, syncToken, route });
5780
+ }
5781
+
5782
+ // src/client/components/TerminalConnect.tsx
5665
5783
  var import_jsx_runtime6 = require("react/jsx-runtime");
5784
+ function shortKey2(key) {
5785
+ return `${key.slice(0, 8)}\u2026${key.slice(-6)}`;
5786
+ }
5787
+ function shortPath(path) {
5788
+ if (!path) return "";
5789
+ const parts = path.split("/").filter(Boolean);
5790
+ return parts.length > 2 ? `\u2026/${parts.slice(-2).join("/")}` : path;
5791
+ }
5792
+ function TerminalConnect() {
5793
+ const { terminalConnectOpen } = useStore();
5794
+ const config = useConfig();
5795
+ const dispatch = useDispatch();
5796
+ const [phase, setPhase] = (0, import_react7.useState)("probing");
5797
+ const [probe, setProbe] = (0, import_react7.useState)(null);
5798
+ const [paired, setPaired] = (0, import_react7.useState)(null);
5799
+ const [showManual, setShowManual] = (0, import_react7.useState)(false);
5800
+ const syncToken = resolveSyncToken(config);
5801
+ const key = terminalConnectOpen && syncToken ? currentTerminalKey(resolveSyncUrl(config), syncToken) : null;
5802
+ (0, import_react7.useEffect)(() => {
5803
+ if (!terminalConnectOpen) return;
5804
+ let cancelled = false;
5805
+ setPhase("probing");
5806
+ setProbe(null);
5807
+ setPaired(null);
5808
+ setShowManual(false);
5809
+ const look = () => void probeTerminal().then((found) => {
5810
+ if (cancelled) return;
5811
+ setProbe(found);
5812
+ setPhase((current) => current === "pairing" || current === "paired" ? current : found ? "found" : "missing");
5813
+ });
5814
+ look();
5815
+ const timer = window.setInterval(look, 2e3);
5816
+ return () => {
5817
+ cancelled = true;
5818
+ window.clearInterval(timer);
5819
+ };
5820
+ }, [terminalConnectOpen]);
5821
+ if (!terminalConnectOpen) return null;
5822
+ const close = () => dispatch({ type: "SET_TERMINAL_CONNECT", open: false });
5823
+ const copy = async (value, what) => {
5824
+ try {
5825
+ await navigator.clipboard.writeText(value);
5826
+ toast(dispatch, `${what} copied`, "success");
5827
+ } catch {
5828
+ toast(dispatch, "Copy failed \u2014 select the text manually", "error");
5829
+ }
5830
+ };
5831
+ const connect = () => {
5832
+ if (!probe || !key) return;
5833
+ setPhase("pairing");
5834
+ void pairTerminal(probe.port, key).then((result) => {
5835
+ if (result.ok) {
5836
+ setPaired({ name: result.name, repo: result.repo });
5837
+ setPhase("paired");
5838
+ toast(dispatch, `Terminal paired as \u201C${result.name}\u201D`, "success");
5839
+ } else {
5840
+ setPhase("found");
5841
+ toast(dispatch, `Pairing failed: ${result.error}`, "error");
5842
+ }
5843
+ });
5844
+ };
5845
+ const connectCmd = key ? `npx -y @satanwagen/reviewkit-cli connect ${key}` : "";
5846
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5847
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-backdrop", onClick: close }),
5848
+ /* @__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: [
5849
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5850
+ "Connect terminal / agent",
5851
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { flex: 1 } }),
5852
+ /* @__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 }) })
5853
+ ] }),
5854
+ !key && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
5855
+ "Live sync is off on this page (",
5856
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "sync=false" }),
5857
+ " or no token), so there is no shared room a terminal could read."
5858
+ ] }),
5859
+ key && phase === "paired" && paired && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
5860
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-emblema-dot", "data-state": "live" }),
5861
+ " Paired as ",
5862
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: paired.name }),
5863
+ paired.repo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5864
+ " in ",
5865
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: shortPath(paired.repo) })
5866
+ ] }) : null,
5867
+ ". The terminal shows this page's comments live, and Claude Code / Cursor can read and resolve them through the ",
5868
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "reviewkit" }),
5869
+ " MCP tools."
5870
+ ] }),
5871
+ key && phase !== "paired" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5872
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
5873
+ phase === "probing" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5874
+ "Looking for a ",
5875
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "reviewkit" }),
5876
+ " terminal on this machine\u2026"
5877
+ ] }),
5878
+ phase === "missing" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5879
+ "No terminal is listening yet. In the site's repo run",
5880
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { className: "rk-connect-cmd", children: "npx -y @satanwagen/reviewkit-cli setup" }),
5881
+ "and this dialog will pick it up by itself."
5882
+ ] }),
5883
+ (phase === "found" || phase === "pairing") && probe && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5884
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-emblema-dot", "data-state": "live" }),
5885
+ " Terminal found",
5886
+ probe.cwd ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5887
+ " in ",
5888
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: shortPath(probe.cwd) })
5889
+ ] }) : null,
5890
+ ".",
5891
+ probe.projects.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5892
+ " Already paired: ",
5893
+ probe.projects.join(", "),
5894
+ "."
5895
+ ] }) : null
5896
+ ] })
5897
+ ] }),
5898
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-connect-actions", children: [
5899
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5900
+ "button",
5901
+ {
5902
+ type: "button",
5903
+ className: "rk-btn rk-btn-primary",
5904
+ disabled: phase !== "found",
5905
+ onClick: connect,
5906
+ title: "Send this site's connection to the terminal \u2014 nothing to copy",
5907
+ children: phase === "pairing" ? "Connecting\u2026" : "Connect this terminal"
5908
+ }
5909
+ ),
5910
+ /* @__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" })
5911
+ ] }),
5912
+ showManual && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5913
+ /* @__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):" }),
5914
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("code", { className: "rk-connect-cmd", title: connectCmd, children: [
5915
+ "npx -y @satanwagen/reviewkit-cli connect ",
5916
+ shortKey2(key)
5917
+ ] }),
5918
+ /* @__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" }) })
5919
+ ] })
5920
+ ] })
5921
+ ] }) })
5922
+ ] });
5923
+ }
5924
+
5925
+ // src/client/components/CoachMark.tsx
5926
+ var import_react8 = require("react");
5927
+ var import_jsx_runtime7 = require("react/jsx-runtime");
5666
5928
  var COACH_KEY = "review-kit:coach-dismissed";
5667
5929
  function CoachMark() {
5668
5930
  const { coachOpen, session } = useStore();
5669
5931
  const dispatch = useDispatch();
5670
5932
  const config = useConfig();
5671
- const [name, setName] = (0, import_react7.useState)(session?.author ?? config.defaultAuthor ?? "");
5933
+ const [name, setName] = (0, import_react8.useState)(session?.author ?? config.defaultAuthor ?? "");
5672
5934
  if (!coachOpen) return null;
5673
5935
  const done = (startPicking) => {
5674
5936
  if (name.trim() !== "") dispatch({ type: "SET_AUTHOR", author: name.trim() });
@@ -5679,39 +5941,39 @@ function CoachMark() {
5679
5941
  }
5680
5942
  if (startPicking) dispatch({ type: "SET_MODE", mode: "pick" });
5681
5943
  };
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 }),
5944
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
5945
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-coach-accent" }),
5946
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach-body", children: [
5947
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5948
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconTarget, { size: 16 }),
5687
5949
  " Review mode"
5688
5950
  ] }),
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" }),
5951
+ /* @__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." }),
5952
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
5953
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5954
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "T" }),
5693
5955
  " rewrite"
5694
5956
  ] }),
5695
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5696
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "C" }),
5957
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5958
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "C" }),
5697
5959
  " comment"
5698
5960
  ] }),
5699
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5700
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "I" }),
5961
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5962
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "I" }),
5701
5963
  " image"
5702
5964
  ] }),
5703
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5704
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "V" }),
5965
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5966
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "V" }),
5705
5967
  " select"
5706
5968
  ] }),
5707
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5708
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "?" }),
5969
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
5970
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "?" }),
5709
5971
  " all shortcuts"
5710
5972
  ] })
5711
5973
  ] }),
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)(
5974
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
5975
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
5976
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5715
5977
  "input",
5716
5978
  {
5717
5979
  id: "rk-author",
@@ -5725,36 +5987,36 @@ function CoachMark() {
5725
5987
  }
5726
5988
  )
5727
5989
  ] }),
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" })
5990
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
5991
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
5992
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
5731
5993
  ] })
5732
5994
  ] })
5733
5995
  ] });
5734
5996
  }
5735
5997
 
5736
5998
  // src/client/components/Confirm.tsx
5737
- var import_react8 = require("react");
5738
- var import_jsx_runtime7 = require("react/jsx-runtime");
5999
+ var import_react9 = require("react");
6000
+ var import_jsx_runtime8 = require("react/jsx-runtime");
5739
6001
  function ConfirmHost() {
5740
6002
  const { confirm } = useStore();
5741
6003
  const dispatch = useDispatch();
5742
- const cancelRef = (0, import_react8.useRef)(null);
5743
- (0, import_react8.useEffect)(() => {
6004
+ const cancelRef = (0, import_react9.useRef)(null);
6005
+ (0, import_react9.useEffect)(() => {
5744
6006
  if (confirm) cancelRef.current?.focus();
5745
6007
  }, [confirm]);
5746
6008
  if (!confirm) return null;
5747
6009
  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 })
6010
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
6011
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-backdrop", onClick: close }),
6012
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
6013
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal-body", children: [
6014
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-modal-title", children: confirm.title }),
6015
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-modal-text", children: confirm.message })
5754
6016
  ] }),
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)(
6017
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal-actions", children: [
6018
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
6019
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5758
6020
  "button",
5759
6021
  {
5760
6022
  type: "button",
@@ -5773,10 +6035,10 @@ function ConfirmHost() {
5773
6035
  }
5774
6036
 
5775
6037
  // src/client/components/ImageEditor.tsx
5776
- var import_react10 = require("react");
6038
+ var import_react11 = require("react");
5777
6039
 
5778
6040
  // src/client/useAnchored.ts
5779
- var import_react9 = require("react");
6041
+ var import_react10 = require("react");
5780
6042
 
5781
6043
  // src/client/position.ts
5782
6044
  function place(anchor, size, side, offset) {
@@ -5821,9 +6083,9 @@ function computePosition(anchor, size, options = {}) {
5821
6083
 
5822
6084
  // src/client/useAnchored.ts
5823
6085
  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)(() => {
6086
+ const ref = (0, import_react10.useRef)(null);
6087
+ const [pos, setPos] = (0, import_react10.useState)(null);
6088
+ (0, import_react10.useLayoutEffect)(() => {
5827
6089
  if (!anchorEl) {
5828
6090
  setPos(null);
5829
6091
  return;
@@ -5854,7 +6116,7 @@ function anchoredStyle(pos) {
5854
6116
  }
5855
6117
 
5856
6118
  // src/client/components/ImageEditor.tsx
5857
- var import_jsx_runtime8 = require("react/jsx-runtime");
6119
+ var import_jsx_runtime9 = require("react/jsx-runtime");
5858
6120
  var MAX_BYTES = 2 * 1024 * 1024;
5859
6121
  var FETCH_TIMEOUT_MS = 12e3;
5860
6122
  var PROBE_TIMEOUT_MS = 8e3;
@@ -5912,15 +6174,15 @@ function urlFromDataTransfer(dt) {
5912
6174
  function ImageEditor({ editor }) {
5913
6175
  const dispatch = useDispatch();
5914
6176
  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);
6177
+ const [replacement, setReplacement] = (0, import_react11.useState)(null);
6178
+ const [urlValue, setUrlValue] = (0, import_react11.useState)("");
6179
+ const [loading2, setLoading] = (0, import_react11.useState)(false);
6180
+ const [description, setDescription] = (0, import_react11.useState)("");
6181
+ const [priority, setPriority] = (0, import_react11.useState)("nice");
6182
+ const [over, setOver] = (0, import_react11.useState)(false);
6183
+ const fileInput = (0, import_react11.useRef)(null);
6184
+ const abortRef = (0, import_react11.useRef)(null);
6185
+ const debounceRef = (0, import_react11.useRef)(null);
5924
6186
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
5925
6187
  const image = findImage(editor.element);
5926
6188
  const beforeSrc = image?.currentSrc || image?.src || "";
@@ -5984,7 +6246,7 @@ function ImageEditor({ editor }) {
5984
6246
  }
5985
6247
  }
5986
6248
  };
5987
- (0, import_react10.useEffect)(() => {
6249
+ (0, import_react11.useEffect)(() => {
5988
6250
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
5989
6251
  if (urlValue.trim() === "" || replacement !== null) return;
5990
6252
  debounceRef.current = window.setTimeout(() => {
@@ -5994,8 +6256,8 @@ function ImageEditor({ editor }) {
5994
6256
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
5995
6257
  };
5996
6258
  }, [urlValue, replacement]);
5997
- (0, import_react10.useEffect)(() => () => abortRef.current?.abort(), []);
5998
- (0, import_react10.useEffect)(() => {
6259
+ (0, import_react11.useEffect)(() => () => abortRef.current?.abort(), []);
6260
+ (0, import_react11.useEffect)(() => {
5999
6261
  const onPaste = (event) => {
6000
6262
  const data = event.clipboardData;
6001
6263
  if (!data) return;
@@ -6073,23 +6335,23 @@ function ImageEditor({ editor }) {
6073
6335
  toast(dispatch, "Image proposal saved", "success");
6074
6336
  };
6075
6337
  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 }),
6338
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
6339
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-head", children: [
6340
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconImage, { size: 14 }),
6079
6341
  "Replace image"
6080
6342
  ] }),
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" })
6343
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-body", children: [
6344
+ 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: [
6345
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("figure", { className: "rk-thumb", children: [
6346
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: beforeSrc, alt: "Current" }),
6347
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("figcaption", { children: "Before" })
6086
6348
  ] }),
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" })
6349
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("figure", { className: "rk-thumb", children: [
6350
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: previewSrc, alt: "Proposed replacement" }),
6351
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
6090
6352
  ] })
6091
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
6092
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
6353
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
6354
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
6093
6355
  "button",
6094
6356
  {
6095
6357
  type: "button",
@@ -6102,18 +6364,18 @@ function ImageEditor({ editor }) {
6102
6364
  onDragLeave: () => setOver(false),
6103
6365
  onDrop,
6104
6366
  children: [
6105
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconUpload, { size: 18 }),
6106
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
6367
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconUpload, { size: 18 }),
6368
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6107
6369
  "Drop an image \u2014 from your disk or another tab \u2014",
6108
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
6370
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("br", {}),
6109
6371
  "or click to browse (max 2 MB)"
6110
6372
  ] })
6111
6373
  ]
6112
6374
  }
6113
6375
  ),
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)(
6376
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-url", children: [
6377
+ loading2 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconLink, { size: 13 }),
6378
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6117
6379
  "input",
6118
6380
  {
6119
6381
  className: "rk-input",
@@ -6128,9 +6390,9 @@ function ImageEditor({ editor }) {
6128
6390
  )
6129
6391
  ] })
6130
6392
  ] }),
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)(
6393
+ replacement && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
6394
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
6395
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6134
6396
  "textarea",
6135
6397
  {
6136
6398
  className: "rk-textarea",
@@ -6140,43 +6402,43 @@ function ImageEditor({ editor }) {
6140
6402
  rows: 2
6141
6403
  }
6142
6404
  ),
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" })
6405
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6406
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6407
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6146
6408
  ] })
6147
6409
  ] }),
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" }),
6410
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-foot", children: [
6411
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "rk-hint", children: [
6412
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "esc" }),
6151
6413
  " cancel"
6152
6414
  ] }),
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" })
6415
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-actions", children: [
6416
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6417
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6156
6418
  ] })
6157
6419
  ] })
6158
6420
  ] });
6159
6421
  }
6160
6422
 
6161
6423
  // src/client/components/InlineEditor.tsx
6162
- var import_react11 = require("react");
6163
- var import_jsx_runtime9 = require("react/jsx-runtime");
6424
+ var import_react12 = require("react");
6425
+ var import_jsx_runtime10 = require("react/jsx-runtime");
6164
6426
  function InlineEditor({ editor }) {
6165
6427
  const dispatch = useDispatch();
6166
6428
  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);
6429
+ const [after, setAfter] = (0, import_react12.useState)(() => editor.element.innerText);
6430
+ const [priority, setPriority] = (0, import_react12.useState)("nice");
6431
+ const [note, setNote] = (0, import_react12.useState)("");
6432
+ const original = (0, import_react12.useRef)(null);
6433
+ const baseline = (0, import_react12.useRef)(null);
6434
+ const [sizeDelta, setSizeDelta] = (0, import_react12.useState)(0);
6435
+ const [widthDelta, setWidthDelta] = (0, import_react12.useState)(0);
6436
+ const [clipped, setClipped] = (0, import_react12.useState)(false);
6437
+ const ringRef = (0, import_react12.useRef)(null);
6438
+ const afterRef = (0, import_react12.useRef)(null);
6177
6439
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
6178
6440
  const element = editor.element;
6179
- (0, import_react11.useEffect)(() => {
6441
+ (0, import_react12.useEffect)(() => {
6180
6442
  const el = element;
6181
6443
  original.current = { html: el.innerHTML, text: el.innerText, styleAttr: el.getAttribute("style") };
6182
6444
  const rect = el.getBoundingClientRect();
@@ -6206,8 +6468,8 @@ function InlineEditor({ editor }) {
6206
6468
  }
6207
6469
  };
6208
6470
  }, [element]);
6209
- const focusedOnce = (0, import_react11.useRef)(false);
6210
- (0, import_react11.useEffect)(() => {
6471
+ const focusedOnce = (0, import_react12.useRef)(false);
6472
+ (0, import_react12.useEffect)(() => {
6211
6473
  const field = afterRef.current;
6212
6474
  if (pos === null || focusedOnce.current || !field) return;
6213
6475
  focusedOnce.current = true;
@@ -6215,7 +6477,7 @@ function InlineEditor({ editor }) {
6215
6477
  const end = field.value.length;
6216
6478
  field.setSelectionRange(end, end);
6217
6479
  }, [pos]);
6218
- (0, import_react11.useEffect)(() => {
6480
+ (0, import_react12.useEffect)(() => {
6219
6481
  const field = afterRef.current;
6220
6482
  if (!field) return;
6221
6483
  field.style.height = "auto";
@@ -6225,7 +6487,7 @@ function InlineEditor({ editor }) {
6225
6487
  setAfter(value);
6226
6488
  if (element.innerText !== value) setElementText(element, value);
6227
6489
  };
6228
- (0, import_react11.useEffect)(() => {
6490
+ (0, import_react12.useEffect)(() => {
6229
6491
  let raf = 0;
6230
6492
  let lastDelta = 0;
6231
6493
  let lastWidth = 0;
@@ -6295,7 +6557,7 @@ function InlineEditor({ editor }) {
6295
6557
  cancel();
6296
6558
  }
6297
6559
  };
6298
- (0, import_react11.useEffect)(() => {
6560
+ (0, import_react12.useEffect)(() => {
6299
6561
  const el = element;
6300
6562
  const onKeyDown = (event) => {
6301
6563
  if (event.key === "Enter" && !event.shiftKey) {
@@ -6313,19 +6575,19 @@ function InlineEditor({ editor }) {
6313
6575
  });
6314
6576
  const before = original.current?.text ?? "";
6315
6577
  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 }),
6578
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
6579
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: ringRef, className: "rk-edit-ring" }),
6580
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
6581
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-head", children: [
6582
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconText, { size: 14 }),
6321
6583
  "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: [
6584
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-spacer" }),
6585
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "rk-delta", "aria-live": "polite", children: [
6324
6586
  before.length,
6325
6587
  " \u2192 ",
6326
6588
  after.length,
6327
6589
  " ",
6328
- delta !== 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
6590
+ delta !== 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
6329
6591
  "(",
6330
6592
  delta > 0 ? "+" : "",
6331
6593
  delta,
@@ -6333,15 +6595,15 @@ function InlineEditor({ editor }) {
6333
6595
  ] })
6334
6596
  ] })
6335
6597
  ] }),
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 })
6598
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-body", children: [
6599
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff", "aria-label": "Before and after", children: [
6600
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6601
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6602
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-text", children: before })
6341
6603
  ] }),
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)(
6604
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6605
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6606
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6345
6607
  "textarea",
6346
6608
  {
6347
6609
  ref: afterRef,
@@ -6355,11 +6617,11 @@ function InlineEditor({ editor }) {
6355
6617
  )
6356
6618
  ] })
6357
6619
  ] }),
6358
- (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-size-note", role: "status", children: [
6620
+ (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-size-note", role: "status", children: [
6359
6621
  Math.abs(sizeDelta) > 2 && (() => {
6360
6622
  const lines = baseline.current ? Math.round(Math.abs(sizeDelta) / baseline.current.lineHeight) : 0;
6361
6623
  const lineNote = lines >= 1 ? ` (~${lines} line${lines === 1 ? "" : "s"})` : "";
6362
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6624
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
6363
6625
  "Block ",
6364
6626
  sizeDelta > 0 ? "grew" : "shrank",
6365
6627
  " by ",
@@ -6369,16 +6631,16 @@ function InlineEditor({ editor }) {
6369
6631
  " \u2014 as it would with this text live."
6370
6632
  ] });
6371
6633
  })(),
6372
- Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6634
+ Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
6373
6635
  "Block got ",
6374
6636
  widthDelta > 0 ? "wider" : "narrower",
6375
6637
  " by ",
6376
6638
  Math.abs(widthDelta),
6377
6639
  " px \u2014 as it would with this text live."
6378
6640
  ] }),
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." })
6641
+ 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
6642
  ] }),
6381
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6643
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6382
6644
  "input",
6383
6645
  {
6384
6646
  className: "rk-input",
@@ -6387,24 +6649,24 @@ function InlineEditor({ editor }) {
6387
6649
  onChange: (event) => setNote(event.target.value)
6388
6650
  }
6389
6651
  ),
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" })
6652
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6653
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6654
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6393
6655
  ] })
6394
6656
  ] }),
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" }),
6657
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-foot", children: [
6658
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "rk-hint", children: [
6659
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
6398
6660
  " save \xB7 ",
6399
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
6661
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
6400
6662
  " newline \xB7",
6401
6663
  " ",
6402
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "esc" }),
6664
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "esc" }),
6403
6665
  " cancel"
6404
6666
  ] }),
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" })
6667
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-actions", children: [
6668
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
6669
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
6408
6670
  ] })
6409
6671
  ] })
6410
6672
  ] })
@@ -6412,8 +6674,8 @@ function InlineEditor({ editor }) {
6412
6674
  }
6413
6675
 
6414
6676
  // src/client/components/NoteComposer.tsx
6415
- var import_react12 = require("react");
6416
- var import_jsx_runtime10 = require("react/jsx-runtime");
6677
+ var import_react13 = require("react");
6678
+ var import_jsx_runtime11 = require("react/jsx-runtime");
6417
6679
  var TITLES = {
6418
6680
  comment: "Comment",
6419
6681
  delete: "Mark for deletion",
@@ -6429,14 +6691,14 @@ var PLACEHOLDERS = {
6429
6691
  function NoteComposer({ editor }) {
6430
6692
  const dispatch = useDispatch();
6431
6693
  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);
6694
+ const [text, setText] = (0, import_react13.useState)("");
6695
+ const [direction, setDirection] = (0, import_react13.useState)(void 0);
6696
+ const [priority, setPriority] = (0, import_react13.useState)("nice");
6697
+ const textareaRef = (0, import_react13.useRef)(null);
6436
6698
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
6437
6699
  const { kind } = editor;
6438
- const focusedOnce = (0, import_react12.useRef)(false);
6439
- (0, import_react12.useEffect)(() => {
6700
+ const focusedOnce = (0, import_react13.useRef)(false);
6701
+ (0, import_react13.useEffect)(() => {
6440
6702
  const field = textareaRef.current;
6441
6703
  if (pos === null || focusedOnce.current || !field) return;
6442
6704
  focusedOnce.current = true;
@@ -6495,13 +6757,13 @@ function NoteComposer({ editor }) {
6495
6757
  save();
6496
6758
  }
6497
6759
  };
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: [
6760
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
6761
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-head", children: [
6500
6762
  iconForType(kind),
6501
6763
  TITLES[kind]
6502
6764
  ] }),
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)(
6765
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-body", children: [
6766
+ 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
6767
  "button",
6506
6768
  {
6507
6769
  type: "button",
@@ -6511,7 +6773,7 @@ function NoteComposer({ editor }) {
6511
6773
  },
6512
6774
  option
6513
6775
  )) }),
6514
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6776
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6515
6777
  "textarea",
6516
6778
  {
6517
6779
  ref: textareaRef,
@@ -6523,29 +6785,29 @@ function NoteComposer({ editor }) {
6523
6785
  rows: 3
6524
6786
  }
6525
6787
  ),
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" })
6788
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6789
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6790
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6529
6791
  ] })
6530
6792
  ] }),
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" }),
6793
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-foot", children: [
6794
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-hint", children: [
6795
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
6534
6796
  " save \xB7 ",
6535
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "esc" }),
6797
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-kbd", children: "esc" }),
6536
6798
  " cancel"
6537
6799
  ] }),
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" })
6800
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-actions", children: [
6801
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6802
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6541
6803
  ] })
6542
6804
  ] })
6543
6805
  ] });
6544
6806
  }
6545
6807
 
6546
6808
  // src/client/components/Panel.tsx
6547
- var import_react13 = require("react");
6548
- var import_jsx_runtime11 = require("react/jsx-runtime");
6809
+ var import_react14 = require("react");
6810
+ var import_jsx_runtime12 = require("react/jsx-runtime");
6549
6811
  function changeSummary(item) {
6550
6812
  if (isCopyItem(item)) return `\u201C${item.payload.after}\u201D`;
6551
6813
  if (isImageItem(item)) {
@@ -6627,24 +6889,24 @@ function formatBytes(bytes) {
6627
6889
  function CopyDiff({ before, after }) {
6628
6890
  const parts = diffParts(before, after);
6629
6891
  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: [
6892
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diffview", children: [
6893
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6894
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6895
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-diff-text", children: [
6634
6896
  parts.prefix,
6635
- parts.delMid && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("mark", { className: "rk-del", children: parts.delMid }),
6897
+ parts.delMid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("mark", { className: "rk-del", children: parts.delMid }),
6636
6898
  parts.suffix
6637
6899
  ] })
6638
6900
  ] }),
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: [
6901
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6902
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6903
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-diff-text", children: [
6642
6904
  parts.prefix,
6643
- parts.insMid && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("mark", { className: "rk-ins", children: parts.insMid }),
6905
+ parts.insMid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("mark", { className: "rk-ins", children: parts.insMid }),
6644
6906
  parts.suffix
6645
6907
  ] })
6646
6908
  ] }),
6647
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diff-len", children: [
6909
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-len", children: [
6648
6910
  before.length,
6649
6911
  " \u2192 ",
6650
6912
  after.length,
@@ -6655,8 +6917,8 @@ function CopyDiff({ before, after }) {
6655
6917
  }
6656
6918
  function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
6657
6919
  const replacement = afterDataUrl ?? afterSrc;
6658
- const [dims, setDims] = (0, import_react13.useState)("");
6659
- (0, import_react13.useEffect)(() => {
6920
+ const [dims, setDims] = (0, import_react14.useState)("");
6921
+ (0, import_react14.useEffect)(() => {
6660
6922
  if (!replacement) return;
6661
6923
  const img = new Image();
6662
6924
  img.onload = () => setDims(`${img.naturalWidth}\xD7${img.naturalHeight}px`);
@@ -6681,55 +6943,67 @@ function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
6681
6943
  window.open(replacement, "_blank", "noopener");
6682
6944
  }
6683
6945
  };
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" })
6946
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail", children: [
6947
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-thumbs", children: [
6948
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "rk-thumb", children: [
6949
+ 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" }),
6950
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { children: "Before" })
6689
6951
  ] }),
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: [
6952
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "rk-thumb", children: [
6953
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: replacement, alt: "Proposed replacement" }),
6954
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figcaption", { children: [
6693
6955
  "After",
6694
6956
  embedded ? "" : " \xB7 linked"
6695
6957
  ] })
6696
6958
  ] })
6697
6959
  ] }),
6698
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-imgdetail-meta", children: [
6960
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail-meta", children: [
6699
6961
  embedded ? "Embedded image" : "Linked image",
6700
6962
  dims ? ` \xB7 ${dims}` : "",
6701
6963
  bytes !== null ? ` \xB7 ${formatBytes(bytes)}` : ""
6702
6964
  ] }),
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 }),
6965
+ !embedded && afterSrc && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
6966
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail-actions", children: [
6967
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
6968
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconDownload, { size: 12 }),
6707
6969
  " Download"
6708
6970
  ] }),
6709
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
6971
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
6710
6972
  ] })
6711
6973
  ] });
6712
6974
  }
6975
+ var SYNC_REASON_LABEL = {
6976
+ "token-short": "token too short",
6977
+ "bad-token": "token rejected",
6978
+ "room-full": "room full",
6979
+ unreachable: "server unreachable"
6980
+ };
6981
+ var SYNC_REASON_HELP = {
6982
+ "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.",
6983
+ "bad-token": "The sync server rejected the token. Check `syncToken` / `token` in <ReviewKit />.",
6984
+ "room-full": "This page already has the maximum number of reviewers online (16). Try again later.",
6985
+ 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."
6986
+ };
6713
6987
  function Panel() {
6714
- const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, emblemaPush, peers } = useStore();
6988
+ const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, syncReason, emblemaPush, peers } = useStore();
6715
6989
  const dispatch = useDispatch();
6716
6990
  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)(() => {
6991
+ const [sticky, setStickyState] = (0, import_react14.useState)(() => isStickyEnabled(config.token));
6992
+ const [query, setQuery] = (0, import_react14.useState)("");
6993
+ const [typeFilter, setTypeFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
6994
+ const [prioFilter, setPrioFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
6995
+ const [statusFilter, setStatusFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
6996
+ const [filterOpen, setFilterOpen] = (0, import_react14.useState)(false);
6997
+ const [settingsOpen, setSettingsOpen] = (0, import_react14.useState)(false);
6998
+ const [emblemaAlive, setEmblemaAlive] = (0, import_react14.useState)(null);
6999
+ const [emblemaSending, setEmblemaSending] = (0, import_react14.useState)(false);
7000
+ const [emblemaSync, setEmblemaSync] = (0, import_react14.useState)("unknown");
7001
+ const panelRef = (0, import_react14.useRef)(null);
7002
+ const searchRef = (0, import_react14.useRef)(null);
7003
+ const importRef = (0, import_react14.useRef)(null);
7004
+ const sheetDrag = (0, import_react14.useRef)(null);
7005
+ const items = (0, import_react14.useMemo)(() => session?.items ?? [], [session]);
7006
+ const { orphanIds, hiddenIds } = (0, import_react14.useMemo)(() => {
6733
7007
  const orphans = /* @__PURE__ */ new Set();
6734
7008
  const hiddens = /* @__PURE__ */ new Set();
6735
7009
  for (const item of items) {
@@ -6740,15 +7014,15 @@ function Panel() {
6740
7014
  }
6741
7015
  return { orphanIds: orphans, hiddenIds: hiddens };
6742
7016
  }, [items, panelOpen]);
6743
- const activeFilter = (0, import_react13.useMemo)(
7017
+ const activeFilter = (0, import_react14.useMemo)(
6744
7018
  () => typeFilter.size + prioFilter.size + statusFilter.size > 0 ? { types: [...typeFilter], priorities: [...prioFilter], statuses: [...statusFilter] } : null,
6745
7019
  [typeFilter, prioFilter, statusFilter]
6746
7020
  );
6747
- (0, import_react13.useEffect)(() => {
7021
+ (0, import_react14.useEffect)(() => {
6748
7022
  dispatch({ type: "SET_FILTER", filter: activeFilter });
6749
7023
  return () => dispatch({ type: "SET_FILTER", filter: null });
6750
7024
  }, [activeFilter, dispatch]);
6751
- const filtered = (0, import_react13.useMemo)(() => {
7025
+ const filtered = (0, import_react14.useMemo)(() => {
6752
7026
  const q = query.trim().toLowerCase();
6753
7027
  return items.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => {
6754
7028
  if (!itemMatchesFilter(item, activeFilter)) return false;
@@ -6757,22 +7031,22 @@ function Panel() {
6757
7031
  return haystack.includes(q);
6758
7032
  });
6759
7033
  }, [items, query, activeFilter]);
6760
- const typeCounts = (0, import_react13.useMemo)(() => {
7034
+ const typeCounts = (0, import_react14.useMemo)(() => {
6761
7035
  const counts = /* @__PURE__ */ new Map();
6762
7036
  for (const item of items) counts.set(item.type, (counts.get(item.type) ?? 0) + 1);
6763
7037
  return counts;
6764
7038
  }, [items]);
6765
- const prioCounts = (0, import_react13.useMemo)(() => {
7039
+ const prioCounts = (0, import_react14.useMemo)(() => {
6766
7040
  const counts = /* @__PURE__ */ new Map();
6767
7041
  for (const item of items) counts.set(item.priority, (counts.get(item.priority) ?? 0) + 1);
6768
7042
  return counts;
6769
7043
  }, [items]);
6770
- const statusCounts = (0, import_react13.useMemo)(() => {
7044
+ const statusCounts = (0, import_react14.useMemo)(() => {
6771
7045
  const counts = /* @__PURE__ */ new Map();
6772
7046
  for (const item of items) counts.set(item.status, (counts.get(item.status) ?? 0) + 1);
6773
7047
  return counts;
6774
7048
  }, [items]);
6775
- const groups = (0, import_react13.useMemo)(() => {
7049
+ const groups = (0, import_react14.useMemo)(() => {
6776
7050
  const map = /* @__PURE__ */ new Map();
6777
7051
  for (const entry of filtered) {
6778
7052
  const list = map.get(entry.item.route) ?? [];
@@ -6781,7 +7055,7 @@ function Panel() {
6781
7055
  }
6782
7056
  return Array.from(map.entries());
6783
7057
  }, [filtered]);
6784
- (0, import_react13.useEffect)(() => {
7058
+ (0, import_react14.useEffect)(() => {
6785
7059
  if (!filterOpen && !settingsOpen) return;
6786
7060
  const onDown = (event) => {
6787
7061
  const path = event.composedPath();
@@ -6792,7 +7066,7 @@ function Panel() {
6792
7066
  document.addEventListener("pointerdown", onDown, true);
6793
7067
  return () => document.removeEventListener("pointerdown", onDown, true);
6794
7068
  }, [filterOpen, settingsOpen]);
6795
- (0, import_react13.useEffect)(() => {
7069
+ (0, import_react14.useEffect)(() => {
6796
7070
  if (!settingsOpen) return;
6797
7071
  let alive = true;
6798
7072
  const probe = () => {
@@ -6816,7 +7090,7 @@ function Panel() {
6816
7090
  live: "paired",
6817
7091
  off: "not paired"
6818
7092
  }[emblemaPairState];
6819
- (0, import_react13.useEffect)(() => {
7093
+ (0, import_react14.useEffect)(() => {
6820
7094
  if (!panelOpen || activeItemId === null) return;
6821
7095
  const row = panelRef.current?.querySelector(`[data-item-id="${activeItemId}"]`);
6822
7096
  row?.scrollIntoView({ block: "nearest" });
@@ -6932,7 +7206,7 @@ function Panel() {
6932
7206
  panel.style.transform = "";
6933
7207
  if (dy > 110) dispatch({ type: "SET_PANEL", open: false });
6934
7208
  };
6935
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7209
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6936
7210
  "div",
6937
7211
  {
6938
7212
  ref: panelRef,
@@ -6942,7 +7216,7 @@ function Panel() {
6942
7216
  "aria-label": "Review feedback panel",
6943
7217
  onKeyDown,
6944
7218
  children: [
6945
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7219
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6946
7220
  "div",
6947
7221
  {
6948
7222
  className: "rk-sheet-handle",
@@ -6953,34 +7227,41 @@ function Panel() {
6953
7227
  onPointerCancel: onSheetHandleUp
6954
7228
  }
6955
7229
  ),
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: [
7230
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-head", children: [
7231
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7232
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-panel-title", children: "Feedback" }),
7233
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-sub", children: [
6960
7234
  items.length,
6961
7235
  " item",
6962
7236
  items.length === 1 ? "" : "s",
6963
7237
  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)(
7238
+ syncStatus !== "disabled" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7239
+ "span",
7240
+ {
7241
+ className: `rk-sync rk-sync-${syncStatus}${syncReason ? ` rk-sync-reason-${syncReason}` : ""}`,
7242
+ 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"],
7243
+ children: [
7244
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-sync-dot" }),
7245
+ 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"]}`
7246
+ ]
7247
+ }
7248
+ ),
7249
+ config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6969
7250
  "span",
6970
7251
  {
6971
7252
  className: `rk-sync rk-epush-${emblemaPush}`,
6972
7253
  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
7254
  children: [
6974
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-sync-dot" }),
7255
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-sync-dot" }),
6975
7256
  emblemaPush === "ok" ? "emblema" : emblemaPush === "sending" ? "emblema\u2026" : emblemaPush === "rejected" ? "emblema rejected" : "emblema offline"
6976
7257
  ]
6977
7258
  }
6978
7259
  )
6979
7260
  ] })
6980
7261
  ] }),
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)(
7262
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-spacer" }),
7263
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7264
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6984
7265
  "button",
6985
7266
  {
6986
7267
  type: "button",
@@ -6996,11 +7277,11 @@ function Panel() {
6996
7277
  void pingEmblema(config.emblema).then((port) => setEmblemaAlive(port !== null));
6997
7278
  }
6998
7279
  },
6999
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconGear, { size: 15 })
7280
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconGear, { size: 15 })
7000
7281
  }
7001
7282
  ),
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)(
7283
+ settingsOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
7284
+ config.token && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7004
7285
  "button",
7005
7286
  {
7006
7287
  type: "button",
@@ -7015,12 +7296,12 @@ function Panel() {
7015
7296
  toast(dispatch, next ? "Review stays on in this tab" : "Review active only via the link", "info");
7016
7297
  },
7017
7298
  children: [
7018
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
7299
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
7019
7300
  "Keep review on in this tab"
7020
7301
  ]
7021
7302
  }
7022
7303
  ),
7023
- config.emblema && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7304
+ config.emblema && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7024
7305
  "button",
7025
7306
  {
7026
7307
  type: "button",
@@ -7050,7 +7331,7 @@ function Panel() {
7050
7331
  }).finally(() => setEmblemaSending(false));
7051
7332
  },
7052
7333
  children: [
7053
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7334
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7054
7335
  "span",
7055
7336
  {
7056
7337
  className: "rk-emblema-dot",
@@ -7061,25 +7342,40 @@ function Panel() {
7061
7342
  ]
7062
7343
  }
7063
7344
  ),
7064
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7345
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7065
7346
  "button",
7066
7347
  {
7067
7348
  type: "button",
7068
7349
  role: "menuitem",
7069
7350
  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",
7351
+ 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
7352
  onClick: () => {
7072
7353
  setSettingsOpen(false);
7073
7354
  dispatch({ type: "SET_EMBLEMA_CONNECT", open: true });
7074
7355
  },
7075
7356
  children: [
7076
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
7357
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
7077
7358
  "Connect to Emblema\u2026",
7078
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: emblemaPairLabel })
7359
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: emblemaPairLabel })
7079
7360
  ]
7080
7361
  }
7081
7362
  ),
7082
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7363
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7364
+ "button",
7365
+ {
7366
+ type: "button",
7367
+ role: "menuitem",
7368
+ className: "rk-menu-item",
7369
+ 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",
7370
+ disabled: !resolveSyncToken(config),
7371
+ onClick: () => {
7372
+ setSettingsOpen(false);
7373
+ dispatch({ type: "SET_TERMINAL_CONNECT", open: true });
7374
+ },
7375
+ children: "Connect terminal / agent\u2026"
7376
+ }
7377
+ ),
7378
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7083
7379
  "button",
7084
7380
  {
7085
7381
  type: "button",
@@ -7092,31 +7388,31 @@ function Panel() {
7092
7388
  )
7093
7389
  ] })
7094
7390
  ] }),
7095
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7391
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7096
7392
  "button",
7097
7393
  {
7098
7394
  type: "button",
7099
7395
  className: "rk-icon-btn",
7100
7396
  "aria-label": "Keyboard shortcuts",
7101
7397
  onClick: () => dispatch({ type: "SET_CHEATSHEET", open: true }),
7102
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconKeyboard, { size: 15 })
7398
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconKeyboard, { size: 15 })
7103
7399
  }
7104
7400
  ),
7105
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7401
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7106
7402
  "button",
7107
7403
  {
7108
7404
  type: "button",
7109
7405
  className: "rk-icon-btn",
7110
7406
  "aria-label": "Close panel",
7111
7407
  onClick: () => dispatch({ type: "SET_PANEL", open: false }),
7112
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconClose, { size: 14 })
7408
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconClose, { size: 14 })
7113
7409
  }
7114
7410
  )
7115
7411
  ] }),
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)(
7412
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-view-row", children: [
7413
+ /* @__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" }) }),
7414
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
7415
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7120
7416
  "button",
7121
7417
  {
7122
7418
  type: "button",
@@ -7126,7 +7422,7 @@ function Panel() {
7126
7422
  children: "Original"
7127
7423
  }
7128
7424
  ),
7129
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7425
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7130
7426
  "button",
7131
7427
  {
7132
7428
  type: "button",
@@ -7138,8 +7434,8 @@ function Panel() {
7138
7434
  )
7139
7435
  ] })
7140
7436
  ] }),
7141
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-tools", children: [
7142
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7437
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-tools", children: [
7438
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7143
7439
  "button",
7144
7440
  {
7145
7441
  type: "button",
@@ -7150,15 +7446,15 @@ function Panel() {
7150
7446
  dispatch({ type: "SET_MODE", mode: "pick" });
7151
7447
  },
7152
7448
  children: [
7153
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconTarget, { size: 14 }),
7449
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTarget, { size: 14 }),
7154
7450
  " Pick an element"
7155
7451
  ]
7156
7452
  }
7157
7453
  ),
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)(
7454
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-search-row", children: [
7455
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-search", children: [
7456
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconSearch, { size: 13 }),
7457
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7162
7458
  "input",
7163
7459
  {
7164
7460
  ref: searchRef,
@@ -7170,8 +7466,8 @@ function Panel() {
7170
7466
  }
7171
7467
  )
7172
7468
  ] }),
7173
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7174
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7469
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7470
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7175
7471
  "button",
7176
7472
  {
7177
7473
  type: "button",
@@ -7183,15 +7479,15 @@ function Panel() {
7183
7479
  setSettingsOpen(false);
7184
7480
  },
7185
7481
  children: [
7186
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconFilter, { size: 13 }),
7482
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFilter, { size: 13 }),
7187
7483
  "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 })
7484
+ 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
7485
  ]
7190
7486
  }
7191
7487
  ),
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)(
7488
+ filterOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
7489
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Type" }),
7490
+ TYPE_FILTERS.map((type) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7195
7491
  "button",
7196
7492
  {
7197
7493
  type: "button",
@@ -7205,16 +7501,16 @@ function Panel() {
7205
7501
  setTypeFilter(next);
7206
7502
  },
7207
7503
  children: [
7208
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
7504
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
7209
7505
  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 })
7506
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: type }),
7507
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
7212
7508
  ]
7213
7509
  },
7214
7510
  type
7215
7511
  )),
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)(
7512
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Priority" }),
7513
+ PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7218
7514
  "button",
7219
7515
  {
7220
7516
  type: "button",
@@ -7228,15 +7524,15 @@ function Panel() {
7228
7524
  setPrioFilter(next);
7229
7525
  },
7230
7526
  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 })
7527
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
7528
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
7529
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
7234
7530
  ]
7235
7531
  },
7236
7532
  priority
7237
7533
  )),
7238
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-menu-title", children: "Status" }),
7239
- STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7534
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Status" }),
7535
+ STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7240
7536
  "button",
7241
7537
  {
7242
7538
  type: "button",
@@ -7250,15 +7546,15 @@ function Panel() {
7250
7546
  setStatusFilter(next);
7251
7547
  },
7252
7548
  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 })
7549
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
7550
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-status-dot rk-st-${status}` }),
7551
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: status }),
7552
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
7257
7553
  ]
7258
7554
  },
7259
7555
  status
7260
7556
  )),
7261
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7557
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7262
7558
  "button",
7263
7559
  {
7264
7560
  type: "button",
@@ -7278,24 +7574,24 @@ function Panel() {
7278
7574
  ] })
7279
7575
  ] })
7280
7576
  ] }),
7281
- preview && unapplied > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-unplaced", role: "status", children: [
7577
+ preview && unapplied > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-unplaced", role: "status", children: [
7282
7578
  unapplied,
7283
7579
  " saved change",
7284
7580
  unapplied === 1 ? "" : "s",
7285
7581
  " couldn\u2019t be placed on the current page \u2014 the page has changed since they were captured."
7286
7582
  ] }),
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 }),
7583
+ /* @__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: [
7584
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-empty-art", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTarget, { size: 22 }) }),
7585
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
7586
+ /* @__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." })
7587
+ ] }) : groups.map(([route, entries]) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7588
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-group-title", children: route }),
7293
7589
  entries.map(({ item, number }) => {
7294
7590
  const active2 = item.id === activeItemId;
7295
7591
  const orphan = orphanIds.has(item.id);
7296
7592
  const hiddenEl = hiddenIds.has(item.id);
7297
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7298
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7593
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7594
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7299
7595
  "div",
7300
7596
  {
7301
7597
  "data-item-id": item.id,
@@ -7310,18 +7606,18 @@ function Panel() {
7310
7606
  }
7311
7607
  },
7312
7608
  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: [
7609
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-num", children: number }),
7610
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-icon", children: iconForType(item.type) }),
7611
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-body", children: [
7612
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-snippet", children: [
7317
7613
  item.target.tagName,
7318
7614
  " \xB7 ",
7319
7615
  item.target.textSnippet || item.target.selector
7320
7616
  ] }),
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)(
7617
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-change", children: changeSummary(item) }),
7618
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-meta", children: [
7619
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
7620
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7325
7621
  "span",
7326
7622
  {
7327
7623
  className: "rk-author-dot",
@@ -7329,15 +7625,15 @@ function Panel() {
7329
7625
  }
7330
7626
  ),
7331
7627
  item.author?.name ?? "Unknown",
7332
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-time", children: [
7628
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-time", children: [
7333
7629
  " \xB7 ",
7334
7630
  timeAgo(item.createdAt)
7335
7631
  ] })
7336
7632
  ] }),
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)(
7633
+ item.priority === "must" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini rk-must", children: "must" }),
7634
+ item.status !== "open" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
7635
+ orphan && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
7636
+ hiddenEl && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7341
7637
  "span",
7342
7638
  {
7343
7639
  className: "rk-badge-mini rk-hidden-el",
@@ -7345,14 +7641,14 @@ function Panel() {
7345
7641
  children: "hidden"
7346
7642
  }
7347
7643
  ),
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: [
7644
+ item.note && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini", children: "note" }),
7645
+ 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
7646
  peer.name || "peer",
7351
7647
  " is writing\u2026"
7352
7648
  ] }, peer.id))
7353
7649
  ] })
7354
7650
  ] }),
7355
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-actions", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7651
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-actions", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7356
7652
  "button",
7357
7653
  {
7358
7654
  type: "button",
@@ -7367,15 +7663,15 @@ function Panel() {
7367
7663
  "info"
7368
7664
  );
7369
7665
  },
7370
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconTrash, { size: 13 })
7666
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTrash, { size: 13 })
7371
7667
  }
7372
7668
  ) })
7373
7669
  ]
7374
7670
  }
7375
7671
  ),
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)(
7672
+ active2 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-item-edit", children: [
7673
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
7674
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7379
7675
  "span",
7380
7676
  {
7381
7677
  className: "rk-author-dot",
@@ -7386,8 +7682,8 @@ function Panel() {
7386
7682
  " \xB7 ",
7387
7683
  timeAgo(item.createdAt)
7388
7684
  ] }),
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)(
7685
+ isCopyItem(item) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CopyDiff, { before: item.payload.before, after: item.payload.after }),
7686
+ isImageItem(item) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7391
7687
  ImageDetail,
7392
7688
  {
7393
7689
  beforeSrc: item.payload.beforeSrc,
@@ -7395,7 +7691,7 @@ function Panel() {
7395
7691
  afterDataUrl: item.payload.afterDataUrl
7396
7692
  }
7397
7693
  ),
7398
- isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7694
+ isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7399
7695
  "textarea",
7400
7696
  {
7401
7697
  className: "rk-textarea",
@@ -7405,7 +7701,7 @@ function Panel() {
7405
7701
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: withPayloadText(item, event.target.value) })
7406
7702
  }
7407
7703
  ),
7408
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7704
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7409
7705
  "input",
7410
7706
  {
7411
7707
  className: "rk-input",
@@ -7415,9 +7711,9 @@ function Panel() {
7415
7711
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: { note: event.target.value } })
7416
7712
  }
7417
7713
  ),
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)(
7714
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
7715
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
7716
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7421
7717
  "button",
7422
7718
  {
7423
7719
  type: "button",
@@ -7426,7 +7722,7 @@ function Panel() {
7426
7722
  children: "Must"
7427
7723
  }
7428
7724
  ),
7429
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7725
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7430
7726
  "button",
7431
7727
  {
7432
7728
  type: "button",
@@ -7436,7 +7732,7 @@ function Panel() {
7436
7732
  }
7437
7733
  )
7438
7734
  ] }),
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)(
7735
+ /* @__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
7736
  "button",
7441
7737
  {
7442
7738
  type: "button",
@@ -7451,22 +7747,22 @@ function Panel() {
7451
7747
  ] }, item.id);
7452
7748
  })
7453
7749
  ] }, 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 }),
7750
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-foot", children: [
7751
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
7752
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconDownload, { size: 13 }),
7457
7753
  " Export"
7458
7754
  ] }),
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 }),
7755
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
7756
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconCopyClipboard, { size: 13 }),
7461
7757
  " Copy"
7462
7758
  ] }),
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 }),
7759
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
7760
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconUpload, { size: 13 }),
7465
7761
  " Import"
7466
7762
  ] }),
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)(
7763
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-spacer" }),
7764
+ 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 }) }),
7765
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7470
7766
  "button",
7471
7767
  {
7472
7768
  type: "button",
@@ -7477,7 +7773,7 @@ function Panel() {
7477
7773
  children: "Clear"
7478
7774
  }
7479
7775
  ),
7480
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7776
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7481
7777
  "button",
7482
7778
  {
7483
7779
  type: "button",
@@ -7487,7 +7783,7 @@ function Panel() {
7487
7783
  children: "Exit review"
7488
7784
  }
7489
7785
  ),
7490
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7786
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7491
7787
  "input",
7492
7788
  {
7493
7789
  ref: importRef,
@@ -7508,8 +7804,8 @@ function Panel() {
7508
7804
  }
7509
7805
 
7510
7806
  // src/client/components/Presence.tsx
7511
- var import_react14 = require("react");
7512
- var import_jsx_runtime12 = require("react/jsx-runtime");
7807
+ var import_react15 = require("react");
7808
+ var import_jsx_runtime13 = require("react/jsx-runtime");
7513
7809
  function initials(name) {
7514
7810
  const parts = name.trim().split(/\s+/).filter(Boolean);
7515
7811
  if (parts.length === 0) return "\xB7";
@@ -7519,8 +7815,8 @@ function initials(name) {
7519
7815
  function PresenceStrip() {
7520
7816
  const { peers, syncStatus } = useStore();
7521
7817
  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)(
7818
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
7819
+ peers.slice(0, 5).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7524
7820
  "span",
7525
7821
  {
7526
7822
  className: "rk-avatar",
@@ -7530,7 +7826,7 @@ function PresenceStrip() {
7530
7826
  },
7531
7827
  peer.id
7532
7828
  )),
7533
- peers.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-avatar rk-avatar-more", children: [
7829
+ peers.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "rk-avatar rk-avatar-more", children: [
7534
7830
  "+",
7535
7831
  peers.length - 5
7536
7832
  ] })
@@ -7538,13 +7834,13 @@ function PresenceStrip() {
7538
7834
  }
7539
7835
  function PeerCursors() {
7540
7836
  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)(
7837
+ const ringRefs = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
7838
+ const cursorRefs = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
7839
+ const motion = (0, import_react15.useRef)(
7544
7840
  /* @__PURE__ */ new Map()
7545
7841
  );
7546
- const peersRef = (0, import_react14.useRef)(peers);
7547
- const seen = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
7842
+ const peersRef = (0, import_react15.useRef)(peers);
7843
+ const seen = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
7548
7844
  for (const peer of peers) {
7549
7845
  const sig = peer.cursor ? `${peer.cursor.x},${peer.cursor.y},${peer.cursor.ox},${peer.cursor.oy}` : "";
7550
7846
  if (seen.current.get(peer.id) !== sig) {
@@ -7554,7 +7850,7 @@ function PeerCursors() {
7554
7850
  }
7555
7851
  }
7556
7852
  peersRef.current = peers;
7557
- (0, import_react14.useEffect)(() => {
7853
+ (0, import_react15.useEffect)(() => {
7558
7854
  const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
7559
7855
  let raf = 0;
7560
7856
  let last = performance.now();
@@ -7659,8 +7955,8 @@ function PeerCursors() {
7659
7955
  return () => cancelAnimationFrame(raf);
7660
7956
  }, []);
7661
7957
  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)(
7958
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
7959
+ peers.map((peer) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7664
7960
  "div",
7665
7961
  {
7666
7962
  ref: (el) => {
@@ -7672,7 +7968,7 @@ function PeerCursors() {
7672
7968
  },
7673
7969
  `ring-${peer.id}`
7674
7970
  )),
7675
- peers.map((peer, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7971
+ peers.map((peer, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7676
7972
  "div",
7677
7973
  {
7678
7974
  ref: (el) => {
@@ -7682,8 +7978,8 @@ function PeerCursors() {
7682
7978
  className: "rk-peer-cursor",
7683
7979
  style: { opacity: 0 },
7684
7980
  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)(
7981
+ /* @__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" }) }),
7982
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7687
7983
  "span",
7688
7984
  {
7689
7985
  className: "rk-peer-name",
@@ -7702,19 +7998,19 @@ function PeerCursors() {
7702
7998
  }
7703
7999
 
7704
8000
  // src/client/components/QuickComment.tsx
7705
- var import_react15 = require("react");
7706
- var import_jsx_runtime13 = require("react/jsx-runtime");
8001
+ var import_react16 = require("react");
8002
+ var import_jsx_runtime14 = require("react/jsx-runtime");
7707
8003
  function QuickComment() {
7708
8004
  const { quick } = useStore();
7709
8005
  if (!quick) return null;
7710
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(QuickComposerCard, { quick }, quick.id);
8006
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(QuickComposerCard, { quick }, quick.id);
7711
8007
  }
7712
8008
  function QuickComposerCard({ quick }) {
7713
8009
  const { route, session } = useStore();
7714
8010
  const dispatch = useDispatch();
7715
- const [text, setText] = (0, import_react15.useState)("");
7716
- const fieldRef = (0, import_react15.useRef)(null);
7717
- (0, import_react15.useEffect)(() => {
8011
+ const [text, setText] = (0, import_react16.useState)("");
8012
+ const fieldRef = (0, import_react16.useRef)(null);
8013
+ (0, import_react16.useEffect)(() => {
7718
8014
  const field = fieldRef.current;
7719
8015
  if (!field) return;
7720
8016
  field.focus();
@@ -7767,18 +8063,18 @@ function QuickComposerCard({ quick }) {
7767
8063
  };
7768
8064
  const x = Math.min(Math.max(quick.x, 8), window.innerWidth - 288);
7769
8065
  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" }),
8066
+ 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: [
8067
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rk-quick-head", children: [
8068
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconComment, { size: 12 }),
8069
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Comment here" }),
8070
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "rk-hint", children: [
8071
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
7776
8072
  " save \xB7 ",
7777
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "rk-kbd", children: "esc" }),
8073
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "rk-kbd", children: "esc" }),
7778
8074
  " cancel"
7779
8075
  ] })
7780
8076
  ] }),
7781
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
8077
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7782
8078
  "textarea",
7783
8079
  {
7784
8080
  ref: fieldRef,
@@ -7794,8 +8090,8 @@ function QuickComposerCard({ quick }) {
7794
8090
  }
7795
8091
 
7796
8092
  // src/client/components/PickLayer.tsx
7797
- var import_react16 = require("react");
7798
- var import_jsx_runtime14 = require("react/jsx-runtime");
8093
+ var import_react17 = require("react");
8094
+ var import_jsx_runtime15 = require("react/jsx-runtime");
7799
8095
  function applyRect(el, rect, pad2 = 3) {
7800
8096
  if (!el) return;
7801
8097
  if (!rect) {
@@ -7810,14 +8106,14 @@ function applyRect(el, rect, pad2 = 3) {
7810
8106
  function PickLayer() {
7811
8107
  const { mode, selection, tool } = useStore();
7812
8108
  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);
8109
+ const [hovered, setHovered] = (0, import_react17.useState)(null);
8110
+ const outlineRef = (0, import_react17.useRef)(null);
8111
+ const labelRef = (0, import_react17.useRef)(null);
8112
+ const selectRef = (0, import_react17.useRef)(null);
8113
+ const hoveredRef = (0, import_react17.useRef)(null);
7818
8114
  hoveredRef.current = hovered;
7819
8115
  const picking = mode === "pick";
7820
- (0, import_react16.useEffect)(() => {
8116
+ (0, import_react17.useEffect)(() => {
7821
8117
  if (!picking) {
7822
8118
  setHovered(null);
7823
8119
  return;
@@ -7861,7 +8157,7 @@ function PickLayer() {
7861
8157
  document.removeEventListener("mousedown", swallow, true);
7862
8158
  };
7863
8159
  }, [picking, tool, dispatch]);
7864
- (0, import_react16.useEffect)(() => {
8160
+ (0, import_react17.useEffect)(() => {
7865
8161
  if (!picking) return;
7866
8162
  const body = document.body;
7867
8163
  const prev = body.style.cursor;
@@ -7871,7 +8167,7 @@ function PickLayer() {
7871
8167
  if (body.getAttribute("style") === "") body.removeAttribute("style");
7872
8168
  };
7873
8169
  }, [picking, tool]);
7874
- (0, import_react16.useEffect)(() => {
8170
+ (0, import_react17.useEffect)(() => {
7875
8171
  let raf = 0;
7876
8172
  const tick = () => {
7877
8173
  const hoverEl = hoveredRef.current;
@@ -7897,21 +8193,21 @@ function PickLayer() {
7897
8193
  raf = requestAnimationFrame(tick);
7898
8194
  return () => cancelAnimationFrame(raf);
7899
8195
  }, [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" }) })
8196
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8197
+ picking && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8198
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
8199
+ /* @__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: [
8200
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
8201
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
8202
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Pick an element" }) })
7907
8203
  ] }),
7908
- selection && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
8204
+ selection && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
7909
8205
  ] });
7910
8206
  }
7911
8207
 
7912
8208
  // src/client/components/Pins.tsx
7913
- var import_react17 = require("react");
7914
- var import_jsx_runtime15 = require("react/jsx-runtime");
8209
+ var import_react18 = require("react");
8210
+ var import_jsx_runtime16 = require("react/jsx-runtime");
7915
8211
  var STATUS_LABELS = {
7916
8212
  open: "open",
7917
8213
  accepted: "accepted",
@@ -7923,22 +8219,22 @@ var RERESOLVE_MS = 800;
7923
8219
  function Pins() {
7924
8220
  const { session, route, activeItemId, editor, mode, filter } = useStore();
7925
8221
  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)(() => {
8222
+ const [clusters, setClusters] = (0, import_react18.useState)([]);
8223
+ const [orphanCount, setOrphanCount] = (0, import_react18.useState)(0);
8224
+ const wrapRefs = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8225
+ const visibleCache = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8226
+ const highlightRef = (0, import_react18.useRef)(null);
8227
+ const hoveredItems = (0, import_react18.useRef)(null);
8228
+ const elementCache = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8229
+ const lastResolve = (0, import_react18.useRef)(0);
8230
+ const [newestId, setNewestId] = (0, import_react18.useState)(null);
8231
+ const prevCount = (0, import_react18.useRef)(0);
8232
+ const items = (0, import_react18.useMemo)(() => {
7937
8233
  const all = session?.items ?? [];
7938
8234
  return all.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => item.route === route);
7939
8235
  }, [session, route]);
7940
8236
  const byId = new Map(items.map(({ item }) => [item.id, item]));
7941
- (0, import_react17.useEffect)(() => {
8237
+ (0, import_react18.useEffect)(() => {
7942
8238
  const count = session?.items.length ?? 0;
7943
8239
  if (count > prevCount.current && session && count > 0) {
7944
8240
  const id = session.items[count - 1].id;
@@ -7949,7 +8245,7 @@ function Pins() {
7949
8245
  }
7950
8246
  prevCount.current = count;
7951
8247
  }, [session]);
7952
- (0, import_react17.useEffect)(() => {
8248
+ (0, import_react18.useEffect)(() => {
7953
8249
  let raf = 0;
7954
8250
  let lastSignature = "";
7955
8251
  const tick = (time) => {
@@ -8033,8 +8329,8 @@ function Pins() {
8033
8329
  dispatch({ type: "SET_ACTIVE_ITEM", id });
8034
8330
  dispatch({ type: "SET_PANEL", open: true });
8035
8331
  };
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 } }),
8332
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
8333
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
8038
8334
  clusters.map((cluster) => {
8039
8335
  const isCluster = cluster.ids.length > 1;
8040
8336
  const isActive = activeItemId !== null && cluster.ids.includes(activeItemId);
@@ -8044,7 +8340,7 @@ function Pins() {
8044
8340
  const status = statuses.size === 1 ? members[0]?.status : void 0;
8045
8341
  const dimmed = filter !== null && members.length > 0 && !members.some((item) => itemMatchesFilter(item, filter));
8046
8342
  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)(
8343
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8048
8344
  "div",
8049
8345
  {
8050
8346
  ref: (el) => {
@@ -8052,7 +8348,7 @@ function Pins() {
8052
8348
  else wrapRefs.current.delete(cluster.key);
8053
8349
  },
8054
8350
  className: `rk-pin-wrap${picking ? " rk-pin-ghost" : ""}`,
8055
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8351
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8056
8352
  "button",
8057
8353
  {
8058
8354
  type: "button",
@@ -8074,7 +8370,7 @@ function Pins() {
8074
8370
  cluster.key
8075
8371
  );
8076
8372
  }),
8077
- orphanCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8373
+ orphanCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8078
8374
  "button",
8079
8375
  {
8080
8376
  type: "button",
@@ -8082,7 +8378,7 @@ function Pins() {
8082
8378
  style: { bottom: 16, left: "50%", transform: "translateX(-50%)" },
8083
8379
  onClick: () => dispatch({ type: "SET_PANEL", open: true }),
8084
8380
  children: [
8085
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconUnlink, { size: 13 }),
8381
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconUnlink, { size: 13 }),
8086
8382
  orphanCount,
8087
8383
  " unanchored item",
8088
8384
  orphanCount === 1 ? "" : "s"
@@ -8093,13 +8389,13 @@ function Pins() {
8093
8389
  }
8094
8390
 
8095
8391
  // src/client/components/Toolbar.tsx
8096
- var import_react18 = require("react");
8097
- var import_jsx_runtime16 = require("react/jsx-runtime");
8392
+ var import_react19 = require("react");
8393
+ var import_jsx_runtime17 = require("react/jsx-runtime");
8098
8394
  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 }) }
8395
+ { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconTarget, { size: n }) },
8396
+ { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconText, { size: n }) },
8397
+ { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconComment, { size: n }) },
8398
+ { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconImage, { size: n }) }
8103
8399
  ];
8104
8400
  var POS_KEY2 = "review-kit:toolbar-pos";
8105
8401
  var DRAG_THRESHOLD2 = 6;
@@ -8107,24 +8403,24 @@ function Toolbar() {
8107
8403
  const { selection, editor, peers, syncStatus, mode, tool } = useStore();
8108
8404
  const hasPeers = syncStatus !== "disabled" && peers.length > 0;
8109
8405
  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);
8406
+ const ref = (0, import_react19.useRef)(null);
8407
+ const [pos, setPos] = (0, import_react19.useState)(() => readStoredPos(POS_KEY2));
8408
+ const drag = (0, import_react19.useRef)(null);
8409
+ const [dragging, setDragging] = (0, import_react19.useState)(false);
8410
+ const justDragged = (0, import_react19.useRef)(false);
8115
8411
  const visible = editor === null || selection !== null;
8116
8412
  const applyPos = (next, persist) => {
8117
8413
  if (next === null && ref.current) ref.current.style.bottom = "";
8118
8414
  setPos(next);
8119
8415
  if (persist) storePos(POS_KEY2, next);
8120
8416
  };
8121
- (0, import_react18.useLayoutEffect)(() => {
8417
+ (0, import_react19.useLayoutEffect)(() => {
8122
8418
  const el = ref.current;
8123
8419
  if (!el || pos === null) return;
8124
8420
  const clamped = clampPos(pos.x, pos.y, el.offsetWidth, el.offsetHeight);
8125
8421
  if (clamped.x !== pos.x || clamped.y !== pos.y) applyPos(clamped, true);
8126
8422
  }, [pos, selection, hasPeers, visible]);
8127
- (0, import_react18.useEffect)(() => {
8423
+ (0, import_react19.useEffect)(() => {
8128
8424
  const onResize = () => {
8129
8425
  const el = ref.current;
8130
8426
  const current = readStoredPos(POS_KEY2);
@@ -8237,7 +8533,7 @@ function Toolbar() {
8237
8533
  mode === "pick" && selection === null ? "rk-ghost" : ""
8238
8534
  ].filter(Boolean).join(" ");
8239
8535
  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)(
8536
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8241
8537
  "div",
8242
8538
  {
8243
8539
  ref,
@@ -8248,7 +8544,7 @@ function Toolbar() {
8248
8544
  onPointerDown,
8249
8545
  onClickCapture,
8250
8546
  children: [
8251
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8547
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8252
8548
  "button",
8253
8549
  {
8254
8550
  type: "button",
@@ -8256,19 +8552,19 @@ function Toolbar() {
8256
8552
  "aria-label": "Move the toolbar \u2014 drag, or use arrow keys; double-click to reset its position",
8257
8553
  onKeyDown: onGripKeyDown,
8258
8554
  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" })
8555
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
8556
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "2", r: "1.1" }),
8557
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "2", r: "1.1" }),
8558
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "7", r: "1.1" }),
8559
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "7", r: "1.1" }),
8560
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "12", r: "1.1" }),
8561
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "12", r: "1.1" })
8266
8562
  ] })
8267
8563
  }
8268
8564
  ),
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)(
8565
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PresenceStrip, {}),
8566
+ hasPeers && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8567
+ /* @__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
8568
  "button",
8273
8569
  {
8274
8570
  type: "button",
@@ -8280,9 +8576,9 @@ function Toolbar() {
8280
8576
  },
8281
8577
  entry.tool
8282
8578
  )) }),
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)(
8579
+ selection && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8580
+ selection && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
8581
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8286
8582
  "button",
8287
8583
  {
8288
8584
  type: "button",
@@ -8291,12 +8587,12 @@ function Toolbar() {
8291
8587
  title: `Rewrite text${keyHint("tool-copy")}`,
8292
8588
  onClick: () => open("copy"),
8293
8589
  children: [
8294
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconText, { size: 14 }),
8590
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconText, { size: 14 }),
8295
8591
  " Rewrite"
8296
8592
  ]
8297
8593
  }
8298
8594
  ),
8299
- hasImage && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8595
+ hasImage && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8300
8596
  "button",
8301
8597
  {
8302
8598
  type: "button",
@@ -8305,12 +8601,12 @@ function Toolbar() {
8305
8601
  title: `Replace image${keyHint("tool-image")}`,
8306
8602
  onClick: () => open("image"),
8307
8603
  children: [
8308
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconImage, { size: 14 }),
8604
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconImage, { size: 14 }),
8309
8605
  " Image"
8310
8606
  ]
8311
8607
  }
8312
8608
  ),
8313
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8609
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8314
8610
  "button",
8315
8611
  {
8316
8612
  type: "button",
@@ -8319,32 +8615,32 @@ function Toolbar() {
8319
8615
  title: `Comment${keyHint("tool-comment")}`,
8320
8616
  onClick: () => open("comment"),
8321
8617
  children: [
8322
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconComment, { size: 14 }),
8618
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconComment, { size: 14 }),
8323
8619
  " Comment"
8324
8620
  ]
8325
8621
  }
8326
8622
  ),
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 }),
8623
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
8624
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconStyle, { size: 14 }),
8329
8625
  " Style"
8330
8626
  ] }),
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 }),
8627
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
8628
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconMove, { size: 14 }),
8333
8629
  " Move"
8334
8630
  ] }),
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 }),
8631
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
8632
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconTrash, { size: 14 }),
8337
8633
  " Delete"
8338
8634
  ] }),
8339
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "rk-toolbar-sep" }),
8340
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8635
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8636
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8341
8637
  "button",
8342
8638
  {
8343
8639
  type: "button",
8344
8640
  className: "rk-icon-btn",
8345
8641
  "aria-label": "Deselect",
8346
8642
  onClick: () => dispatch({ type: "SELECT", selection: null }),
8347
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconClose, { size: 13 })
8643
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconClose, { size: 13 })
8348
8644
  }
8349
8645
  )
8350
8646
  ] })
@@ -8354,40 +8650,40 @@ function Toolbar() {
8354
8650
  }
8355
8651
 
8356
8652
  // src/client/components/Toast.tsx
8357
- var import_react19 = require("react");
8358
- var import_jsx_runtime17 = require("react/jsx-runtime");
8653
+ var import_react20 = require("react");
8654
+ var import_jsx_runtime18 = require("react/jsx-runtime");
8359
8655
  function ToastRow({ toast: toast2 }) {
8360
8656
  const dispatch = useDispatch();
8361
- (0, import_react19.useEffect)(() => {
8657
+ (0, import_react20.useEffect)(() => {
8362
8658
  const timer = window.setTimeout(() => dispatch({ type: "DISMISS_TOAST", id: toast2.id }), 3500);
8363
8659
  return () => window.clearTimeout(timer);
8364
8660
  }, [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 }),
8661
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
8662
+ 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
8663
  toast2.message
8368
8664
  ] });
8369
8665
  }
8370
8666
  function ToastHost() {
8371
8667
  const { toasts } = useStore();
8372
8668
  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)) });
8669
+ 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
8670
  }
8375
8671
 
8376
8672
  // src/client/App.tsx
8377
- var import_jsx_runtime18 = require("react/jsx-runtime");
8673
+ var import_jsx_runtime19 = require("react/jsx-runtime");
8378
8674
  function App() {
8379
8675
  const state = useStore();
8380
8676
  const rawDispatch = useDispatch();
8381
8677
  const config = useConfig();
8382
8678
  const dispatch = useRealtimeSync(state, rawDispatch, config, reducer);
8383
- const saveTimer = (0, import_react20.useRef)(null);
8679
+ const saveTimer = (0, import_react21.useRef)(null);
8384
8680
  const storageKey = config.storageKey ?? defaultStorageKey();
8385
8681
  useAppliedPreview(state, dispatch);
8386
8682
  useEmblemaAutoPush(state, dispatch, config);
8387
- (0, import_react20.useEffect)(() => {
8683
+ (0, import_react21.useEffect)(() => {
8388
8684
  setEffectAssetsUrl(config.fxAssetsUrl);
8389
8685
  }, [config.fxAssetsUrl]);
8390
- (0, import_react20.useEffect)(() => {
8686
+ (0, import_react21.useEffect)(() => {
8391
8687
  let cancelled = false;
8392
8688
  void loadSession(storageKey).then((stored) => {
8393
8689
  if (cancelled) return;
@@ -8405,7 +8701,7 @@ function App() {
8405
8701
  };
8406
8702
  }, [storageKey, config.defaultAuthor, dispatch]);
8407
8703
  const { session } = state;
8408
- (0, import_react20.useEffect)(() => {
8704
+ (0, import_react21.useEffect)(() => {
8409
8705
  if (!session) return;
8410
8706
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
8411
8707
  saveTimer.current = window.setTimeout(() => {
@@ -8416,9 +8712,9 @@ function App() {
8416
8712
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
8417
8713
  };
8418
8714
  }, [session, storageKey, config]);
8419
- (0, import_react20.useEffect)(() => trackPointer(), []);
8420
- const focusHandled = (0, import_react20.useRef)(false);
8421
- (0, import_react20.useEffect)(() => {
8715
+ (0, import_react21.useEffect)(() => trackPointer(), []);
8716
+ const focusHandled = (0, import_react21.useRef)(false);
8717
+ (0, import_react21.useEffect)(() => {
8422
8718
  if (!session || focusHandled.current) return;
8423
8719
  focusHandled.current = true;
8424
8720
  const id = takeFocusItem();
@@ -8443,7 +8739,7 @@ function App() {
8443
8739
  cancelled = true;
8444
8740
  };
8445
8741
  }, [session, dispatch]);
8446
- (0, import_react20.useEffect)(() => {
8742
+ (0, import_react21.useEffect)(() => {
8447
8743
  let last = window.location.pathname;
8448
8744
  const check = () => {
8449
8745
  if (window.location.pathname !== last) {
@@ -8458,9 +8754,9 @@ function App() {
8458
8754
  window.removeEventListener("popstate", check);
8459
8755
  };
8460
8756
  }, [dispatch]);
8461
- const stateRef = (0, import_react20.useRef)(state);
8757
+ const stateRef = (0, import_react21.useRef)(state);
8462
8758
  stateRef.current = state;
8463
- (0, import_react20.useEffect)(() => {
8759
+ (0, import_react21.useEffect)(() => {
8464
8760
  const onKeyDown = (event) => {
8465
8761
  const current = stateRef.current;
8466
8762
  for (const def of SHORTCUTS) {
@@ -8475,7 +8771,7 @@ function App() {
8475
8771
  document.addEventListener("keydown", onKeyDown);
8476
8772
  return () => document.removeEventListener("keydown", onKeyDown);
8477
8773
  }, [dispatch]);
8478
- (0, import_react20.useEffect)(() => {
8774
+ (0, import_react21.useEffect)(() => {
8479
8775
  if (!state.editor) return;
8480
8776
  const editorEl = state.editor.element;
8481
8777
  const onPointerDown = (event) => {
@@ -8488,32 +8784,33 @@ function App() {
8488
8784
  }, [state.editor, dispatch]);
8489
8785
  if (!session) return null;
8490
8786
  if (state.hidden) {
8491
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DispatchContext.Provider, { value: dispatch, children: null });
8787
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DispatchContext.Provider, { value: dispatch, children: null });
8492
8788
  }
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, {})
8789
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DispatchContext.Provider, { value: dispatch, children: [
8790
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PickLayer, {}),
8791
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PeerCursors, {}),
8792
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Toolbar, {}),
8793
+ state.editor?.kind === "copy" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(InlineEditor, { editor: state.editor }),
8794
+ state.editor?.kind === "image" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ImageEditor, { editor: state.editor }),
8795
+ state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(NoteComposer, { editor: state.editor }),
8796
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pins, {}),
8797
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(QuickComment, {}),
8798
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Panel, {}),
8799
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Badge, {}),
8800
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CoachMark, {}),
8801
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CheatSheet, {}),
8802
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(EmblemaConnect, {}),
8803
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TerminalConnect, {}),
8804
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConfirmHost, {}),
8805
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ToastHost, {})
8509
8806
  ] });
8510
8807
  }
8511
8808
 
8512
8809
  // src/client/ReviewKit.tsx
8513
- var import_jsx_runtime19 = require("react/jsx-runtime");
8810
+ var import_jsx_runtime20 = require("react/jsx-runtime");
8514
8811
  function ShadowHost({ config }) {
8515
- const [mount, setMount] = (0, import_react21.useState)(null);
8516
- (0, import_react21.useEffect)(() => {
8812
+ const [mount, setMount] = (0, import_react22.useState)(null);
8813
+ (0, import_react22.useEffect)(() => {
8517
8814
  const host = document.createElement("div");
8518
8815
  host.setAttribute(HOST_ATTR, "");
8519
8816
  host.style.position = "fixed";
@@ -8549,15 +8846,15 @@ function ShadowHost({ config }) {
8549
8846
  }, []);
8550
8847
  if (!mount) return null;
8551
8848
  return (0, import_react_dom.createPortal)(
8552
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StoreProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(App, {}) }),
8849
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StoreProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(App, {}) }),
8553
8850
  mount
8554
8851
  );
8555
8852
  }
8556
8853
  function ReviewKit(props) {
8557
- const [active2, setActive] = (0, import_react21.useState)(false);
8854
+ const [active2, setActive] = (0, import_react22.useState)(false);
8558
8855
  const { enabled, token, allowedHosts, devAutoOn } = props;
8559
8856
  const hostsKey = allowedHosts?.join("\0") ?? "";
8560
- (0, import_react21.useEffect)(() => {
8857
+ (0, import_react22.useEffect)(() => {
8561
8858
  if (enabled === false) {
8562
8859
  setActive(false);
8563
8860
  return;
@@ -8568,7 +8865,7 @@ function ReviewKit(props) {
8568
8865
  return () => window.removeEventListener(DEACTIVATE_EVENT, onDeactivate);
8569
8866
  }, [enabled, token, devAutoOn, hostsKey]);
8570
8867
  if (enabled === false || !active2) return null;
8571
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ShadowHost, { config: props });
8868
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ShadowHost, { config: props });
8572
8869
  }
8573
8870
  // Annotate the CommonJS export names for ESM import in node:
8574
8871
  0 && (module.exports = {