@satanwagen/reviewkit 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -340,7 +340,7 @@ function importSessionFile(file) {
340
340
  }
341
341
 
342
342
  // src/client/ReviewKit.tsx
343
- import { useEffect as useEffect17, useState as useState13 } from "react";
343
+ import { useEffect as useEffect18, useState as useState14 } from "react";
344
344
  import { createPortal } from "react-dom";
345
345
 
346
346
  // src/client/env.ts
@@ -1234,6 +1234,8 @@ input, textarea, select {
1234
1234
  .rk-sync-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--rk-fg-faint); }
1235
1235
  .rk-sync-online .rk-sync-dot { background: var(--rk-success); }
1236
1236
  .rk-sync-connecting .rk-sync-dot { background: var(--rk-warn); }
1237
+ .rk-sync-reason-token-short .rk-sync-dot, .rk-sync-reason-bad-token .rk-sync-dot { background: var(--rk-danger); }
1238
+ .rk-sync[title] { cursor: help; }
1237
1239
  /* Automatic Emblema push hint (same quiet language) */
1238
1240
  .rk-epush-ok .rk-sync-dot { background: var(--rk-success); }
1239
1241
  .rk-epush-sending .rk-sync-dot { background: var(--rk-warn); }
@@ -2306,7 +2308,8 @@ function reducer(state, action) {
2306
2308
  panelOpen: false,
2307
2309
  confirm: null,
2308
2310
  cheatsheetOpen: false,
2309
- emblemaConnectOpen: false
2311
+ emblemaConnectOpen: false,
2312
+ terminalConnectOpen: false
2310
2313
  } : { ...state, hidden: false };
2311
2314
  case "SELECT":
2312
2315
  return { ...state, selection: action.selection };
@@ -2416,12 +2419,16 @@ function reducer(state, action) {
2416
2419
  return { ...state, cheatsheetOpen: action.open };
2417
2420
  case "SET_EMBLEMA_CONNECT":
2418
2421
  return { ...state, emblemaConnectOpen: action.open };
2422
+ case "SET_TERMINAL_CONNECT":
2423
+ return { ...state, terminalConnectOpen: action.open };
2419
2424
  case "SET_COACH":
2420
2425
  return { ...state, coachOpen: action.open };
2421
2426
  case "SET_CORNER":
2422
2427
  return { ...state, badgeCorner: action.corner };
2423
- case "SYNC_STATUS":
2424
- return state.syncStatus === action.status ? state : { ...state, syncStatus: action.status };
2428
+ case "SYNC_STATUS": {
2429
+ const reason = action.reason === void 0 ? action.status === "online" ? null : state.syncReason : action.reason;
2430
+ return state.syncStatus === action.status && state.syncReason === reason ? state : { ...state, syncStatus: action.status, syncReason: reason };
2431
+ }
2425
2432
  case "EMBLEMA_PUSH_STATUS":
2426
2433
  return state.emblemaPush === action.status ? state : { ...state, emblemaPush: action.status };
2427
2434
  /*
@@ -2550,9 +2557,11 @@ function initialState() {
2550
2557
  confirm: null,
2551
2558
  cheatsheetOpen: false,
2552
2559
  emblemaConnectOpen: false,
2560
+ terminalConnectOpen: false,
2553
2561
  coachOpen: false,
2554
2562
  undoStack: [],
2555
2563
  syncStatus: "disabled",
2564
+ syncReason: null,
2556
2565
  emblemaPush: "idle",
2557
2566
  peers: [],
2558
2567
  quick: null,
@@ -2567,7 +2576,7 @@ function StoreProvider({ config, children }) {
2567
2576
  const configValue = useMemo(
2568
2577
  () => config,
2569
2578
  // eslint-disable-next-line react-hooks/exhaustive-deps -- config object identity is per-render; depend on its fields
2570
- [config.enabled, config.token, config.devAutoOn, config.syncToken, config.syncUrl, config.defaultAuthor, config.fxAssetsUrl, config.position, config.storageKey, config.onSessionChange, config.emblema]
2579
+ [config.enabled, config.token, config.devAutoOn, config.sync, config.syncToken, config.syncUrl, config.defaultAuthor, config.fxAssetsUrl, config.position, config.storageKey, config.onSessionChange, config.emblema]
2571
2580
  );
2572
2581
  return /* @__PURE__ */ jsx(ConfigContext.Provider, { value: configValue, children: /* @__PURE__ */ jsx(StateContext.Provider, { value: state, children: /* @__PURE__ */ jsx(DispatchContext.Provider, { value: dispatch, children }) }) });
2573
2582
  }
@@ -2596,7 +2605,7 @@ function toast(dispatch, message, tone = "info") {
2596
2605
  }
2597
2606
 
2598
2607
  // src/client/App.tsx
2599
- import { useEffect as useEffect16, useRef as useRef16 } from "react";
2608
+ import { useEffect as useEffect17, useRef as useRef16 } from "react";
2600
2609
 
2601
2610
  // src/client/persist.ts
2602
2611
  var DB_NAME = "review-kit";
@@ -3532,9 +3541,13 @@ function createRkSync(options) {
3532
3541
  pongTimer = null;
3533
3542
  }
3534
3543
  break;
3535
- case "error":
3544
+ case "error": {
3536
3545
  log("server error:", msg.code, msg.message);
3546
+ const code = typeof msg.code === "string" ? msg.code : "error";
3547
+ const message = typeof msg.message === "string" ? msg.message : "";
3548
+ safely(() => handlers.onError?.(code, message));
3537
3549
  break;
3550
+ }
3538
3551
  case "presence:join": {
3539
3552
  const peer = msg.peer;
3540
3553
  if (peer && typeof peer.id === "string") safely(() => handlers.onPeerJoin?.(peer));
@@ -3736,6 +3749,23 @@ function currentAuthor(name) {
3736
3749
  }
3737
3750
 
3738
3751
  // src/client/sync.ts
3752
+ var DEFAULT_SYNC_URL = "wss://2-59-219-26.sslip.io/rk-sync";
3753
+ function normalizeRoute(route) {
3754
+ let r = String(route ?? "/");
3755
+ if (!r.startsWith("/")) r = "/" + r;
3756
+ return r.length > 1 ? r.replace(/\/+$/, "") : r;
3757
+ }
3758
+ function resolveSyncUrl(config) {
3759
+ return typeof config.syncUrl === "string" && config.syncUrl !== "" ? config.syncUrl : DEFAULT_SYNC_URL;
3760
+ }
3761
+ var MIN_SYNC_TOKEN_LENGTH = 8;
3762
+ var warnedShortToken = false;
3763
+ function resolveSyncToken(config) {
3764
+ if (config.sync === false) return null;
3765
+ const explicit = typeof config.syncToken === "string" && config.syncToken !== "" ? config.syncToken : null;
3766
+ const fallback = typeof config.token === "string" && config.token !== "" ? config.token : null;
3767
+ return explicit ?? fallback;
3768
+ }
3739
3769
  function toPeerState(peer) {
3740
3770
  return {
3741
3771
  id: peer.id,
@@ -3763,20 +3793,43 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
3763
3793
  const stateRef = useRef3(state);
3764
3794
  stateRef.current = state;
3765
3795
  const myIdRef = useRef3("");
3766
- const enabled = typeof config.syncToken === "string" && config.syncToken !== "" && typeof config.syncUrl === "string" && config.syncUrl !== "";
3796
+ const syncToken = resolveSyncToken(config);
3797
+ const syncUrl = resolveSyncUrl(config);
3798
+ const enabled = syncToken !== null;
3767
3799
  const author = state.session?.author ?? "";
3768
3800
  const hasSession = state.session !== null;
3769
3801
  useEffect3(() => {
3770
3802
  if (!enabled || !hasSession) return;
3803
+ const dispatchGuard = rawDispatch;
3804
+ if (syncToken.length < MIN_SYNC_TOKEN_LENGTH) {
3805
+ dispatchGuard({ type: "SYNC_STATUS", status: "offline", reason: "token-short" });
3806
+ if (!warnedShortToken) {
3807
+ warnedShortToken = true;
3808
+ console.warn(
3809
+ `[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.`
3810
+ );
3811
+ }
3812
+ return () => dispatchGuard({ type: "SYNC_STATUS", status: "disabled", reason: null });
3813
+ }
3771
3814
  const id = reviewerId();
3772
3815
  myIdRef.current = id;
3773
3816
  const dispatch = rawDispatch;
3774
3817
  const sync = createRkSync({
3775
- token: config.syncToken,
3776
- url: config.syncUrl,
3818
+ token: syncToken,
3819
+ url: syncUrl,
3777
3820
  user: { id, name: author || "Reviewer", color: colorForReviewer(id) },
3778
3821
  handlers: {
3779
- onStatus: (status) => dispatch({ type: "SYNC_STATUS", status }),
3822
+ onStatus: (status) => dispatch({
3823
+ type: "SYNC_STATUS",
3824
+ status,
3825
+ // Offline with no server verdict = the socket never got through
3826
+ // (server down, CSP connect-src, network); a verdict keeps its reason.
3827
+ ...status === "offline" ? { reason: stateRef.current.syncReason ?? "unreachable" } : {}
3828
+ }),
3829
+ onError: (code) => {
3830
+ if (code === "bad-token") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "bad-token" });
3831
+ else if (code === "room-full") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "room-full" });
3832
+ },
3780
3833
  onSnapshot: (items, peers, deletedIds = []) => {
3781
3834
  const safeItems = items.map(sanitizeRemoteItem).filter((item) => item !== null);
3782
3835
  dispatch({ type: "SYNC_SNAPSHOT", items: safeItems });
@@ -3784,8 +3837,11 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
3784
3837
  for (const id2 of deletedIds) dispatch({ type: "REMOTE_DELETE", id: id2 });
3785
3838
  dispatch({ type: "PEERS_SET", peers: peers.filter((peer) => peer.id !== id).map(toPeerState) });
3786
3839
  const serverIds = new Set(items.map((item) => item.id));
3840
+ const here = normalizeRoute(window.location.pathname);
3787
3841
  for (const item of stateRef.current.session?.items ?? []) {
3788
- if (!serverIds.has(item.id) && !deleted.has(item.id)) sync.addItem(item);
3842
+ if (serverIds.has(item.id) || deleted.has(item.id)) continue;
3843
+ if (normalizeRoute(item.route) !== here) continue;
3844
+ sync.addItem(item);
3789
3845
  }
3790
3846
  },
3791
3847
  onPeerJoin: (peer) => dispatch({
@@ -3833,10 +3889,10 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
3833
3889
  if (activeSyncRef === sync) activeSyncRef = null;
3834
3890
  setEffectBroadcaster(null);
3835
3891
  sync.stop();
3836
- dispatch({ type: "SYNC_STATUS", status: "disabled" });
3892
+ dispatch({ type: "SYNC_STATUS", status: "disabled", reason: null });
3837
3893
  dispatch({ type: "PEERS_SET", peers: [] });
3838
3894
  };
3839
- }, [enabled, hasSession, config.syncToken, config.syncUrl, author, rawDispatch]);
3895
+ }, [enabled, hasSession, syncToken, syncUrl, author, rawDispatch]);
3840
3896
  const wrapped = useMemo2(() => {
3841
3897
  return (action) => {
3842
3898
  const prev = stateRef.current;
@@ -4352,10 +4408,10 @@ function EmblemaConnect() {
4352
4408
  state === null && /* @__PURE__ */ jsxs4("div", { className: "rk-modal-text", children: [
4353
4409
  "The local sync agent isn't running. In the project repo run:",
4354
4410
  /* @__PURE__ */ jsxs4("code", { className: "rk-connect-cmd", children: [
4355
- "npx @satanwagen/reviewkit-cli init --origin ",
4411
+ "npx -p @satanwagen/reviewkit-cli reviewkit-emblema init --origin ",
4356
4412
  window.location.origin
4357
4413
  ] }),
4358
- /* @__PURE__ */ jsx5("code", { className: "rk-connect-cmd", children: "npx @satanwagen/reviewkit-cli serve" }),
4414
+ /* @__PURE__ */ jsx5("code", { className: "rk-connect-cmd", children: "npx -p @satanwagen/reviewkit-cli reviewkit-emblema serve" }),
4359
4415
  "then reopen this dialog."
4360
4416
  ] }),
4361
4417
  state !== null && state !== "loading" && /* @__PURE__ */ jsxs4(Fragment2, { children: [
@@ -4442,15 +4498,229 @@ function EmblemaConnect() {
4442
4498
  ] });
4443
4499
  }
4444
4500
 
4501
+ // src/client/components/TerminalConnect.tsx
4502
+ import { useEffect as useEffect5, useState as useState3 } from "react";
4503
+
4504
+ // src/integrations/terminal.ts
4505
+ var TERMINAL_PAIR_PORTS = [48780, 48781, 48782, 48783];
4506
+ async function fetchJson(url, init, timeoutMs) {
4507
+ const abort = new AbortController();
4508
+ const timer = window.setTimeout(() => abort.abort(), timeoutMs);
4509
+ try {
4510
+ return await fetch(url, { ...init, signal: abort.signal });
4511
+ } finally {
4512
+ window.clearTimeout(timer);
4513
+ }
4514
+ }
4515
+ async function probeTerminal() {
4516
+ for (const port of TERMINAL_PAIR_PORTS) {
4517
+ try {
4518
+ const res = await fetchJson(`http://127.0.0.1:${port}/pair`, { method: "GET" }, 900);
4519
+ if (!res.ok) continue;
4520
+ const body = await res.json();
4521
+ if (typeof body === "object" && body !== null && body.app === "reviewkit-cli") {
4522
+ const b = body;
4523
+ return {
4524
+ port,
4525
+ cwd: typeof b.cwd === "string" ? b.cwd : void 0,
4526
+ projects: Array.isArray(b.projects) ? b.projects.filter((p) => typeof p === "string") : []
4527
+ };
4528
+ }
4529
+ } catch {
4530
+ }
4531
+ }
4532
+ return null;
4533
+ }
4534
+ async function pairTerminal(port, key) {
4535
+ try {
4536
+ const res = await fetchJson(
4537
+ `http://127.0.0.1:${port}/pair`,
4538
+ { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ key }) },
4539
+ 5e3
4540
+ );
4541
+ const body = await res.json().catch(() => null);
4542
+ if (res.ok && body?.ok) return { ok: true, name: body.name ?? "", repo: body.repo ?? null };
4543
+ return { ok: false, error: body?.error ?? `HTTP ${res.status}` };
4544
+ } catch {
4545
+ return { ok: false, error: "the terminal stopped answering" };
4546
+ }
4547
+ }
4548
+
4549
+ // src/client/terminalKey.ts
4550
+ var TERMINAL_KEY_PREFIX = "rkc_";
4551
+ var TERMINAL_KEY_VERSION = 1;
4552
+ function base64url(text) {
4553
+ const bytes = new TextEncoder().encode(text);
4554
+ let binary = "";
4555
+ for (const byte of bytes) binary += String.fromCharCode(byte);
4556
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4557
+ }
4558
+ function encodeTerminalKey(fields) {
4559
+ const payload = { v: TERMINAL_KEY_VERSION, ...fields };
4560
+ return TERMINAL_KEY_PREFIX + base64url(JSON.stringify(payload));
4561
+ }
4562
+ function currentTerminalKey(syncUrl, syncToken) {
4563
+ const origin = window.location.origin;
4564
+ let route = "/";
4565
+ try {
4566
+ route = window.location.pathname || "/";
4567
+ } catch {
4568
+ }
4569
+ return encodeTerminalKey({ name: window.location.hostname, origin, syncUrl, syncToken, route });
4570
+ }
4571
+
4572
+ // src/client/components/TerminalConnect.tsx
4573
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4574
+ function shortKey2(key) {
4575
+ return `${key.slice(0, 8)}\u2026${key.slice(-6)}`;
4576
+ }
4577
+ function shortPath(path) {
4578
+ if (!path) return "";
4579
+ const parts = path.split("/").filter(Boolean);
4580
+ return parts.length > 2 ? `\u2026/${parts.slice(-2).join("/")}` : path;
4581
+ }
4582
+ function TerminalConnect() {
4583
+ const { terminalConnectOpen } = useStore();
4584
+ const config = useConfig();
4585
+ const dispatch = useDispatch();
4586
+ const [phase, setPhase] = useState3("probing");
4587
+ const [probe, setProbe] = useState3(null);
4588
+ const [paired, setPaired] = useState3(null);
4589
+ const [showManual, setShowManual] = useState3(false);
4590
+ const syncToken = resolveSyncToken(config);
4591
+ const key = terminalConnectOpen && syncToken ? currentTerminalKey(resolveSyncUrl(config), syncToken) : null;
4592
+ useEffect5(() => {
4593
+ if (!terminalConnectOpen) return;
4594
+ let cancelled = false;
4595
+ setPhase("probing");
4596
+ setProbe(null);
4597
+ setPaired(null);
4598
+ setShowManual(false);
4599
+ const look = () => void probeTerminal().then((found) => {
4600
+ if (cancelled) return;
4601
+ setProbe(found);
4602
+ setPhase((current) => current === "pairing" || current === "paired" ? current : found ? "found" : "missing");
4603
+ });
4604
+ look();
4605
+ const timer = window.setInterval(look, 2e3);
4606
+ return () => {
4607
+ cancelled = true;
4608
+ window.clearInterval(timer);
4609
+ };
4610
+ }, [terminalConnectOpen]);
4611
+ if (!terminalConnectOpen) return null;
4612
+ const close = () => dispatch({ type: "SET_TERMINAL_CONNECT", open: false });
4613
+ const copy = async (value, what) => {
4614
+ try {
4615
+ await navigator.clipboard.writeText(value);
4616
+ toast(dispatch, `${what} copied`, "success");
4617
+ } catch {
4618
+ toast(dispatch, "Copy failed \u2014 select the text manually", "error");
4619
+ }
4620
+ };
4621
+ const connect = () => {
4622
+ if (!probe || !key) return;
4623
+ setPhase("pairing");
4624
+ void pairTerminal(probe.port, key).then((result) => {
4625
+ if (result.ok) {
4626
+ setPaired({ name: result.name, repo: result.repo });
4627
+ setPhase("paired");
4628
+ toast(dispatch, `Terminal paired as \u201C${result.name}\u201D`, "success");
4629
+ } else {
4630
+ setPhase("found");
4631
+ toast(dispatch, `Pairing failed: ${result.error}`, "error");
4632
+ }
4633
+ });
4634
+ };
4635
+ const connectCmd = key ? `npx -y @satanwagen/reviewkit-cli connect ${key}` : "";
4636
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4637
+ /* @__PURE__ */ jsx6("div", { className: "rk-backdrop", onClick: close }),
4638
+ /* @__PURE__ */ jsx6("div", { className: "rk-modal", role: "dialog", "aria-modal": "true", "aria-label": "Connect terminal or agent", children: /* @__PURE__ */ jsxs5("div", { className: "rk-modal-body", children: [
4639
+ /* @__PURE__ */ jsxs5("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4640
+ "Connect terminal / agent",
4641
+ /* @__PURE__ */ jsx6("span", { style: { flex: 1 } }),
4642
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-icon-btn", "aria-label": "Close", onClick: close, autoFocus: true, children: /* @__PURE__ */ jsx6(IconClose, { size: 13 }) })
4643
+ ] }),
4644
+ !key && /* @__PURE__ */ jsxs5("div", { className: "rk-modal-text", children: [
4645
+ "Live sync is off on this page (",
4646
+ /* @__PURE__ */ jsx6("code", { children: "sync=false" }),
4647
+ " or no token), so there is no shared room a terminal could read."
4648
+ ] }),
4649
+ key && phase === "paired" && paired && /* @__PURE__ */ jsxs5("div", { className: "rk-modal-text", children: [
4650
+ /* @__PURE__ */ jsx6("span", { className: "rk-emblema-dot", "data-state": "live" }),
4651
+ " Paired as ",
4652
+ /* @__PURE__ */ jsx6("strong", { children: paired.name }),
4653
+ paired.repo ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
4654
+ " in ",
4655
+ /* @__PURE__ */ jsx6("code", { children: shortPath(paired.repo) })
4656
+ ] }) : null,
4657
+ ". The terminal shows this page's comments live, and Claude Code / Cursor can read and resolve them through the ",
4658
+ /* @__PURE__ */ jsx6("code", { children: "reviewkit" }),
4659
+ " MCP tools."
4660
+ ] }),
4661
+ key && phase !== "paired" && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4662
+ /* @__PURE__ */ jsxs5("div", { className: "rk-modal-text", children: [
4663
+ phase === "probing" && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4664
+ "Looking for a ",
4665
+ /* @__PURE__ */ jsx6("code", { children: "reviewkit" }),
4666
+ " terminal on this machine\u2026"
4667
+ ] }),
4668
+ phase === "missing" && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4669
+ "No terminal is listening yet. In the site's repo run",
4670
+ /* @__PURE__ */ jsx6("code", { className: "rk-connect-cmd", children: "npx -y @satanwagen/reviewkit-cli setup" }),
4671
+ "and this dialog will pick it up by itself."
4672
+ ] }),
4673
+ (phase === "found" || phase === "pairing") && probe && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4674
+ /* @__PURE__ */ jsx6("span", { className: "rk-emblema-dot", "data-state": "live" }),
4675
+ " Terminal found",
4676
+ probe.cwd ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
4677
+ " in ",
4678
+ /* @__PURE__ */ jsx6("code", { children: shortPath(probe.cwd) })
4679
+ ] }) : null,
4680
+ ".",
4681
+ probe.projects.length > 0 ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
4682
+ " Already paired: ",
4683
+ probe.projects.join(", "),
4684
+ "."
4685
+ ] }) : null
4686
+ ] })
4687
+ ] }),
4688
+ /* @__PURE__ */ jsxs5("div", { className: "rk-connect-actions", children: [
4689
+ /* @__PURE__ */ jsx6(
4690
+ "button",
4691
+ {
4692
+ type: "button",
4693
+ className: "rk-btn rk-btn-primary",
4694
+ disabled: phase !== "found",
4695
+ onClick: connect,
4696
+ title: "Send this site's connection to the terminal \u2014 nothing to copy",
4697
+ children: phase === "pairing" ? "Connecting\u2026" : "Connect this terminal"
4698
+ }
4699
+ ),
4700
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => setShowManual((v) => !v), children: showManual ? "Hide manual option" : "Another machine\u2026" })
4701
+ ] }),
4702
+ showManual && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4703
+ /* @__PURE__ */ jsx6("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):" }),
4704
+ /* @__PURE__ */ jsxs5("code", { className: "rk-connect-cmd", title: connectCmd, children: [
4705
+ "npx -y @satanwagen/reviewkit-cli connect ",
4706
+ shortKey2(key)
4707
+ ] }),
4708
+ /* @__PURE__ */ jsx6("div", { className: "rk-connect-actions", children: /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void copy(connectCmd, "Command"), children: "Copy command" }) })
4709
+ ] })
4710
+ ] })
4711
+ ] }) })
4712
+ ] });
4713
+ }
4714
+
4445
4715
  // src/client/components/CoachMark.tsx
4446
- import { useState as useState3 } from "react";
4447
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4716
+ import { useState as useState4 } from "react";
4717
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4448
4718
  var COACH_KEY = "review-kit:coach-dismissed";
4449
4719
  function CoachMark() {
4450
4720
  const { coachOpen, session } = useStore();
4451
4721
  const dispatch = useDispatch();
4452
4722
  const config = useConfig();
4453
- const [name, setName] = useState3(session?.author ?? config.defaultAuthor ?? "");
4723
+ const [name, setName] = useState4(session?.author ?? config.defaultAuthor ?? "");
4454
4724
  if (!coachOpen) return null;
4455
4725
  const done = (startPicking) => {
4456
4726
  if (name.trim() !== "") dispatch({ type: "SET_AUTHOR", author: name.trim() });
@@ -4461,39 +4731,39 @@ function CoachMark() {
4461
4731
  }
4462
4732
  if (startPicking) dispatch({ type: "SET_MODE", mode: "pick" });
4463
4733
  };
4464
- return /* @__PURE__ */ jsxs5("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
4465
- /* @__PURE__ */ jsx6("div", { className: "rk-coach-accent" }),
4466
- /* @__PURE__ */ jsxs5("div", { className: "rk-coach-body", children: [
4467
- /* @__PURE__ */ jsxs5("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4468
- /* @__PURE__ */ jsx6(IconTarget, { size: 16 }),
4734
+ return /* @__PURE__ */ jsxs6("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
4735
+ /* @__PURE__ */ jsx7("div", { className: "rk-coach-accent" }),
4736
+ /* @__PURE__ */ jsxs6("div", { className: "rk-coach-body", children: [
4737
+ /* @__PURE__ */ jsxs6("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4738
+ /* @__PURE__ */ jsx7(IconTarget, { size: 16 }),
4469
4739
  " Review mode"
4470
4740
  ] }),
4471
- /* @__PURE__ */ jsx6("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." }),
4472
- /* @__PURE__ */ jsxs5("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
4473
- /* @__PURE__ */ jsxs5("span", { children: [
4474
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "T" }),
4741
+ /* @__PURE__ */ jsx7("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." }),
4742
+ /* @__PURE__ */ jsxs6("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
4743
+ /* @__PURE__ */ jsxs6("span", { children: [
4744
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "T" }),
4475
4745
  " rewrite"
4476
4746
  ] }),
4477
- /* @__PURE__ */ jsxs5("span", { children: [
4478
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "C" }),
4747
+ /* @__PURE__ */ jsxs6("span", { children: [
4748
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "C" }),
4479
4749
  " comment"
4480
4750
  ] }),
4481
- /* @__PURE__ */ jsxs5("span", { children: [
4482
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "I" }),
4751
+ /* @__PURE__ */ jsxs6("span", { children: [
4752
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "I" }),
4483
4753
  " image"
4484
4754
  ] }),
4485
- /* @__PURE__ */ jsxs5("span", { children: [
4486
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "V" }),
4755
+ /* @__PURE__ */ jsxs6("span", { children: [
4756
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "V" }),
4487
4757
  " select"
4488
4758
  ] }),
4489
- /* @__PURE__ */ jsxs5("span", { children: [
4490
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "?" }),
4759
+ /* @__PURE__ */ jsxs6("span", { children: [
4760
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "?" }),
4491
4761
  " all shortcuts"
4492
4762
  ] })
4493
4763
  ] }),
4494
- /* @__PURE__ */ jsxs5("div", { children: [
4495
- /* @__PURE__ */ jsx6("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
4496
- /* @__PURE__ */ jsx6(
4764
+ /* @__PURE__ */ jsxs6("div", { children: [
4765
+ /* @__PURE__ */ jsx7("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
4766
+ /* @__PURE__ */ jsx7(
4497
4767
  "input",
4498
4768
  {
4499
4769
  id: "rk-author",
@@ -4507,36 +4777,36 @@ function CoachMark() {
4507
4777
  }
4508
4778
  )
4509
4779
  ] }),
4510
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
4511
- /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
4512
- /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
4780
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
4781
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
4782
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
4513
4783
  ] })
4514
4784
  ] })
4515
4785
  ] });
4516
4786
  }
4517
4787
 
4518
4788
  // src/client/components/Confirm.tsx
4519
- import { useEffect as useEffect5, useRef as useRef5 } from "react";
4520
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4789
+ import { useEffect as useEffect6, useRef as useRef5 } from "react";
4790
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4521
4791
  function ConfirmHost() {
4522
4792
  const { confirm } = useStore();
4523
4793
  const dispatch = useDispatch();
4524
4794
  const cancelRef = useRef5(null);
4525
- useEffect5(() => {
4795
+ useEffect6(() => {
4526
4796
  if (confirm) cancelRef.current?.focus();
4527
4797
  }, [confirm]);
4528
4798
  if (!confirm) return null;
4529
4799
  const close = () => dispatch({ type: "SET_CONFIRM", confirm: null });
4530
- return /* @__PURE__ */ jsxs6(Fragment3, { children: [
4531
- /* @__PURE__ */ jsx7("div", { className: "rk-backdrop", onClick: close }),
4532
- /* @__PURE__ */ jsxs6("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
4533
- /* @__PURE__ */ jsxs6("div", { className: "rk-modal-body", children: [
4534
- /* @__PURE__ */ jsx7("div", { className: "rk-modal-title", children: confirm.title }),
4535
- /* @__PURE__ */ jsx7("div", { className: "rk-modal-text", children: confirm.message })
4800
+ return /* @__PURE__ */ jsxs7(Fragment4, { children: [
4801
+ /* @__PURE__ */ jsx8("div", { className: "rk-backdrop", onClick: close }),
4802
+ /* @__PURE__ */ jsxs7("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
4803
+ /* @__PURE__ */ jsxs7("div", { className: "rk-modal-body", children: [
4804
+ /* @__PURE__ */ jsx8("div", { className: "rk-modal-title", children: confirm.title }),
4805
+ /* @__PURE__ */ jsx8("div", { className: "rk-modal-text", children: confirm.message })
4536
4806
  ] }),
4537
- /* @__PURE__ */ jsxs6("div", { className: "rk-modal-actions", children: [
4538
- /* @__PURE__ */ jsx7("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
4539
- /* @__PURE__ */ jsx7(
4807
+ /* @__PURE__ */ jsxs7("div", { className: "rk-modal-actions", children: [
4808
+ /* @__PURE__ */ jsx8("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
4809
+ /* @__PURE__ */ jsx8(
4540
4810
  "button",
4541
4811
  {
4542
4812
  type: "button",
@@ -4555,10 +4825,10 @@ function ConfirmHost() {
4555
4825
  }
4556
4826
 
4557
4827
  // src/client/components/ImageEditor.tsx
4558
- import { useEffect as useEffect6, useRef as useRef7, useState as useState5 } from "react";
4828
+ import { useEffect as useEffect7, useRef as useRef7, useState as useState6 } from "react";
4559
4829
 
4560
4830
  // src/client/useAnchored.ts
4561
- import { useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState4 } from "react";
4831
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState5 } from "react";
4562
4832
 
4563
4833
  // src/client/position.ts
4564
4834
  function place(anchor, size, side, offset) {
@@ -4604,7 +4874,7 @@ function computePosition(anchor, size, options = {}) {
4604
4874
  // src/client/useAnchored.ts
4605
4875
  function useAnchored(anchorEl, side = "bottom", offset = 8) {
4606
4876
  const ref = useRef6(null);
4607
- const [pos, setPos] = useState4(null);
4877
+ const [pos, setPos] = useState5(null);
4608
4878
  useLayoutEffect2(() => {
4609
4879
  if (!anchorEl) {
4610
4880
  setPos(null);
@@ -4636,7 +4906,7 @@ function anchoredStyle(pos) {
4636
4906
  }
4637
4907
 
4638
4908
  // src/client/components/ImageEditor.tsx
4639
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4909
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4640
4910
  var MAX_BYTES = 2 * 1024 * 1024;
4641
4911
  var FETCH_TIMEOUT_MS = 12e3;
4642
4912
  var PROBE_TIMEOUT_MS = 8e3;
@@ -4694,12 +4964,12 @@ function urlFromDataTransfer(dt) {
4694
4964
  function ImageEditor({ editor }) {
4695
4965
  const dispatch = useDispatch();
4696
4966
  const { route, session } = useStore();
4697
- const [replacement, setReplacement] = useState5(null);
4698
- const [urlValue, setUrlValue] = useState5("");
4699
- const [loading2, setLoading] = useState5(false);
4700
- const [description, setDescription] = useState5("");
4701
- const [priority, setPriority] = useState5("nice");
4702
- const [over, setOver] = useState5(false);
4967
+ const [replacement, setReplacement] = useState6(null);
4968
+ const [urlValue, setUrlValue] = useState6("");
4969
+ const [loading2, setLoading] = useState6(false);
4970
+ const [description, setDescription] = useState6("");
4971
+ const [priority, setPriority] = useState6("nice");
4972
+ const [over, setOver] = useState6(false);
4703
4973
  const fileInput = useRef7(null);
4704
4974
  const abortRef = useRef7(null);
4705
4975
  const debounceRef = useRef7(null);
@@ -4766,7 +5036,7 @@ function ImageEditor({ editor }) {
4766
5036
  }
4767
5037
  }
4768
5038
  };
4769
- useEffect6(() => {
5039
+ useEffect7(() => {
4770
5040
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
4771
5041
  if (urlValue.trim() === "" || replacement !== null) return;
4772
5042
  debounceRef.current = window.setTimeout(() => {
@@ -4776,8 +5046,8 @@ function ImageEditor({ editor }) {
4776
5046
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
4777
5047
  };
4778
5048
  }, [urlValue, replacement]);
4779
- useEffect6(() => () => abortRef.current?.abort(), []);
4780
- useEffect6(() => {
5049
+ useEffect7(() => () => abortRef.current?.abort(), []);
5050
+ useEffect7(() => {
4781
5051
  const onPaste = (event) => {
4782
5052
  const data = event.clipboardData;
4783
5053
  if (!data) return;
@@ -4855,23 +5125,23 @@ function ImageEditor({ editor }) {
4855
5125
  toast(dispatch, "Image proposal saved", "success");
4856
5126
  };
4857
5127
  const previewSrc = replacement?.kind === "data" ? replacement.dataUrl : replacement?.url;
4858
- return /* @__PURE__ */ jsxs7("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
4859
- /* @__PURE__ */ jsxs7("div", { className: "rk-card-head", children: [
4860
- /* @__PURE__ */ jsx8(IconImage, { size: 14 }),
5128
+ return /* @__PURE__ */ jsxs8("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
5129
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-head", children: [
5130
+ /* @__PURE__ */ jsx9(IconImage, { size: 14 }),
4861
5131
  "Replace image"
4862
5132
  ] }),
4863
- /* @__PURE__ */ jsxs7("div", { className: "rk-card-body", children: [
4864
- replacement && previewSrc ? /* @__PURE__ */ jsx8("div", { className: "rk-thumbs", children: /* @__PURE__ */ jsxs7("div", { className: "rk-thumb-pair", title: "Hover to compare with the current image", children: [
4865
- /* @__PURE__ */ jsxs7("figure", { className: "rk-thumb", children: [
4866
- /* @__PURE__ */ jsx8("img", { src: beforeSrc, alt: "Current" }),
4867
- /* @__PURE__ */ jsx8("figcaption", { children: "Before" })
5133
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-body", children: [
5134
+ replacement && previewSrc ? /* @__PURE__ */ jsx9("div", { className: "rk-thumbs", children: /* @__PURE__ */ jsxs8("div", { className: "rk-thumb-pair", title: "Hover to compare with the current image", children: [
5135
+ /* @__PURE__ */ jsxs8("figure", { className: "rk-thumb", children: [
5136
+ /* @__PURE__ */ jsx9("img", { src: beforeSrc, alt: "Current" }),
5137
+ /* @__PURE__ */ jsx9("figcaption", { children: "Before" })
4868
5138
  ] }),
4869
- /* @__PURE__ */ jsxs7("figure", { className: "rk-thumb", children: [
4870
- /* @__PURE__ */ jsx8("img", { src: previewSrc, alt: "Proposed replacement" }),
4871
- /* @__PURE__ */ jsx8("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
5139
+ /* @__PURE__ */ jsxs8("figure", { className: "rk-thumb", children: [
5140
+ /* @__PURE__ */ jsx9("img", { src: previewSrc, alt: "Proposed replacement" }),
5141
+ /* @__PURE__ */ jsx9("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
4872
5142
  ] })
4873
- ] }) }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
4874
- /* @__PURE__ */ jsxs7(
5143
+ ] }) }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
5144
+ /* @__PURE__ */ jsxs8(
4875
5145
  "button",
4876
5146
  {
4877
5147
  type: "button",
@@ -4884,18 +5154,18 @@ function ImageEditor({ editor }) {
4884
5154
  onDragLeave: () => setOver(false),
4885
5155
  onDrop,
4886
5156
  children: [
4887
- /* @__PURE__ */ jsx8(IconUpload, { size: 18 }),
4888
- /* @__PURE__ */ jsxs7("span", { children: [
5157
+ /* @__PURE__ */ jsx9(IconUpload, { size: 18 }),
5158
+ /* @__PURE__ */ jsxs8("span", { children: [
4889
5159
  "Drop an image \u2014 from your disk or another tab \u2014",
4890
- /* @__PURE__ */ jsx8("br", {}),
5160
+ /* @__PURE__ */ jsx9("br", {}),
4891
5161
  "or click to browse (max 2 MB)"
4892
5162
  ] })
4893
5163
  ]
4894
5164
  }
4895
5165
  ),
4896
- /* @__PURE__ */ jsxs7("div", { className: "rk-url", children: [
4897
- loading2 ? /* @__PURE__ */ jsx8("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ jsx8(IconLink, { size: 13 }),
4898
- /* @__PURE__ */ jsx8(
5166
+ /* @__PURE__ */ jsxs8("div", { className: "rk-url", children: [
5167
+ loading2 ? /* @__PURE__ */ jsx9("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ jsx9(IconLink, { size: 13 }),
5168
+ /* @__PURE__ */ jsx9(
4899
5169
  "input",
4900
5170
  {
4901
5171
  className: "rk-input",
@@ -4910,9 +5180,9 @@ function ImageEditor({ editor }) {
4910
5180
  )
4911
5181
  ] })
4912
5182
  ] }),
4913
- replacement && /* @__PURE__ */ jsx8("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
4914
- /* @__PURE__ */ jsx8("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
4915
- /* @__PURE__ */ jsx8(
5183
+ replacement && /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
5184
+ /* @__PURE__ */ jsx9("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
5185
+ /* @__PURE__ */ jsx9(
4916
5186
  "textarea",
4917
5187
  {
4918
5188
  className: "rk-textarea",
@@ -4922,43 +5192,43 @@ function ImageEditor({ editor }) {
4922
5192
  rows: 2
4923
5193
  }
4924
5194
  ),
4925
- /* @__PURE__ */ jsxs7("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
4926
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
4927
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5195
+ /* @__PURE__ */ jsxs8("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5196
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5197
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
4928
5198
  ] })
4929
5199
  ] }),
4930
- /* @__PURE__ */ jsxs7("div", { className: "rk-card-foot", children: [
4931
- /* @__PURE__ */ jsxs7("span", { className: "rk-hint", children: [
4932
- /* @__PURE__ */ jsx8("span", { className: "rk-kbd", children: "esc" }),
5200
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-foot", children: [
5201
+ /* @__PURE__ */ jsxs8("span", { className: "rk-hint", children: [
5202
+ /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "esc" }),
4933
5203
  " cancel"
4934
5204
  ] }),
4935
- /* @__PURE__ */ jsxs7("div", { className: "rk-actions", children: [
4936
- /* @__PURE__ */ jsx8("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
4937
- /* @__PURE__ */ jsx8("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
5205
+ /* @__PURE__ */ jsxs8("div", { className: "rk-actions", children: [
5206
+ /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
5207
+ /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
4938
5208
  ] })
4939
5209
  ] })
4940
5210
  ] });
4941
5211
  }
4942
5212
 
4943
5213
  // src/client/components/InlineEditor.tsx
4944
- import { useEffect as useEffect7, useRef as useRef8, useState as useState6 } from "react";
4945
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
5214
+ import { useEffect as useEffect8, useRef as useRef8, useState as useState7 } from "react";
5215
+ import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
4946
5216
  function InlineEditor({ editor }) {
4947
5217
  const dispatch = useDispatch();
4948
5218
  const { route, session } = useStore();
4949
- const [after, setAfter] = useState6(() => editor.element.innerText);
4950
- const [priority, setPriority] = useState6("nice");
4951
- const [note, setNote] = useState6("");
5219
+ const [after, setAfter] = useState7(() => editor.element.innerText);
5220
+ const [priority, setPriority] = useState7("nice");
5221
+ const [note, setNote] = useState7("");
4952
5222
  const original = useRef8(null);
4953
5223
  const baseline = useRef8(null);
4954
- const [sizeDelta, setSizeDelta] = useState6(0);
4955
- const [widthDelta, setWidthDelta] = useState6(0);
4956
- const [clipped, setClipped] = useState6(false);
5224
+ const [sizeDelta, setSizeDelta] = useState7(0);
5225
+ const [widthDelta, setWidthDelta] = useState7(0);
5226
+ const [clipped, setClipped] = useState7(false);
4957
5227
  const ringRef = useRef8(null);
4958
5228
  const afterRef = useRef8(null);
4959
5229
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
4960
5230
  const element = editor.element;
4961
- useEffect7(() => {
5231
+ useEffect8(() => {
4962
5232
  const el = element;
4963
5233
  original.current = { html: el.innerHTML, text: el.innerText, styleAttr: el.getAttribute("style") };
4964
5234
  const rect = el.getBoundingClientRect();
@@ -4989,7 +5259,7 @@ function InlineEditor({ editor }) {
4989
5259
  };
4990
5260
  }, [element]);
4991
5261
  const focusedOnce = useRef8(false);
4992
- useEffect7(() => {
5262
+ useEffect8(() => {
4993
5263
  const field = afterRef.current;
4994
5264
  if (pos === null || focusedOnce.current || !field) return;
4995
5265
  focusedOnce.current = true;
@@ -4997,7 +5267,7 @@ function InlineEditor({ editor }) {
4997
5267
  const end = field.value.length;
4998
5268
  field.setSelectionRange(end, end);
4999
5269
  }, [pos]);
5000
- useEffect7(() => {
5270
+ useEffect8(() => {
5001
5271
  const field = afterRef.current;
5002
5272
  if (!field) return;
5003
5273
  field.style.height = "auto";
@@ -5007,7 +5277,7 @@ function InlineEditor({ editor }) {
5007
5277
  setAfter(value);
5008
5278
  if (element.innerText !== value) setElementText(element, value);
5009
5279
  };
5010
- useEffect7(() => {
5280
+ useEffect8(() => {
5011
5281
  let raf = 0;
5012
5282
  let lastDelta = 0;
5013
5283
  let lastWidth = 0;
@@ -5077,7 +5347,7 @@ function InlineEditor({ editor }) {
5077
5347
  cancel();
5078
5348
  }
5079
5349
  };
5080
- useEffect7(() => {
5350
+ useEffect8(() => {
5081
5351
  const el = element;
5082
5352
  const onKeyDown = (event) => {
5083
5353
  if (event.key === "Enter" && !event.shiftKey) {
@@ -5095,19 +5365,19 @@ function InlineEditor({ editor }) {
5095
5365
  });
5096
5366
  const before = original.current?.text ?? "";
5097
5367
  const delta = after.length - before.length;
5098
- return /* @__PURE__ */ jsxs8(Fragment5, { children: [
5099
- /* @__PURE__ */ jsx9("div", { ref: ringRef, className: "rk-edit-ring" }),
5100
- /* @__PURE__ */ jsxs8("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
5101
- /* @__PURE__ */ jsxs8("div", { className: "rk-card-head", children: [
5102
- /* @__PURE__ */ jsx9(IconText, { size: 14 }),
5368
+ return /* @__PURE__ */ jsxs9(Fragment6, { children: [
5369
+ /* @__PURE__ */ jsx10("div", { ref: ringRef, className: "rk-edit-ring" }),
5370
+ /* @__PURE__ */ jsxs9("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
5371
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-head", children: [
5372
+ /* @__PURE__ */ jsx10(IconText, { size: 14 }),
5103
5373
  "Rewrite text",
5104
- /* @__PURE__ */ jsx9("span", { className: "rk-spacer" }),
5105
- /* @__PURE__ */ jsxs8("span", { className: "rk-delta", "aria-live": "polite", children: [
5374
+ /* @__PURE__ */ jsx10("span", { className: "rk-spacer" }),
5375
+ /* @__PURE__ */ jsxs9("span", { className: "rk-delta", "aria-live": "polite", children: [
5106
5376
  before.length,
5107
5377
  " \u2192 ",
5108
5378
  after.length,
5109
5379
  " ",
5110
- delta !== 0 && /* @__PURE__ */ jsxs8("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
5380
+ delta !== 0 && /* @__PURE__ */ jsxs9("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
5111
5381
  "(",
5112
5382
  delta > 0 ? "+" : "",
5113
5383
  delta,
@@ -5115,15 +5385,15 @@ function InlineEditor({ editor }) {
5115
5385
  ] })
5116
5386
  ] })
5117
5387
  ] }),
5118
- /* @__PURE__ */ jsxs8("div", { className: "rk-card-body", children: [
5119
- /* @__PURE__ */ jsxs8("div", { className: "rk-diff", "aria-label": "Before and after", children: [
5120
- /* @__PURE__ */ jsxs8("div", { className: "rk-diff-row rk-diff-before", children: [
5121
- /* @__PURE__ */ jsx9("span", { className: "rk-diff-tag", children: "Before" }),
5122
- /* @__PURE__ */ jsx9("span", { className: "rk-diff-text", children: before })
5388
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-body", children: [
5389
+ /* @__PURE__ */ jsxs9("div", { className: "rk-diff", "aria-label": "Before and after", children: [
5390
+ /* @__PURE__ */ jsxs9("div", { className: "rk-diff-row rk-diff-before", children: [
5391
+ /* @__PURE__ */ jsx10("span", { className: "rk-diff-tag", children: "Before" }),
5392
+ /* @__PURE__ */ jsx10("span", { className: "rk-diff-text", children: before })
5123
5393
  ] }),
5124
- /* @__PURE__ */ jsxs8("div", { className: "rk-diff-row rk-diff-after", children: [
5125
- /* @__PURE__ */ jsx9("span", { className: "rk-diff-tag", children: "After" }),
5126
- /* @__PURE__ */ jsx9(
5394
+ /* @__PURE__ */ jsxs9("div", { className: "rk-diff-row rk-diff-after", children: [
5395
+ /* @__PURE__ */ jsx10("span", { className: "rk-diff-tag", children: "After" }),
5396
+ /* @__PURE__ */ jsx10(
5127
5397
  "textarea",
5128
5398
  {
5129
5399
  ref: afterRef,
@@ -5137,11 +5407,11 @@ function InlineEditor({ editor }) {
5137
5407
  )
5138
5408
  ] })
5139
5409
  ] }),
5140
- (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ jsxs8("div", { className: "rk-size-note", role: "status", children: [
5410
+ (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ jsxs9("div", { className: "rk-size-note", role: "status", children: [
5141
5411
  Math.abs(sizeDelta) > 2 && (() => {
5142
5412
  const lines = baseline.current ? Math.round(Math.abs(sizeDelta) / baseline.current.lineHeight) : 0;
5143
5413
  const lineNote = lines >= 1 ? ` (~${lines} line${lines === 1 ? "" : "s"})` : "";
5144
- return /* @__PURE__ */ jsxs8("span", { children: [
5414
+ return /* @__PURE__ */ jsxs9("span", { children: [
5145
5415
  "Block ",
5146
5416
  sizeDelta > 0 ? "grew" : "shrank",
5147
5417
  " by ",
@@ -5151,16 +5421,16 @@ function InlineEditor({ editor }) {
5151
5421
  " \u2014 as it would with this text live."
5152
5422
  ] });
5153
5423
  })(),
5154
- Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ jsxs8("span", { children: [
5424
+ Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ jsxs9("span", { children: [
5155
5425
  "Block got ",
5156
5426
  widthDelta > 0 ? "wider" : "narrower",
5157
5427
  " by ",
5158
5428
  Math.abs(widthDelta),
5159
5429
  " px \u2014 as it would with this text live."
5160
5430
  ] }),
5161
- clipped && /* @__PURE__ */ jsx9("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
5431
+ clipped && /* @__PURE__ */ jsx10("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
5162
5432
  ] }),
5163
- /* @__PURE__ */ jsx9(
5433
+ /* @__PURE__ */ jsx10(
5164
5434
  "input",
5165
5435
  {
5166
5436
  className: "rk-input",
@@ -5169,24 +5439,24 @@ function InlineEditor({ editor }) {
5169
5439
  onChange: (event) => setNote(event.target.value)
5170
5440
  }
5171
5441
  ),
5172
- /* @__PURE__ */ jsxs8("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5173
- /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5174
- /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5442
+ /* @__PURE__ */ jsxs9("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5443
+ /* @__PURE__ */ jsx10("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5444
+ /* @__PURE__ */ jsx10("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5175
5445
  ] })
5176
5446
  ] }),
5177
- /* @__PURE__ */ jsxs8("div", { className: "rk-card-foot", children: [
5178
- /* @__PURE__ */ jsxs8("span", { className: "rk-hint", children: [
5179
- /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "\u21B5" }),
5447
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-foot", children: [
5448
+ /* @__PURE__ */ jsxs9("span", { className: "rk-hint", children: [
5449
+ /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "\u21B5" }),
5180
5450
  " save \xB7 ",
5181
- /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
5451
+ /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
5182
5452
  " newline \xB7",
5183
5453
  " ",
5184
- /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "esc" }),
5454
+ /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "esc" }),
5185
5455
  " cancel"
5186
5456
  ] }),
5187
- /* @__PURE__ */ jsxs8("div", { className: "rk-actions", children: [
5188
- /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
5189
- /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
5457
+ /* @__PURE__ */ jsxs9("div", { className: "rk-actions", children: [
5458
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
5459
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
5190
5460
  ] })
5191
5461
  ] })
5192
5462
  ] })
@@ -5194,8 +5464,8 @@ function InlineEditor({ editor }) {
5194
5464
  }
5195
5465
 
5196
5466
  // src/client/components/NoteComposer.tsx
5197
- import { useEffect as useEffect8, useRef as useRef9, useState as useState7 } from "react";
5198
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
5467
+ import { useEffect as useEffect9, useRef as useRef9, useState as useState8 } from "react";
5468
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
5199
5469
  var TITLES = {
5200
5470
  comment: "Comment",
5201
5471
  delete: "Mark for deletion",
@@ -5211,14 +5481,14 @@ var PLACEHOLDERS = {
5211
5481
  function NoteComposer({ editor }) {
5212
5482
  const dispatch = useDispatch();
5213
5483
  const { route, session } = useStore();
5214
- const [text, setText] = useState7("");
5215
- const [direction, setDirection] = useState7(void 0);
5216
- const [priority, setPriority] = useState7("nice");
5484
+ const [text, setText] = useState8("");
5485
+ const [direction, setDirection] = useState8(void 0);
5486
+ const [priority, setPriority] = useState8("nice");
5217
5487
  const textareaRef = useRef9(null);
5218
5488
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
5219
5489
  const { kind } = editor;
5220
5490
  const focusedOnce = useRef9(false);
5221
- useEffect8(() => {
5491
+ useEffect9(() => {
5222
5492
  const field = textareaRef.current;
5223
5493
  if (pos === null || focusedOnce.current || !field) return;
5224
5494
  focusedOnce.current = true;
@@ -5277,13 +5547,13 @@ function NoteComposer({ editor }) {
5277
5547
  save();
5278
5548
  }
5279
5549
  };
5280
- return /* @__PURE__ */ jsxs9("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
5281
- /* @__PURE__ */ jsxs9("div", { className: "rk-card-head", children: [
5550
+ return /* @__PURE__ */ jsxs10("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
5551
+ /* @__PURE__ */ jsxs10("div", { className: "rk-card-head", children: [
5282
5552
  iconForType(kind),
5283
5553
  TITLES[kind]
5284
5554
  ] }),
5285
- /* @__PURE__ */ jsxs9("div", { className: "rk-card-body", children: [
5286
- kind === "move" && /* @__PURE__ */ jsx10("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ jsx10(
5555
+ /* @__PURE__ */ jsxs10("div", { className: "rk-card-body", children: [
5556
+ kind === "move" && /* @__PURE__ */ jsx11("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ jsx11(
5287
5557
  "button",
5288
5558
  {
5289
5559
  type: "button",
@@ -5293,7 +5563,7 @@ function NoteComposer({ editor }) {
5293
5563
  },
5294
5564
  option
5295
5565
  )) }),
5296
- /* @__PURE__ */ jsx10(
5566
+ /* @__PURE__ */ jsx11(
5297
5567
  "textarea",
5298
5568
  {
5299
5569
  ref: textareaRef,
@@ -5305,29 +5575,29 @@ function NoteComposer({ editor }) {
5305
5575
  rows: 3
5306
5576
  }
5307
5577
  ),
5308
- /* @__PURE__ */ jsxs9("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5309
- /* @__PURE__ */ jsx10("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5310
- /* @__PURE__ */ jsx10("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5578
+ /* @__PURE__ */ jsxs10("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5579
+ /* @__PURE__ */ jsx11("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5580
+ /* @__PURE__ */ jsx11("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5311
5581
  ] })
5312
5582
  ] }),
5313
- /* @__PURE__ */ jsxs9("div", { className: "rk-card-foot", children: [
5314
- /* @__PURE__ */ jsxs9("span", { className: "rk-hint", children: [
5315
- /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
5583
+ /* @__PURE__ */ jsxs10("div", { className: "rk-card-foot", children: [
5584
+ /* @__PURE__ */ jsxs10("span", { className: "rk-hint", children: [
5585
+ /* @__PURE__ */ jsx11("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
5316
5586
  " save \xB7 ",
5317
- /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "esc" }),
5587
+ /* @__PURE__ */ jsx11("span", { className: "rk-kbd", children: "esc" }),
5318
5588
  " cancel"
5319
5589
  ] }),
5320
- /* @__PURE__ */ jsxs9("div", { className: "rk-actions", children: [
5321
- /* @__PURE__ */ jsx10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
5322
- /* @__PURE__ */ jsx10("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
5590
+ /* @__PURE__ */ jsxs10("div", { className: "rk-actions", children: [
5591
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
5592
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
5323
5593
  ] })
5324
5594
  ] })
5325
5595
  ] });
5326
5596
  }
5327
5597
 
5328
5598
  // src/client/components/Panel.tsx
5329
- import { useEffect as useEffect9, useMemo as useMemo3, useRef as useRef10, useState as useState8 } from "react";
5330
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
5599
+ import { useEffect as useEffect10, useMemo as useMemo3, useRef as useRef10, useState as useState9 } from "react";
5600
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
5331
5601
  function changeSummary(item) {
5332
5602
  if (isCopyItem(item)) return `\u201C${item.payload.after}\u201D`;
5333
5603
  if (isImageItem(item)) {
@@ -5409,24 +5679,24 @@ function formatBytes(bytes) {
5409
5679
  function CopyDiff({ before, after }) {
5410
5680
  const parts = diffParts(before, after);
5411
5681
  const delta = after.length - before.length;
5412
- return /* @__PURE__ */ jsxs10("div", { className: "rk-diffview", children: [
5413
- /* @__PURE__ */ jsxs10("div", { className: "rk-diff-row rk-diff-before", children: [
5414
- /* @__PURE__ */ jsx11("span", { className: "rk-diff-tag", children: "Before" }),
5415
- /* @__PURE__ */ jsxs10("span", { className: "rk-diff-text", children: [
5682
+ return /* @__PURE__ */ jsxs11("div", { className: "rk-diffview", children: [
5683
+ /* @__PURE__ */ jsxs11("div", { className: "rk-diff-row rk-diff-before", children: [
5684
+ /* @__PURE__ */ jsx12("span", { className: "rk-diff-tag", children: "Before" }),
5685
+ /* @__PURE__ */ jsxs11("span", { className: "rk-diff-text", children: [
5416
5686
  parts.prefix,
5417
- parts.delMid && /* @__PURE__ */ jsx11("mark", { className: "rk-del", children: parts.delMid }),
5687
+ parts.delMid && /* @__PURE__ */ jsx12("mark", { className: "rk-del", children: parts.delMid }),
5418
5688
  parts.suffix
5419
5689
  ] })
5420
5690
  ] }),
5421
- /* @__PURE__ */ jsxs10("div", { className: "rk-diff-row rk-diff-after", children: [
5422
- /* @__PURE__ */ jsx11("span", { className: "rk-diff-tag", children: "After" }),
5423
- /* @__PURE__ */ jsxs10("span", { className: "rk-diff-text", children: [
5691
+ /* @__PURE__ */ jsxs11("div", { className: "rk-diff-row rk-diff-after", children: [
5692
+ /* @__PURE__ */ jsx12("span", { className: "rk-diff-tag", children: "After" }),
5693
+ /* @__PURE__ */ jsxs11("span", { className: "rk-diff-text", children: [
5424
5694
  parts.prefix,
5425
- parts.insMid && /* @__PURE__ */ jsx11("mark", { className: "rk-ins", children: parts.insMid }),
5695
+ parts.insMid && /* @__PURE__ */ jsx12("mark", { className: "rk-ins", children: parts.insMid }),
5426
5696
  parts.suffix
5427
5697
  ] })
5428
5698
  ] }),
5429
- /* @__PURE__ */ jsxs10("div", { className: "rk-diff-len", children: [
5699
+ /* @__PURE__ */ jsxs11("div", { className: "rk-diff-len", children: [
5430
5700
  before.length,
5431
5701
  " \u2192 ",
5432
5702
  after.length,
@@ -5437,8 +5707,8 @@ function CopyDiff({ before, after }) {
5437
5707
  }
5438
5708
  function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
5439
5709
  const replacement = afterDataUrl ?? afterSrc;
5440
- const [dims, setDims] = useState8("");
5441
- useEffect9(() => {
5710
+ const [dims, setDims] = useState9("");
5711
+ useEffect10(() => {
5442
5712
  if (!replacement) return;
5443
5713
  const img = new Image();
5444
5714
  img.onload = () => setDims(`${img.naturalWidth}\xD7${img.naturalHeight}px`);
@@ -5463,49 +5733,61 @@ function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
5463
5733
  window.open(replacement, "_blank", "noopener");
5464
5734
  }
5465
5735
  };
5466
- return /* @__PURE__ */ jsxs10("div", { className: "rk-imgdetail", children: [
5467
- /* @__PURE__ */ jsxs10("div", { className: "rk-thumbs", children: [
5468
- /* @__PURE__ */ jsxs10("figure", { className: "rk-thumb", children: [
5469
- beforeSrc ? /* @__PURE__ */ jsx11("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ jsx11("div", { className: "rk-thumb-missing", children: "no image" }),
5470
- /* @__PURE__ */ jsx11("figcaption", { children: "Before" })
5736
+ return /* @__PURE__ */ jsxs11("div", { className: "rk-imgdetail", children: [
5737
+ /* @__PURE__ */ jsxs11("div", { className: "rk-thumbs", children: [
5738
+ /* @__PURE__ */ jsxs11("figure", { className: "rk-thumb", children: [
5739
+ beforeSrc ? /* @__PURE__ */ jsx12("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ jsx12("div", { className: "rk-thumb-missing", children: "no image" }),
5740
+ /* @__PURE__ */ jsx12("figcaption", { children: "Before" })
5471
5741
  ] }),
5472
- /* @__PURE__ */ jsxs10("figure", { className: "rk-thumb", children: [
5473
- /* @__PURE__ */ jsx11("img", { src: replacement, alt: "Proposed replacement" }),
5474
- /* @__PURE__ */ jsxs10("figcaption", { children: [
5742
+ /* @__PURE__ */ jsxs11("figure", { className: "rk-thumb", children: [
5743
+ /* @__PURE__ */ jsx12("img", { src: replacement, alt: "Proposed replacement" }),
5744
+ /* @__PURE__ */ jsxs11("figcaption", { children: [
5475
5745
  "After",
5476
5746
  embedded ? "" : " \xB7 linked"
5477
5747
  ] })
5478
5748
  ] })
5479
5749
  ] }),
5480
- /* @__PURE__ */ jsxs10("div", { className: "rk-imgdetail-meta", children: [
5750
+ /* @__PURE__ */ jsxs11("div", { className: "rk-imgdetail-meta", children: [
5481
5751
  embedded ? "Embedded image" : "Linked image",
5482
5752
  dims ? ` \xB7 ${dims}` : "",
5483
5753
  bytes !== null ? ` \xB7 ${formatBytes(bytes)}` : ""
5484
5754
  ] }),
5485
- !embedded && afterSrc && /* @__PURE__ */ jsx11("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
5486
- /* @__PURE__ */ jsxs10("div", { className: "rk-imgdetail-actions", children: [
5487
- /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
5488
- /* @__PURE__ */ jsx11(IconDownload, { size: 12 }),
5755
+ !embedded && afterSrc && /* @__PURE__ */ jsx12("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
5756
+ /* @__PURE__ */ jsxs11("div", { className: "rk-imgdetail-actions", children: [
5757
+ /* @__PURE__ */ jsxs11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
5758
+ /* @__PURE__ */ jsx12(IconDownload, { size: 12 }),
5489
5759
  " Download"
5490
5760
  ] }),
5491
- /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
5761
+ /* @__PURE__ */ jsx12("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
5492
5762
  ] })
5493
5763
  ] });
5494
5764
  }
5765
+ var SYNC_REASON_LABEL = {
5766
+ "token-short": "token too short",
5767
+ "bad-token": "token rejected",
5768
+ "room-full": "room full",
5769
+ unreachable: "server unreachable"
5770
+ };
5771
+ var SYNC_REASON_HELP = {
5772
+ "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.",
5773
+ "bad-token": "The sync server rejected the token. Check `syncToken` / `token` in <ReviewKit />.",
5774
+ "room-full": "This page already has the maximum number of reviewers online (16). Try again later.",
5775
+ 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."
5776
+ };
5495
5777
  function Panel() {
5496
- const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, emblemaPush, peers } = useStore();
5778
+ const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, syncReason, emblemaPush, peers } = useStore();
5497
5779
  const dispatch = useDispatch();
5498
5780
  const config = useConfig();
5499
- const [sticky, setStickyState] = useState8(() => isStickyEnabled(config.token));
5500
- const [query, setQuery] = useState8("");
5501
- const [typeFilter, setTypeFilter] = useState8(/* @__PURE__ */ new Set());
5502
- const [prioFilter, setPrioFilter] = useState8(/* @__PURE__ */ new Set());
5503
- const [statusFilter, setStatusFilter] = useState8(/* @__PURE__ */ new Set());
5504
- const [filterOpen, setFilterOpen] = useState8(false);
5505
- const [settingsOpen, setSettingsOpen] = useState8(false);
5506
- const [emblemaAlive, setEmblemaAlive] = useState8(null);
5507
- const [emblemaSending, setEmblemaSending] = useState8(false);
5508
- const [emblemaSync, setEmblemaSync] = useState8("unknown");
5781
+ const [sticky, setStickyState] = useState9(() => isStickyEnabled(config.token));
5782
+ const [query, setQuery] = useState9("");
5783
+ const [typeFilter, setTypeFilter] = useState9(/* @__PURE__ */ new Set());
5784
+ const [prioFilter, setPrioFilter] = useState9(/* @__PURE__ */ new Set());
5785
+ const [statusFilter, setStatusFilter] = useState9(/* @__PURE__ */ new Set());
5786
+ const [filterOpen, setFilterOpen] = useState9(false);
5787
+ const [settingsOpen, setSettingsOpen] = useState9(false);
5788
+ const [emblemaAlive, setEmblemaAlive] = useState9(null);
5789
+ const [emblemaSending, setEmblemaSending] = useState9(false);
5790
+ const [emblemaSync, setEmblemaSync] = useState9("unknown");
5509
5791
  const panelRef = useRef10(null);
5510
5792
  const searchRef = useRef10(null);
5511
5793
  const importRef = useRef10(null);
@@ -5526,7 +5808,7 @@ function Panel() {
5526
5808
  () => typeFilter.size + prioFilter.size + statusFilter.size > 0 ? { types: [...typeFilter], priorities: [...prioFilter], statuses: [...statusFilter] } : null,
5527
5809
  [typeFilter, prioFilter, statusFilter]
5528
5810
  );
5529
- useEffect9(() => {
5811
+ useEffect10(() => {
5530
5812
  dispatch({ type: "SET_FILTER", filter: activeFilter });
5531
5813
  return () => dispatch({ type: "SET_FILTER", filter: null });
5532
5814
  }, [activeFilter, dispatch]);
@@ -5563,7 +5845,7 @@ function Panel() {
5563
5845
  }
5564
5846
  return Array.from(map.entries());
5565
5847
  }, [filtered]);
5566
- useEffect9(() => {
5848
+ useEffect10(() => {
5567
5849
  if (!filterOpen && !settingsOpen) return;
5568
5850
  const onDown = (event) => {
5569
5851
  const path = event.composedPath();
@@ -5574,7 +5856,7 @@ function Panel() {
5574
5856
  document.addEventListener("pointerdown", onDown, true);
5575
5857
  return () => document.removeEventListener("pointerdown", onDown, true);
5576
5858
  }, [filterOpen, settingsOpen]);
5577
- useEffect9(() => {
5859
+ useEffect10(() => {
5578
5860
  if (!settingsOpen) return;
5579
5861
  let alive = true;
5580
5862
  const probe = () => {
@@ -5598,7 +5880,7 @@ function Panel() {
5598
5880
  live: "paired",
5599
5881
  off: "not paired"
5600
5882
  }[emblemaPairState];
5601
- useEffect9(() => {
5883
+ useEffect10(() => {
5602
5884
  if (!panelOpen || activeItemId === null) return;
5603
5885
  const row = panelRef.current?.querySelector(`[data-item-id="${activeItemId}"]`);
5604
5886
  row?.scrollIntoView({ block: "nearest" });
@@ -5714,7 +5996,7 @@ function Panel() {
5714
5996
  panel.style.transform = "";
5715
5997
  if (dy > 110) dispatch({ type: "SET_PANEL", open: false });
5716
5998
  };
5717
- return /* @__PURE__ */ jsxs10(
5999
+ return /* @__PURE__ */ jsxs11(
5718
6000
  "div",
5719
6001
  {
5720
6002
  ref: panelRef,
@@ -5724,7 +6006,7 @@ function Panel() {
5724
6006
  "aria-label": "Review feedback panel",
5725
6007
  onKeyDown,
5726
6008
  children: [
5727
- /* @__PURE__ */ jsx11(
6009
+ /* @__PURE__ */ jsx12(
5728
6010
  "div",
5729
6011
  {
5730
6012
  className: "rk-sheet-handle",
@@ -5735,34 +6017,41 @@ function Panel() {
5735
6017
  onPointerCancel: onSheetHandleUp
5736
6018
  }
5737
6019
  ),
5738
- /* @__PURE__ */ jsxs10("div", { className: "rk-panel-head", children: [
5739
- /* @__PURE__ */ jsxs10("div", { children: [
5740
- /* @__PURE__ */ jsx11("div", { className: "rk-panel-title", children: "Feedback" }),
5741
- /* @__PURE__ */ jsxs10("div", { className: "rk-panel-sub", children: [
6020
+ /* @__PURE__ */ jsxs11("div", { className: "rk-panel-head", children: [
6021
+ /* @__PURE__ */ jsxs11("div", { children: [
6022
+ /* @__PURE__ */ jsx12("div", { className: "rk-panel-title", children: "Feedback" }),
6023
+ /* @__PURE__ */ jsxs11("div", { className: "rk-panel-sub", children: [
5742
6024
  items.length,
5743
6025
  " item",
5744
6026
  items.length === 1 ? "" : "s",
5745
6027
  session.author ? ` \xB7 ${session.author}` : "",
5746
- syncStatus !== "disabled" && /* @__PURE__ */ jsxs10("span", { className: `rk-sync rk-sync-${syncStatus}`, children: [
5747
- /* @__PURE__ */ jsx11("span", { className: "rk-sync-dot" }),
5748
- syncStatus === "online" ? `live${peers.length > 0 ? ` \xB7 ${peers.length} other${peers.length === 1 ? "" : "s"}` : ""}` : syncStatus === "connecting" ? "connecting\u2026" : "local only"
5749
- ] }),
5750
- config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ jsxs10(
6028
+ syncStatus !== "disabled" && /* @__PURE__ */ jsxs11(
6029
+ "span",
6030
+ {
6031
+ className: `rk-sync rk-sync-${syncStatus}${syncReason ? ` rk-sync-reason-${syncReason}` : ""}`,
6032
+ 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"],
6033
+ children: [
6034
+ /* @__PURE__ */ jsx12("span", { className: "rk-sync-dot" }),
6035
+ 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"]}`
6036
+ ]
6037
+ }
6038
+ ),
6039
+ config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ jsxs11(
5751
6040
  "span",
5752
6041
  {
5753
6042
  className: `rk-sync rk-epush-${emblemaPush}`,
5754
6043
  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",
5755
6044
  children: [
5756
- /* @__PURE__ */ jsx11("span", { className: "rk-sync-dot" }),
6045
+ /* @__PURE__ */ jsx12("span", { className: "rk-sync-dot" }),
5757
6046
  emblemaPush === "ok" ? "emblema" : emblemaPush === "sending" ? "emblema\u2026" : emblemaPush === "rejected" ? "emblema rejected" : "emblema offline"
5758
6047
  ]
5759
6048
  }
5760
6049
  )
5761
6050
  ] })
5762
6051
  ] }),
5763
- /* @__PURE__ */ jsx11("span", { className: "rk-spacer" }),
5764
- /* @__PURE__ */ jsxs10("div", { className: "rk-menu-scope rk-menu-anchor", children: [
5765
- /* @__PURE__ */ jsx11(
6052
+ /* @__PURE__ */ jsx12("span", { className: "rk-spacer" }),
6053
+ /* @__PURE__ */ jsxs11("div", { className: "rk-menu-scope rk-menu-anchor", children: [
6054
+ /* @__PURE__ */ jsx12(
5766
6055
  "button",
5767
6056
  {
5768
6057
  type: "button",
@@ -5778,11 +6067,11 @@ function Panel() {
5778
6067
  void pingEmblema(config.emblema).then((port) => setEmblemaAlive(port !== null));
5779
6068
  }
5780
6069
  },
5781
- children: /* @__PURE__ */ jsx11(IconGear, { size: 15 })
6070
+ children: /* @__PURE__ */ jsx12(IconGear, { size: 15 })
5782
6071
  }
5783
6072
  ),
5784
- settingsOpen && /* @__PURE__ */ jsxs10("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
5785
- config.token && /* @__PURE__ */ jsxs10(
6073
+ settingsOpen && /* @__PURE__ */ jsxs11("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
6074
+ config.token && /* @__PURE__ */ jsxs11(
5786
6075
  "button",
5787
6076
  {
5788
6077
  type: "button",
@@ -5797,12 +6086,12 @@ function Panel() {
5797
6086
  toast(dispatch, next ? "Review stays on in this tab" : "Review active only via the link", "info");
5798
6087
  },
5799
6088
  children: [
5800
- /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
6089
+ /* @__PURE__ */ jsx12("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
5801
6090
  "Keep review on in this tab"
5802
6091
  ]
5803
6092
  }
5804
6093
  ),
5805
- config.emblema && /* @__PURE__ */ jsxs10(
6094
+ config.emblema && /* @__PURE__ */ jsxs11(
5806
6095
  "button",
5807
6096
  {
5808
6097
  type: "button",
@@ -5832,7 +6121,7 @@ function Panel() {
5832
6121
  }).finally(() => setEmblemaSending(false));
5833
6122
  },
5834
6123
  children: [
5835
- /* @__PURE__ */ jsx11(
6124
+ /* @__PURE__ */ jsx12(
5836
6125
  "span",
5837
6126
  {
5838
6127
  className: "rk-emblema-dot",
@@ -5843,25 +6132,40 @@ function Panel() {
5843
6132
  ]
5844
6133
  }
5845
6134
  ),
5846
- /* @__PURE__ */ jsxs10(
6135
+ /* @__PURE__ */ jsxs11(
5847
6136
  "button",
5848
6137
  {
5849
6138
  type: "button",
5850
6139
  role: "menuitem",
5851
6140
  className: "rk-menu-item",
5852
- 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",
6141
+ 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",
5853
6142
  onClick: () => {
5854
6143
  setSettingsOpen(false);
5855
6144
  dispatch({ type: "SET_EMBLEMA_CONNECT", open: true });
5856
6145
  },
5857
6146
  children: [
5858
- /* @__PURE__ */ jsx11("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
6147
+ /* @__PURE__ */ jsx12("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
5859
6148
  "Connect to Emblema\u2026",
5860
- /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: emblemaPairLabel })
6149
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-count", children: emblemaPairLabel })
5861
6150
  ]
5862
6151
  }
5863
6152
  ),
5864
- /* @__PURE__ */ jsx11(
6153
+ /* @__PURE__ */ jsx12(
6154
+ "button",
6155
+ {
6156
+ type: "button",
6157
+ role: "menuitem",
6158
+ className: "rk-menu-item",
6159
+ 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",
6160
+ disabled: !resolveSyncToken(config),
6161
+ onClick: () => {
6162
+ setSettingsOpen(false);
6163
+ dispatch({ type: "SET_TERMINAL_CONNECT", open: true });
6164
+ },
6165
+ children: "Connect terminal / agent\u2026"
6166
+ }
6167
+ ),
6168
+ /* @__PURE__ */ jsx12(
5865
6169
  "button",
5866
6170
  {
5867
6171
  type: "button",
@@ -5874,31 +6178,31 @@ function Panel() {
5874
6178
  )
5875
6179
  ] })
5876
6180
  ] }),
5877
- /* @__PURE__ */ jsx11(
6181
+ /* @__PURE__ */ jsx12(
5878
6182
  "button",
5879
6183
  {
5880
6184
  type: "button",
5881
6185
  className: "rk-icon-btn",
5882
6186
  "aria-label": "Keyboard shortcuts",
5883
6187
  onClick: () => dispatch({ type: "SET_CHEATSHEET", open: true }),
5884
- children: /* @__PURE__ */ jsx11(IconKeyboard, { size: 15 })
6188
+ children: /* @__PURE__ */ jsx12(IconKeyboard, { size: 15 })
5885
6189
  }
5886
6190
  ),
5887
- /* @__PURE__ */ jsx11(
6191
+ /* @__PURE__ */ jsx12(
5888
6192
  "button",
5889
6193
  {
5890
6194
  type: "button",
5891
6195
  className: "rk-icon-btn",
5892
6196
  "aria-label": "Close panel",
5893
6197
  onClick: () => dispatch({ type: "SET_PANEL", open: false }),
5894
- children: /* @__PURE__ */ jsx11(IconClose, { size: 14 })
6198
+ children: /* @__PURE__ */ jsx12(IconClose, { size: 14 })
5895
6199
  }
5896
6200
  )
5897
6201
  ] }),
5898
- /* @__PURE__ */ jsxs10("div", { className: "rk-view-row", children: [
5899
- /* @__PURE__ */ jsx11("div", { className: "rk-view-info", children: /* @__PURE__ */ jsx11("div", { className: "rk-view-title", children: "Page view" }) }),
5900
- /* @__PURE__ */ jsxs10("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
5901
- /* @__PURE__ */ jsx11(
6202
+ /* @__PURE__ */ jsxs11("div", { className: "rk-view-row", children: [
6203
+ /* @__PURE__ */ jsx12("div", { className: "rk-view-info", children: /* @__PURE__ */ jsx12("div", { className: "rk-view-title", children: "Page view" }) }),
6204
+ /* @__PURE__ */ jsxs11("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
6205
+ /* @__PURE__ */ jsx12(
5902
6206
  "button",
5903
6207
  {
5904
6208
  type: "button",
@@ -5908,7 +6212,7 @@ function Panel() {
5908
6212
  children: "Original"
5909
6213
  }
5910
6214
  ),
5911
- /* @__PURE__ */ jsx11(
6215
+ /* @__PURE__ */ jsx12(
5912
6216
  "button",
5913
6217
  {
5914
6218
  type: "button",
@@ -5920,8 +6224,8 @@ function Panel() {
5920
6224
  )
5921
6225
  ] })
5922
6226
  ] }),
5923
- /* @__PURE__ */ jsxs10("div", { className: "rk-panel-tools", children: [
5924
- /* @__PURE__ */ jsxs10(
6227
+ /* @__PURE__ */ jsxs11("div", { className: "rk-panel-tools", children: [
6228
+ /* @__PURE__ */ jsxs11(
5925
6229
  "button",
5926
6230
  {
5927
6231
  type: "button",
@@ -5932,15 +6236,15 @@ function Panel() {
5932
6236
  dispatch({ type: "SET_MODE", mode: "pick" });
5933
6237
  },
5934
6238
  children: [
5935
- /* @__PURE__ */ jsx11(IconTarget, { size: 14 }),
6239
+ /* @__PURE__ */ jsx12(IconTarget, { size: 14 }),
5936
6240
  " Pick an element"
5937
6241
  ]
5938
6242
  }
5939
6243
  ),
5940
- /* @__PURE__ */ jsxs10("div", { className: "rk-search-row", children: [
5941
- /* @__PURE__ */ jsxs10("div", { className: "rk-search", children: [
5942
- /* @__PURE__ */ jsx11(IconSearch, { size: 13 }),
5943
- /* @__PURE__ */ jsx11(
6244
+ /* @__PURE__ */ jsxs11("div", { className: "rk-search-row", children: [
6245
+ /* @__PURE__ */ jsxs11("div", { className: "rk-search", children: [
6246
+ /* @__PURE__ */ jsx12(IconSearch, { size: 13 }),
6247
+ /* @__PURE__ */ jsx12(
5944
6248
  "input",
5945
6249
  {
5946
6250
  ref: searchRef,
@@ -5952,8 +6256,8 @@ function Panel() {
5952
6256
  }
5953
6257
  )
5954
6258
  ] }),
5955
- /* @__PURE__ */ jsxs10("div", { className: "rk-menu-scope rk-menu-anchor", children: [
5956
- /* @__PURE__ */ jsxs10(
6259
+ /* @__PURE__ */ jsxs11("div", { className: "rk-menu-scope rk-menu-anchor", children: [
6260
+ /* @__PURE__ */ jsxs11(
5957
6261
  "button",
5958
6262
  {
5959
6263
  type: "button",
@@ -5965,15 +6269,15 @@ function Panel() {
5965
6269
  setSettingsOpen(false);
5966
6270
  },
5967
6271
  children: [
5968
- /* @__PURE__ */ jsx11(IconFilter, { size: 13 }),
6272
+ /* @__PURE__ */ jsx12(IconFilter, { size: 13 }),
5969
6273
  "Filter",
5970
- typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ jsx11("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
6274
+ typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ jsx12("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
5971
6275
  ]
5972
6276
  }
5973
6277
  ),
5974
- filterOpen && /* @__PURE__ */ jsxs10("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
5975
- /* @__PURE__ */ jsx11("div", { className: "rk-menu-title", children: "Type" }),
5976
- TYPE_FILTERS.map((type) => /* @__PURE__ */ jsxs10(
6278
+ filterOpen && /* @__PURE__ */ jsxs11("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
6279
+ /* @__PURE__ */ jsx12("div", { className: "rk-menu-title", children: "Type" }),
6280
+ TYPE_FILTERS.map((type) => /* @__PURE__ */ jsxs11(
5977
6281
  "button",
5978
6282
  {
5979
6283
  type: "button",
@@ -5987,16 +6291,16 @@ function Panel() {
5987
6291
  setTypeFilter(next);
5988
6292
  },
5989
6293
  children: [
5990
- /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
6294
+ /* @__PURE__ */ jsx12("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
5991
6295
  iconForType(type, 12),
5992
- /* @__PURE__ */ jsx11("span", { className: "rk-menu-label", children: type }),
5993
- /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
6296
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-label", children: type }),
6297
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
5994
6298
  ]
5995
6299
  },
5996
6300
  type
5997
6301
  )),
5998
- /* @__PURE__ */ jsx11("div", { className: "rk-menu-title", children: "Priority" }),
5999
- PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ jsxs10(
6302
+ /* @__PURE__ */ jsx12("div", { className: "rk-menu-title", children: "Priority" }),
6303
+ PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ jsxs11(
6000
6304
  "button",
6001
6305
  {
6002
6306
  type: "button",
@@ -6010,15 +6314,15 @@ function Panel() {
6010
6314
  setPrioFilter(next);
6011
6315
  },
6012
6316
  children: [
6013
- /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
6014
- /* @__PURE__ */ jsx11("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
6015
- /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
6317
+ /* @__PURE__ */ jsx12("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
6318
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
6319
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
6016
6320
  ]
6017
6321
  },
6018
6322
  priority
6019
6323
  )),
6020
- /* @__PURE__ */ jsx11("div", { className: "rk-menu-title", children: "Status" }),
6021
- STATUSES.map((status) => /* @__PURE__ */ jsxs10(
6324
+ /* @__PURE__ */ jsx12("div", { className: "rk-menu-title", children: "Status" }),
6325
+ STATUSES.map((status) => /* @__PURE__ */ jsxs11(
6022
6326
  "button",
6023
6327
  {
6024
6328
  type: "button",
@@ -6032,15 +6336,15 @@ function Panel() {
6032
6336
  setStatusFilter(next);
6033
6337
  },
6034
6338
  children: [
6035
- /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
6036
- /* @__PURE__ */ jsx11("span", { className: `rk-status-dot rk-st-${status}` }),
6037
- /* @__PURE__ */ jsx11("span", { className: "rk-menu-label", children: status }),
6038
- /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
6339
+ /* @__PURE__ */ jsx12("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
6340
+ /* @__PURE__ */ jsx12("span", { className: `rk-status-dot rk-st-${status}` }),
6341
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-label", children: status }),
6342
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
6039
6343
  ]
6040
6344
  },
6041
6345
  status
6042
6346
  )),
6043
- /* @__PURE__ */ jsx11(
6347
+ /* @__PURE__ */ jsx12(
6044
6348
  "button",
6045
6349
  {
6046
6350
  type: "button",
@@ -6060,24 +6364,24 @@ function Panel() {
6060
6364
  ] })
6061
6365
  ] })
6062
6366
  ] }),
6063
- preview && unapplied > 0 && /* @__PURE__ */ jsxs10("div", { className: "rk-unplaced", role: "status", children: [
6367
+ preview && unapplied > 0 && /* @__PURE__ */ jsxs11("div", { className: "rk-unplaced", role: "status", children: [
6064
6368
  unapplied,
6065
6369
  " saved change",
6066
6370
  unapplied === 1 ? "" : "s",
6067
6371
  " couldn\u2019t be placed on the current page \u2014 the page has changed since they were captured."
6068
6372
  ] }),
6069
- /* @__PURE__ */ jsx11("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ jsxs10("div", { className: "rk-empty", children: [
6070
- /* @__PURE__ */ jsx11("div", { className: "rk-empty-art", children: /* @__PURE__ */ jsx11(IconTarget, { size: 22 }) }),
6071
- /* @__PURE__ */ jsx11("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
6072
- /* @__PURE__ */ jsx11("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." })
6073
- ] }) : groups.map(([route, entries]) => /* @__PURE__ */ jsxs10("div", { children: [
6074
- /* @__PURE__ */ jsx11("div", { className: "rk-group-title", children: route }),
6373
+ /* @__PURE__ */ jsx12("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ jsxs11("div", { className: "rk-empty", children: [
6374
+ /* @__PURE__ */ jsx12("div", { className: "rk-empty-art", children: /* @__PURE__ */ jsx12(IconTarget, { size: 22 }) }),
6375
+ /* @__PURE__ */ jsx12("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
6376
+ /* @__PURE__ */ jsx12("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." })
6377
+ ] }) : groups.map(([route, entries]) => /* @__PURE__ */ jsxs11("div", { children: [
6378
+ /* @__PURE__ */ jsx12("div", { className: "rk-group-title", children: route }),
6075
6379
  entries.map(({ item, number }) => {
6076
6380
  const active = item.id === activeItemId;
6077
6381
  const orphan = orphanIds.has(item.id);
6078
6382
  const hiddenEl = hiddenIds.has(item.id);
6079
- return /* @__PURE__ */ jsxs10("div", { children: [
6080
- /* @__PURE__ */ jsxs10(
6383
+ return /* @__PURE__ */ jsxs11("div", { children: [
6384
+ /* @__PURE__ */ jsxs11(
6081
6385
  "div",
6082
6386
  {
6083
6387
  "data-item-id": item.id,
@@ -6092,18 +6396,18 @@ function Panel() {
6092
6396
  }
6093
6397
  },
6094
6398
  children: [
6095
- /* @__PURE__ */ jsx11("span", { className: "rk-item-num", children: number }),
6096
- /* @__PURE__ */ jsx11("span", { className: "rk-item-icon", children: iconForType(item.type) }),
6097
- /* @__PURE__ */ jsxs10("span", { className: "rk-item-body", children: [
6098
- /* @__PURE__ */ jsxs10("span", { className: "rk-item-snippet", children: [
6399
+ /* @__PURE__ */ jsx12("span", { className: "rk-item-num", children: number }),
6400
+ /* @__PURE__ */ jsx12("span", { className: "rk-item-icon", children: iconForType(item.type) }),
6401
+ /* @__PURE__ */ jsxs11("span", { className: "rk-item-body", children: [
6402
+ /* @__PURE__ */ jsxs11("span", { className: "rk-item-snippet", children: [
6099
6403
  item.target.tagName,
6100
6404
  " \xB7 ",
6101
6405
  item.target.textSnippet || item.target.selector
6102
6406
  ] }),
6103
- /* @__PURE__ */ jsx11("span", { className: "rk-item-change", children: changeSummary(item) }),
6104
- /* @__PURE__ */ jsxs10("span", { className: "rk-item-meta", children: [
6105
- /* @__PURE__ */ jsxs10("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
6106
- /* @__PURE__ */ jsx11(
6407
+ /* @__PURE__ */ jsx12("span", { className: "rk-item-change", children: changeSummary(item) }),
6408
+ /* @__PURE__ */ jsxs11("span", { className: "rk-item-meta", children: [
6409
+ /* @__PURE__ */ jsxs11("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
6410
+ /* @__PURE__ */ jsx12(
6107
6411
  "span",
6108
6412
  {
6109
6413
  className: "rk-author-dot",
@@ -6111,15 +6415,15 @@ function Panel() {
6111
6415
  }
6112
6416
  ),
6113
6417
  item.author?.name ?? "Unknown",
6114
- /* @__PURE__ */ jsxs10("span", { className: "rk-time", children: [
6418
+ /* @__PURE__ */ jsxs11("span", { className: "rk-time", children: [
6115
6419
  " \xB7 ",
6116
6420
  timeAgo(item.createdAt)
6117
6421
  ] })
6118
6422
  ] }),
6119
- item.priority === "must" && /* @__PURE__ */ jsx11("span", { className: "rk-badge-mini rk-must", children: "must" }),
6120
- item.status !== "open" && /* @__PURE__ */ jsx11("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
6121
- orphan && /* @__PURE__ */ jsx11("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
6122
- hiddenEl && /* @__PURE__ */ jsx11(
6423
+ item.priority === "must" && /* @__PURE__ */ jsx12("span", { className: "rk-badge-mini rk-must", children: "must" }),
6424
+ item.status !== "open" && /* @__PURE__ */ jsx12("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
6425
+ orphan && /* @__PURE__ */ jsx12("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
6426
+ hiddenEl && /* @__PURE__ */ jsx12(
6123
6427
  "span",
6124
6428
  {
6125
6429
  className: "rk-badge-mini rk-hidden-el",
@@ -6127,14 +6431,14 @@ function Panel() {
6127
6431
  children: "hidden"
6128
6432
  }
6129
6433
  ),
6130
- item.note && /* @__PURE__ */ jsx11("span", { className: "rk-badge-mini", children: "note" }),
6131
- peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ jsxs10("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
6434
+ item.note && /* @__PURE__ */ jsx12("span", { className: "rk-badge-mini", children: "note" }),
6435
+ peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ jsxs11("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
6132
6436
  peer.name || "peer",
6133
6437
  " is writing\u2026"
6134
6438
  ] }, peer.id))
6135
6439
  ] })
6136
6440
  ] }),
6137
- /* @__PURE__ */ jsx11("span", { className: "rk-item-actions", children: /* @__PURE__ */ jsx11(
6441
+ /* @__PURE__ */ jsx12("span", { className: "rk-item-actions", children: /* @__PURE__ */ jsx12(
6138
6442
  "button",
6139
6443
  {
6140
6444
  type: "button",
@@ -6149,15 +6453,15 @@ function Panel() {
6149
6453
  "info"
6150
6454
  );
6151
6455
  },
6152
- children: /* @__PURE__ */ jsx11(IconTrash, { size: 13 })
6456
+ children: /* @__PURE__ */ jsx12(IconTrash, { size: 13 })
6153
6457
  }
6154
6458
  ) })
6155
6459
  ]
6156
6460
  }
6157
6461
  ),
6158
- active && /* @__PURE__ */ jsxs10("div", { className: "rk-item-edit", children: [
6159
- /* @__PURE__ */ jsxs10("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
6160
- /* @__PURE__ */ jsx11(
6462
+ active && /* @__PURE__ */ jsxs11("div", { className: "rk-item-edit", children: [
6463
+ /* @__PURE__ */ jsxs11("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
6464
+ /* @__PURE__ */ jsx12(
6161
6465
  "span",
6162
6466
  {
6163
6467
  className: "rk-author-dot",
@@ -6168,8 +6472,8 @@ function Panel() {
6168
6472
  " \xB7 ",
6169
6473
  timeAgo(item.createdAt)
6170
6474
  ] }),
6171
- isCopyItem(item) && /* @__PURE__ */ jsx11(CopyDiff, { before: item.payload.before, after: item.payload.after }),
6172
- isImageItem(item) && /* @__PURE__ */ jsx11(
6475
+ isCopyItem(item) && /* @__PURE__ */ jsx12(CopyDiff, { before: item.payload.before, after: item.payload.after }),
6476
+ isImageItem(item) && /* @__PURE__ */ jsx12(
6173
6477
  ImageDetail,
6174
6478
  {
6175
6479
  beforeSrc: item.payload.beforeSrc,
@@ -6177,7 +6481,7 @@ function Panel() {
6177
6481
  afterDataUrl: item.payload.afterDataUrl
6178
6482
  }
6179
6483
  ),
6180
- isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ jsx11(
6484
+ isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ jsx12(
6181
6485
  "textarea",
6182
6486
  {
6183
6487
  className: "rk-textarea",
@@ -6187,7 +6491,7 @@ function Panel() {
6187
6491
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: withPayloadText(item, event.target.value) })
6188
6492
  }
6189
6493
  ),
6190
- /* @__PURE__ */ jsx11(
6494
+ /* @__PURE__ */ jsx12(
6191
6495
  "input",
6192
6496
  {
6193
6497
  className: "rk-input",
@@ -6197,9 +6501,9 @@ function Panel() {
6197
6501
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: { note: event.target.value } })
6198
6502
  }
6199
6503
  ),
6200
- /* @__PURE__ */ jsxs10("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
6201
- /* @__PURE__ */ jsxs10("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6202
- /* @__PURE__ */ jsx11(
6504
+ /* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
6505
+ /* @__PURE__ */ jsxs11("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6506
+ /* @__PURE__ */ jsx12(
6203
6507
  "button",
6204
6508
  {
6205
6509
  type: "button",
@@ -6208,7 +6512,7 @@ function Panel() {
6208
6512
  children: "Must"
6209
6513
  }
6210
6514
  ),
6211
- /* @__PURE__ */ jsx11(
6515
+ /* @__PURE__ */ jsx12(
6212
6516
  "button",
6213
6517
  {
6214
6518
  type: "button",
@@ -6218,7 +6522,7 @@ function Panel() {
6218
6522
  }
6219
6523
  )
6220
6524
  ] }),
6221
- /* @__PURE__ */ jsx11("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ jsx11(
6525
+ /* @__PURE__ */ jsx12("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ jsx12(
6222
6526
  "button",
6223
6527
  {
6224
6528
  type: "button",
@@ -6233,22 +6537,22 @@ function Panel() {
6233
6537
  ] }, item.id);
6234
6538
  })
6235
6539
  ] }, route)) }),
6236
- /* @__PURE__ */ jsxs10("div", { className: "rk-panel-foot", children: [
6237
- /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
6238
- /* @__PURE__ */ jsx11(IconDownload, { size: 13 }),
6540
+ /* @__PURE__ */ jsxs11("div", { className: "rk-panel-foot", children: [
6541
+ /* @__PURE__ */ jsxs11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
6542
+ /* @__PURE__ */ jsx12(IconDownload, { size: 13 }),
6239
6543
  " Export"
6240
6544
  ] }),
6241
- /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
6242
- /* @__PURE__ */ jsx11(IconCopyClipboard, { size: 13 }),
6545
+ /* @__PURE__ */ jsxs11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
6546
+ /* @__PURE__ */ jsx12(IconCopyClipboard, { size: 13 }),
6243
6547
  " Copy"
6244
6548
  ] }),
6245
- /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
6246
- /* @__PURE__ */ jsx11(IconUpload, { size: 13 }),
6549
+ /* @__PURE__ */ jsxs11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
6550
+ /* @__PURE__ */ jsx12(IconUpload, { size: 13 }),
6247
6551
  " Import"
6248
6552
  ] }),
6249
- /* @__PURE__ */ jsx11("span", { className: "rk-spacer" }),
6250
- undoStack.length > 0 && /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-icon-btn", "aria-label": "Undo last change (\u2318Z)", onClick: () => dispatch({ type: "UNDO" }), children: /* @__PURE__ */ jsx11(IconUndo, { size: 14 }) }),
6251
- /* @__PURE__ */ jsx11(
6553
+ /* @__PURE__ */ jsx12("span", { className: "rk-spacer" }),
6554
+ undoStack.length > 0 && /* @__PURE__ */ jsx12("button", { type: "button", className: "rk-icon-btn", "aria-label": "Undo last change (\u2318Z)", onClick: () => dispatch({ type: "UNDO" }), children: /* @__PURE__ */ jsx12(IconUndo, { size: 14 }) }),
6555
+ /* @__PURE__ */ jsx12(
6252
6556
  "button",
6253
6557
  {
6254
6558
  type: "button",
@@ -6259,7 +6563,7 @@ function Panel() {
6259
6563
  children: "Clear"
6260
6564
  }
6261
6565
  ),
6262
- /* @__PURE__ */ jsx11(
6566
+ /* @__PURE__ */ jsx12(
6263
6567
  "button",
6264
6568
  {
6265
6569
  type: "button",
@@ -6269,7 +6573,7 @@ function Panel() {
6269
6573
  children: "Exit review"
6270
6574
  }
6271
6575
  ),
6272
- /* @__PURE__ */ jsx11(
6576
+ /* @__PURE__ */ jsx12(
6273
6577
  "input",
6274
6578
  {
6275
6579
  ref: importRef,
@@ -6290,8 +6594,8 @@ function Panel() {
6290
6594
  }
6291
6595
 
6292
6596
  // src/client/components/Presence.tsx
6293
- import { useEffect as useEffect10, useRef as useRef11 } from "react";
6294
- import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
6597
+ import { useEffect as useEffect11, useRef as useRef11 } from "react";
6598
+ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
6295
6599
  function initials(name) {
6296
6600
  const parts = name.trim().split(/\s+/).filter(Boolean);
6297
6601
  if (parts.length === 0) return "\xB7";
@@ -6301,8 +6605,8 @@ function initials(name) {
6301
6605
  function PresenceStrip() {
6302
6606
  const { peers, syncStatus } = useStore();
6303
6607
  if (syncStatus === "disabled" || peers.length === 0) return null;
6304
- return /* @__PURE__ */ jsxs11("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
6305
- peers.slice(0, 5).map((peer) => /* @__PURE__ */ jsx12(
6608
+ return /* @__PURE__ */ jsxs12("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
6609
+ peers.slice(0, 5).map((peer) => /* @__PURE__ */ jsx13(
6306
6610
  "span",
6307
6611
  {
6308
6612
  className: "rk-avatar",
@@ -6312,7 +6616,7 @@ function PresenceStrip() {
6312
6616
  },
6313
6617
  peer.id
6314
6618
  )),
6315
- peers.length > 5 && /* @__PURE__ */ jsxs11("span", { className: "rk-avatar rk-avatar-more", children: [
6619
+ peers.length > 5 && /* @__PURE__ */ jsxs12("span", { className: "rk-avatar rk-avatar-more", children: [
6316
6620
  "+",
6317
6621
  peers.length - 5
6318
6622
  ] })
@@ -6336,7 +6640,7 @@ function PeerCursors() {
6336
6640
  }
6337
6641
  }
6338
6642
  peersRef.current = peers;
6339
- useEffect10(() => {
6643
+ useEffect11(() => {
6340
6644
  const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
6341
6645
  let raf = 0;
6342
6646
  let last = performance.now();
@@ -6441,8 +6745,8 @@ function PeerCursors() {
6441
6745
  return () => cancelAnimationFrame(raf);
6442
6746
  }, []);
6443
6747
  if (peers.length === 0) return null;
6444
- return /* @__PURE__ */ jsxs11(Fragment6, { children: [
6445
- peers.map((peer) => /* @__PURE__ */ jsx12(
6748
+ return /* @__PURE__ */ jsxs12(Fragment7, { children: [
6749
+ peers.map((peer) => /* @__PURE__ */ jsx13(
6446
6750
  "div",
6447
6751
  {
6448
6752
  ref: (el) => {
@@ -6454,7 +6758,7 @@ function PeerCursors() {
6454
6758
  },
6455
6759
  `ring-${peer.id}`
6456
6760
  )),
6457
- peers.map((peer, index) => /* @__PURE__ */ jsxs11(
6761
+ peers.map((peer, index) => /* @__PURE__ */ jsxs12(
6458
6762
  "div",
6459
6763
  {
6460
6764
  ref: (el) => {
@@ -6464,8 +6768,8 @@ function PeerCursors() {
6464
6768
  className: "rk-peer-cursor",
6465
6769
  style: { opacity: 0 },
6466
6770
  children: [
6467
- /* @__PURE__ */ jsx12("svg", { width: "14", height: "18", viewBox: "0 0 14 18", "aria-hidden": "true", children: /* @__PURE__ */ jsx12("path", { d: "M1 1l12 7.5-5.2 1.2L5 16z", fill: peer.color, stroke: "rgba(0,0,0,0.25)", strokeWidth: "1" }) }),
6468
- /* @__PURE__ */ jsxs11(
6771
+ /* @__PURE__ */ jsx13("svg", { width: "14", height: "18", viewBox: "0 0 14 18", "aria-hidden": "true", children: /* @__PURE__ */ jsx13("path", { d: "M1 1l12 7.5-5.2 1.2L5 16z", fill: peer.color, stroke: "rgba(0,0,0,0.25)", strokeWidth: "1" }) }),
6772
+ /* @__PURE__ */ jsxs12(
6469
6773
  "span",
6470
6774
  {
6471
6775
  className: "rk-peer-name",
@@ -6484,19 +6788,19 @@ function PeerCursors() {
6484
6788
  }
6485
6789
 
6486
6790
  // src/client/components/QuickComment.tsx
6487
- import { useEffect as useEffect11, useRef as useRef12, useState as useState9 } from "react";
6488
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
6791
+ import { useEffect as useEffect12, useRef as useRef12, useState as useState10 } from "react";
6792
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
6489
6793
  function QuickComment() {
6490
6794
  const { quick } = useStore();
6491
6795
  if (!quick) return null;
6492
- return /* @__PURE__ */ jsx13(QuickComposerCard, { quick }, quick.id);
6796
+ return /* @__PURE__ */ jsx14(QuickComposerCard, { quick }, quick.id);
6493
6797
  }
6494
6798
  function QuickComposerCard({ quick }) {
6495
6799
  const { route, session } = useStore();
6496
6800
  const dispatch = useDispatch();
6497
- const [text, setText] = useState9("");
6801
+ const [text, setText] = useState10("");
6498
6802
  const fieldRef = useRef12(null);
6499
- useEffect11(() => {
6803
+ useEffect12(() => {
6500
6804
  const field = fieldRef.current;
6501
6805
  if (!field) return;
6502
6806
  field.focus();
@@ -6549,18 +6853,18 @@ function QuickComposerCard({ quick }) {
6549
6853
  };
6550
6854
  const x = Math.min(Math.max(quick.x, 8), window.innerWidth - 288);
6551
6855
  const y = Math.min(Math.max(quick.y + 12, 8), window.innerHeight - 108);
6552
- return /* @__PURE__ */ jsxs12("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
6553
- /* @__PURE__ */ jsxs12("div", { className: "rk-quick-head", children: [
6554
- /* @__PURE__ */ jsx13(IconComment, { size: 12 }),
6555
- /* @__PURE__ */ jsx13("span", { children: "Comment here" }),
6556
- /* @__PURE__ */ jsxs12("span", { className: "rk-hint", children: [
6557
- /* @__PURE__ */ jsx13("span", { className: "rk-kbd", children: "\u21B5" }),
6856
+ return /* @__PURE__ */ jsxs13("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
6857
+ /* @__PURE__ */ jsxs13("div", { className: "rk-quick-head", children: [
6858
+ /* @__PURE__ */ jsx14(IconComment, { size: 12 }),
6859
+ /* @__PURE__ */ jsx14("span", { children: "Comment here" }),
6860
+ /* @__PURE__ */ jsxs13("span", { className: "rk-hint", children: [
6861
+ /* @__PURE__ */ jsx14("span", { className: "rk-kbd", children: "\u21B5" }),
6558
6862
  " save \xB7 ",
6559
- /* @__PURE__ */ jsx13("span", { className: "rk-kbd", children: "esc" }),
6863
+ /* @__PURE__ */ jsx14("span", { className: "rk-kbd", children: "esc" }),
6560
6864
  " cancel"
6561
6865
  ] })
6562
6866
  ] }),
6563
- /* @__PURE__ */ jsx13(
6867
+ /* @__PURE__ */ jsx14(
6564
6868
  "textarea",
6565
6869
  {
6566
6870
  ref: fieldRef,
@@ -6576,8 +6880,8 @@ function QuickComposerCard({ quick }) {
6576
6880
  }
6577
6881
 
6578
6882
  // src/client/components/PickLayer.tsx
6579
- import { useEffect as useEffect12, useRef as useRef13, useState as useState10 } from "react";
6580
- import { Fragment as Fragment7, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
6883
+ import { useEffect as useEffect13, useRef as useRef13, useState as useState11 } from "react";
6884
+ import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
6581
6885
  function applyRect(el, rect, pad2 = 3) {
6582
6886
  if (!el) return;
6583
6887
  if (!rect) {
@@ -6592,14 +6896,14 @@ function applyRect(el, rect, pad2 = 3) {
6592
6896
  function PickLayer() {
6593
6897
  const { mode, selection, tool } = useStore();
6594
6898
  const dispatch = useDispatch();
6595
- const [hovered, setHovered] = useState10(null);
6899
+ const [hovered, setHovered] = useState11(null);
6596
6900
  const outlineRef = useRef13(null);
6597
6901
  const labelRef = useRef13(null);
6598
6902
  const selectRef = useRef13(null);
6599
6903
  const hoveredRef = useRef13(null);
6600
6904
  hoveredRef.current = hovered;
6601
6905
  const picking = mode === "pick";
6602
- useEffect12(() => {
6906
+ useEffect13(() => {
6603
6907
  if (!picking) {
6604
6908
  setHovered(null);
6605
6909
  return;
@@ -6643,7 +6947,7 @@ function PickLayer() {
6643
6947
  document.removeEventListener("mousedown", swallow, true);
6644
6948
  };
6645
6949
  }, [picking, tool, dispatch]);
6646
- useEffect12(() => {
6950
+ useEffect13(() => {
6647
6951
  if (!picking) return;
6648
6952
  const body = document.body;
6649
6953
  const prev = body.style.cursor;
@@ -6653,7 +6957,7 @@ function PickLayer() {
6653
6957
  if (body.getAttribute("style") === "") body.removeAttribute("style");
6654
6958
  };
6655
6959
  }, [picking, tool]);
6656
- useEffect12(() => {
6960
+ useEffect13(() => {
6657
6961
  let raf = 0;
6658
6962
  const tick = () => {
6659
6963
  const hoverEl = hoveredRef.current;
@@ -6679,21 +6983,21 @@ function PickLayer() {
6679
6983
  raf = requestAnimationFrame(tick);
6680
6984
  return () => cancelAnimationFrame(raf);
6681
6985
  }, [picking, selection]);
6682
- return /* @__PURE__ */ jsxs13(Fragment7, { children: [
6683
- picking && /* @__PURE__ */ jsxs13(Fragment7, { children: [
6684
- /* @__PURE__ */ jsx14("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
6685
- /* @__PURE__ */ jsx14("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ jsxs13(Fragment7, { children: [
6686
- /* @__PURE__ */ jsx14("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
6687
- /* @__PURE__ */ jsx14("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
6688
- ] }) : /* @__PURE__ */ jsx14("span", { children: "Pick an element" }) })
6986
+ return /* @__PURE__ */ jsxs14(Fragment8, { children: [
6987
+ picking && /* @__PURE__ */ jsxs14(Fragment8, { children: [
6988
+ /* @__PURE__ */ jsx15("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
6989
+ /* @__PURE__ */ jsx15("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ jsxs14(Fragment8, { children: [
6990
+ /* @__PURE__ */ jsx15("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
6991
+ /* @__PURE__ */ jsx15("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
6992
+ ] }) : /* @__PURE__ */ jsx15("span", { children: "Pick an element" }) })
6689
6993
  ] }),
6690
- selection && /* @__PURE__ */ jsx14("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
6994
+ selection && /* @__PURE__ */ jsx15("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
6691
6995
  ] });
6692
6996
  }
6693
6997
 
6694
6998
  // src/client/components/Pins.tsx
6695
- import { useEffect as useEffect13, useMemo as useMemo4, useRef as useRef14, useState as useState11 } from "react";
6696
- import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
6999
+ import { useEffect as useEffect14, useMemo as useMemo4, useRef as useRef14, useState as useState12 } from "react";
7000
+ import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
6697
7001
  var STATUS_LABELS = {
6698
7002
  open: "open",
6699
7003
  accepted: "accepted",
@@ -6705,22 +7009,22 @@ var RERESOLVE_MS = 800;
6705
7009
  function Pins() {
6706
7010
  const { session, route, activeItemId, editor, mode, filter } = useStore();
6707
7011
  const dispatch = useDispatch();
6708
- const [clusters, setClusters] = useState11([]);
6709
- const [orphanCount, setOrphanCount] = useState11(0);
7012
+ const [clusters, setClusters] = useState12([]);
7013
+ const [orphanCount, setOrphanCount] = useState12(0);
6710
7014
  const wrapRefs = useRef14(/* @__PURE__ */ new Map());
6711
7015
  const visibleCache = useRef14(/* @__PURE__ */ new Map());
6712
7016
  const highlightRef = useRef14(null);
6713
7017
  const hoveredItems = useRef14(null);
6714
7018
  const elementCache = useRef14(/* @__PURE__ */ new Map());
6715
7019
  const lastResolve = useRef14(0);
6716
- const [newestId, setNewestId] = useState11(null);
7020
+ const [newestId, setNewestId] = useState12(null);
6717
7021
  const prevCount = useRef14(0);
6718
7022
  const items = useMemo4(() => {
6719
7023
  const all = session?.items ?? [];
6720
7024
  return all.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => item.route === route);
6721
7025
  }, [session, route]);
6722
7026
  const byId = new Map(items.map(({ item }) => [item.id, item]));
6723
- useEffect13(() => {
7027
+ useEffect14(() => {
6724
7028
  const count = session?.items.length ?? 0;
6725
7029
  if (count > prevCount.current && session && count > 0) {
6726
7030
  const id = session.items[count - 1].id;
@@ -6731,7 +7035,7 @@ function Pins() {
6731
7035
  }
6732
7036
  prevCount.current = count;
6733
7037
  }, [session]);
6734
- useEffect13(() => {
7038
+ useEffect14(() => {
6735
7039
  let raf = 0;
6736
7040
  let lastSignature = "";
6737
7041
  const tick = (time) => {
@@ -6815,8 +7119,8 @@ function Pins() {
6815
7119
  dispatch({ type: "SET_ACTIVE_ITEM", id });
6816
7120
  dispatch({ type: "SET_PANEL", open: true });
6817
7121
  };
6818
- return /* @__PURE__ */ jsxs14(Fragment8, { children: [
6819
- /* @__PURE__ */ jsx15("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
7122
+ return /* @__PURE__ */ jsxs15(Fragment9, { children: [
7123
+ /* @__PURE__ */ jsx16("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
6820
7124
  clusters.map((cluster) => {
6821
7125
  const isCluster = cluster.ids.length > 1;
6822
7126
  const isActive = activeItemId !== null && cluster.ids.includes(activeItemId);
@@ -6826,7 +7130,7 @@ function Pins() {
6826
7130
  const status = statuses.size === 1 ? members[0]?.status : void 0;
6827
7131
  const dimmed = filter !== null && members.length > 0 && !members.some((item) => itemMatchesFilter(item, filter));
6828
7132
  const statusHint = isCluster ? status ? ` \xB7 all ${STATUS_LABELS[status] ?? status}` : "" : ` \xB7 ${STATUS_LABELS[members[0]?.status ?? "open"] ?? members[0]?.status}`;
6829
- return /* @__PURE__ */ jsx15(
7133
+ return /* @__PURE__ */ jsx16(
6830
7134
  "div",
6831
7135
  {
6832
7136
  ref: (el) => {
@@ -6834,7 +7138,7 @@ function Pins() {
6834
7138
  else wrapRefs.current.delete(cluster.key);
6835
7139
  },
6836
7140
  className: `rk-pin-wrap${picking ? " rk-pin-ghost" : ""}`,
6837
- children: /* @__PURE__ */ jsx15(
7141
+ children: /* @__PURE__ */ jsx16(
6838
7142
  "button",
6839
7143
  {
6840
7144
  type: "button",
@@ -6856,7 +7160,7 @@ function Pins() {
6856
7160
  cluster.key
6857
7161
  );
6858
7162
  }),
6859
- orphanCount > 0 && /* @__PURE__ */ jsxs14(
7163
+ orphanCount > 0 && /* @__PURE__ */ jsxs15(
6860
7164
  "button",
6861
7165
  {
6862
7166
  type: "button",
@@ -6864,7 +7168,7 @@ function Pins() {
6864
7168
  style: { bottom: 16, left: "50%", transform: "translateX(-50%)" },
6865
7169
  onClick: () => dispatch({ type: "SET_PANEL", open: true }),
6866
7170
  children: [
6867
- /* @__PURE__ */ jsx15(IconUnlink, { size: 13 }),
7171
+ /* @__PURE__ */ jsx16(IconUnlink, { size: 13 }),
6868
7172
  orphanCount,
6869
7173
  " unanchored item",
6870
7174
  orphanCount === 1 ? "" : "s"
@@ -6875,13 +7179,13 @@ function Pins() {
6875
7179
  }
6876
7180
 
6877
7181
  // src/client/components/Toolbar.tsx
6878
- import { useEffect as useEffect14, useLayoutEffect as useLayoutEffect3, useRef as useRef15, useState as useState12 } from "react";
6879
- import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
7182
+ import { useEffect as useEffect15, useLayoutEffect as useLayoutEffect3, useRef as useRef15, useState as useState13 } from "react";
7183
+ import { Fragment as Fragment10, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6880
7184
  var TOOLS = [
6881
- { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ jsx16(IconTarget, { size: n }) },
6882
- { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ jsx16(IconText, { size: n }) },
6883
- { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ jsx16(IconComment, { size: n }) },
6884
- { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ jsx16(IconImage, { size: n }) }
7185
+ { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ jsx17(IconTarget, { size: n }) },
7186
+ { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ jsx17(IconText, { size: n }) },
7187
+ { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ jsx17(IconComment, { size: n }) },
7188
+ { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ jsx17(IconImage, { size: n }) }
6885
7189
  ];
6886
7190
  var POS_KEY2 = "review-kit:toolbar-pos";
6887
7191
  var DRAG_THRESHOLD2 = 6;
@@ -6890,9 +7194,9 @@ function Toolbar() {
6890
7194
  const hasPeers = syncStatus !== "disabled" && peers.length > 0;
6891
7195
  const dispatch = useDispatch();
6892
7196
  const ref = useRef15(null);
6893
- const [pos, setPos] = useState12(() => readStoredPos(POS_KEY2));
7197
+ const [pos, setPos] = useState13(() => readStoredPos(POS_KEY2));
6894
7198
  const drag = useRef15(null);
6895
- const [dragging, setDragging] = useState12(false);
7199
+ const [dragging, setDragging] = useState13(false);
6896
7200
  const justDragged = useRef15(false);
6897
7201
  const visible = editor === null || selection !== null;
6898
7202
  const applyPos = (next, persist) => {
@@ -6906,7 +7210,7 @@ function Toolbar() {
6906
7210
  const clamped = clampPos(pos.x, pos.y, el.offsetWidth, el.offsetHeight);
6907
7211
  if (clamped.x !== pos.x || clamped.y !== pos.y) applyPos(clamped, true);
6908
7212
  }, [pos, selection, hasPeers, visible]);
6909
- useEffect14(() => {
7213
+ useEffect15(() => {
6910
7214
  const onResize = () => {
6911
7215
  const el = ref.current;
6912
7216
  const current = readStoredPos(POS_KEY2);
@@ -7019,7 +7323,7 @@ function Toolbar() {
7019
7323
  mode === "pick" && selection === null ? "rk-ghost" : ""
7020
7324
  ].filter(Boolean).join(" ");
7021
7325
  const style = pos !== null ? { left: 0, top: 0, translate: `${pos.x}px ${pos.y}px` } : void 0;
7022
- return /* @__PURE__ */ jsxs15(
7326
+ return /* @__PURE__ */ jsxs16(
7023
7327
  "div",
7024
7328
  {
7025
7329
  ref,
@@ -7030,7 +7334,7 @@ function Toolbar() {
7030
7334
  onPointerDown,
7031
7335
  onClickCapture,
7032
7336
  children: [
7033
- /* @__PURE__ */ jsx16(
7337
+ /* @__PURE__ */ jsx17(
7034
7338
  "button",
7035
7339
  {
7036
7340
  type: "button",
@@ -7038,19 +7342,19 @@ function Toolbar() {
7038
7342
  "aria-label": "Move the toolbar \u2014 drag, or use arrow keys; double-click to reset its position",
7039
7343
  onKeyDown: onGripKeyDown,
7040
7344
  onDoubleClick: () => applyPos(null, true),
7041
- children: /* @__PURE__ */ jsxs15("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
7042
- /* @__PURE__ */ jsx16("circle", { cx: "2", cy: "2", r: "1.1" }),
7043
- /* @__PURE__ */ jsx16("circle", { cx: "6", cy: "2", r: "1.1" }),
7044
- /* @__PURE__ */ jsx16("circle", { cx: "2", cy: "7", r: "1.1" }),
7045
- /* @__PURE__ */ jsx16("circle", { cx: "6", cy: "7", r: "1.1" }),
7046
- /* @__PURE__ */ jsx16("circle", { cx: "2", cy: "12", r: "1.1" }),
7047
- /* @__PURE__ */ jsx16("circle", { cx: "6", cy: "12", r: "1.1" })
7345
+ children: /* @__PURE__ */ jsxs16("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
7346
+ /* @__PURE__ */ jsx17("circle", { cx: "2", cy: "2", r: "1.1" }),
7347
+ /* @__PURE__ */ jsx17("circle", { cx: "6", cy: "2", r: "1.1" }),
7348
+ /* @__PURE__ */ jsx17("circle", { cx: "2", cy: "7", r: "1.1" }),
7349
+ /* @__PURE__ */ jsx17("circle", { cx: "6", cy: "7", r: "1.1" }),
7350
+ /* @__PURE__ */ jsx17("circle", { cx: "2", cy: "12", r: "1.1" }),
7351
+ /* @__PURE__ */ jsx17("circle", { cx: "6", cy: "12", r: "1.1" })
7048
7352
  ] })
7049
7353
  }
7050
7354
  ),
7051
- /* @__PURE__ */ jsx16(PresenceStrip, {}),
7052
- hasPeers && /* @__PURE__ */ jsx16("span", { className: "rk-toolbar-sep" }),
7053
- /* @__PURE__ */ jsx16("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ jsx16(
7355
+ /* @__PURE__ */ jsx17(PresenceStrip, {}),
7356
+ hasPeers && /* @__PURE__ */ jsx17("span", { className: "rk-toolbar-sep" }),
7357
+ /* @__PURE__ */ jsx17("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ jsx17(
7054
7358
  "button",
7055
7359
  {
7056
7360
  type: "button",
@@ -7062,9 +7366,9 @@ function Toolbar() {
7062
7366
  },
7063
7367
  entry.tool
7064
7368
  )) }),
7065
- selection && /* @__PURE__ */ jsx16("span", { className: "rk-toolbar-sep" }),
7066
- selection && /* @__PURE__ */ jsxs15(Fragment9, { children: [
7067
- /* @__PURE__ */ jsxs15(
7369
+ selection && /* @__PURE__ */ jsx17("span", { className: "rk-toolbar-sep" }),
7370
+ selection && /* @__PURE__ */ jsxs16(Fragment10, { children: [
7371
+ /* @__PURE__ */ jsxs16(
7068
7372
  "button",
7069
7373
  {
7070
7374
  type: "button",
@@ -7073,12 +7377,12 @@ function Toolbar() {
7073
7377
  title: `Rewrite text${keyHint("tool-copy")}`,
7074
7378
  onClick: () => open("copy"),
7075
7379
  children: [
7076
- /* @__PURE__ */ jsx16(IconText, { size: 14 }),
7380
+ /* @__PURE__ */ jsx17(IconText, { size: 14 }),
7077
7381
  " Rewrite"
7078
7382
  ]
7079
7383
  }
7080
7384
  ),
7081
- hasImage && /* @__PURE__ */ jsxs15(
7385
+ hasImage && /* @__PURE__ */ jsxs16(
7082
7386
  "button",
7083
7387
  {
7084
7388
  type: "button",
@@ -7087,12 +7391,12 @@ function Toolbar() {
7087
7391
  title: `Replace image${keyHint("tool-image")}`,
7088
7392
  onClick: () => open("image"),
7089
7393
  children: [
7090
- /* @__PURE__ */ jsx16(IconImage, { size: 14 }),
7394
+ /* @__PURE__ */ jsx17(IconImage, { size: 14 }),
7091
7395
  " Image"
7092
7396
  ]
7093
7397
  }
7094
7398
  ),
7095
- /* @__PURE__ */ jsxs15(
7399
+ /* @__PURE__ */ jsxs16(
7096
7400
  "button",
7097
7401
  {
7098
7402
  type: "button",
@@ -7101,32 +7405,32 @@ function Toolbar() {
7101
7405
  title: `Comment${keyHint("tool-comment")}`,
7102
7406
  onClick: () => open("comment"),
7103
7407
  children: [
7104
- /* @__PURE__ */ jsx16(IconComment, { size: 14 }),
7408
+ /* @__PURE__ */ jsx17(IconComment, { size: 14 }),
7105
7409
  " Comment"
7106
7410
  ]
7107
7411
  }
7108
7412
  ),
7109
- /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
7110
- /* @__PURE__ */ jsx16(IconStyle, { size: 14 }),
7413
+ /* @__PURE__ */ jsxs16("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
7414
+ /* @__PURE__ */ jsx17(IconStyle, { size: 14 }),
7111
7415
  " Style"
7112
7416
  ] }),
7113
- /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
7114
- /* @__PURE__ */ jsx16(IconMove, { size: 14 }),
7417
+ /* @__PURE__ */ jsxs16("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
7418
+ /* @__PURE__ */ jsx17(IconMove, { size: 14 }),
7115
7419
  " Move"
7116
7420
  ] }),
7117
- /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
7118
- /* @__PURE__ */ jsx16(IconTrash, { size: 14 }),
7421
+ /* @__PURE__ */ jsxs16("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
7422
+ /* @__PURE__ */ jsx17(IconTrash, { size: 14 }),
7119
7423
  " Delete"
7120
7424
  ] }),
7121
- /* @__PURE__ */ jsx16("span", { className: "rk-toolbar-sep" }),
7122
- /* @__PURE__ */ jsx16(
7425
+ /* @__PURE__ */ jsx17("span", { className: "rk-toolbar-sep" }),
7426
+ /* @__PURE__ */ jsx17(
7123
7427
  "button",
7124
7428
  {
7125
7429
  type: "button",
7126
7430
  className: "rk-icon-btn",
7127
7431
  "aria-label": "Deselect",
7128
7432
  onClick: () => dispatch({ type: "SELECT", selection: null }),
7129
- children: /* @__PURE__ */ jsx16(IconClose, { size: 13 })
7433
+ children: /* @__PURE__ */ jsx17(IconClose, { size: 13 })
7130
7434
  }
7131
7435
  )
7132
7436
  ] })
@@ -7136,27 +7440,27 @@ function Toolbar() {
7136
7440
  }
7137
7441
 
7138
7442
  // src/client/components/Toast.tsx
7139
- import { useEffect as useEffect15 } from "react";
7140
- import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
7443
+ import { useEffect as useEffect16 } from "react";
7444
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
7141
7445
  function ToastRow({ toast: toast2 }) {
7142
7446
  const dispatch = useDispatch();
7143
- useEffect15(() => {
7447
+ useEffect16(() => {
7144
7448
  const timer = window.setTimeout(() => dispatch({ type: "DISMISS_TOAST", id: toast2.id }), 3500);
7145
7449
  return () => window.clearTimeout(timer);
7146
7450
  }, [toast2.id, dispatch]);
7147
- return /* @__PURE__ */ jsxs16("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
7148
- toast2.tone === "success" ? /* @__PURE__ */ jsx17(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ jsx17(IconWarning, { size: 13 }) : /* @__PURE__ */ jsx17(IconComment, { size: 13 }),
7451
+ return /* @__PURE__ */ jsxs17("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
7452
+ toast2.tone === "success" ? /* @__PURE__ */ jsx18(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ jsx18(IconWarning, { size: 13 }) : /* @__PURE__ */ jsx18(IconComment, { size: 13 }),
7149
7453
  toast2.message
7150
7454
  ] });
7151
7455
  }
7152
7456
  function ToastHost() {
7153
7457
  const { toasts } = useStore();
7154
7458
  if (toasts.length === 0) return null;
7155
- return /* @__PURE__ */ jsx17("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ jsx17(ToastRow, { toast: toast2 }, toast2.id)) });
7459
+ return /* @__PURE__ */ jsx18("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ jsx18(ToastRow, { toast: toast2 }, toast2.id)) });
7156
7460
  }
7157
7461
 
7158
7462
  // src/client/App.tsx
7159
- import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
7463
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
7160
7464
  function App() {
7161
7465
  const state = useStore();
7162
7466
  const rawDispatch = useDispatch();
@@ -7166,10 +7470,10 @@ function App() {
7166
7470
  const storageKey = config.storageKey ?? defaultStorageKey();
7167
7471
  useAppliedPreview(state, dispatch);
7168
7472
  useEmblemaAutoPush(state, dispatch, config);
7169
- useEffect16(() => {
7473
+ useEffect17(() => {
7170
7474
  setEffectAssetsUrl(config.fxAssetsUrl);
7171
7475
  }, [config.fxAssetsUrl]);
7172
- useEffect16(() => {
7476
+ useEffect17(() => {
7173
7477
  let cancelled = false;
7174
7478
  void loadSession(storageKey).then((stored) => {
7175
7479
  if (cancelled) return;
@@ -7187,7 +7491,7 @@ function App() {
7187
7491
  };
7188
7492
  }, [storageKey, config.defaultAuthor, dispatch]);
7189
7493
  const { session } = state;
7190
- useEffect16(() => {
7494
+ useEffect17(() => {
7191
7495
  if (!session) return;
7192
7496
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
7193
7497
  saveTimer.current = window.setTimeout(() => {
@@ -7198,9 +7502,9 @@ function App() {
7198
7502
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
7199
7503
  };
7200
7504
  }, [session, storageKey, config]);
7201
- useEffect16(() => trackPointer(), []);
7505
+ useEffect17(() => trackPointer(), []);
7202
7506
  const focusHandled = useRef16(false);
7203
- useEffect16(() => {
7507
+ useEffect17(() => {
7204
7508
  if (!session || focusHandled.current) return;
7205
7509
  focusHandled.current = true;
7206
7510
  const id = takeFocusItem();
@@ -7225,7 +7529,7 @@ function App() {
7225
7529
  cancelled = true;
7226
7530
  };
7227
7531
  }, [session, dispatch]);
7228
- useEffect16(() => {
7532
+ useEffect17(() => {
7229
7533
  let last = window.location.pathname;
7230
7534
  const check = () => {
7231
7535
  if (window.location.pathname !== last) {
@@ -7242,7 +7546,7 @@ function App() {
7242
7546
  }, [dispatch]);
7243
7547
  const stateRef = useRef16(state);
7244
7548
  stateRef.current = state;
7245
- useEffect16(() => {
7549
+ useEffect17(() => {
7246
7550
  const onKeyDown = (event) => {
7247
7551
  const current = stateRef.current;
7248
7552
  for (const def of SHORTCUTS) {
@@ -7257,7 +7561,7 @@ function App() {
7257
7561
  document.addEventListener("keydown", onKeyDown);
7258
7562
  return () => document.removeEventListener("keydown", onKeyDown);
7259
7563
  }, [dispatch]);
7260
- useEffect16(() => {
7564
+ useEffect17(() => {
7261
7565
  if (!state.editor) return;
7262
7566
  const editorEl = state.editor.element;
7263
7567
  const onPointerDown = (event) => {
@@ -7270,32 +7574,33 @@ function App() {
7270
7574
  }, [state.editor, dispatch]);
7271
7575
  if (!session) return null;
7272
7576
  if (state.hidden) {
7273
- return /* @__PURE__ */ jsx18(DispatchContext.Provider, { value: dispatch, children: null });
7577
+ return /* @__PURE__ */ jsx19(DispatchContext.Provider, { value: dispatch, children: null });
7274
7578
  }
7275
- return /* @__PURE__ */ jsxs17(DispatchContext.Provider, { value: dispatch, children: [
7276
- /* @__PURE__ */ jsx18(PickLayer, {}),
7277
- /* @__PURE__ */ jsx18(PeerCursors, {}),
7278
- /* @__PURE__ */ jsx18(Toolbar, {}),
7279
- state.editor?.kind === "copy" && /* @__PURE__ */ jsx18(InlineEditor, { editor: state.editor }),
7280
- state.editor?.kind === "image" && /* @__PURE__ */ jsx18(ImageEditor, { editor: state.editor }),
7281
- state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ jsx18(NoteComposer, { editor: state.editor }),
7282
- /* @__PURE__ */ jsx18(Pins, {}),
7283
- /* @__PURE__ */ jsx18(QuickComment, {}),
7284
- /* @__PURE__ */ jsx18(Panel, {}),
7285
- /* @__PURE__ */ jsx18(Badge, {}),
7286
- /* @__PURE__ */ jsx18(CoachMark, {}),
7287
- /* @__PURE__ */ jsx18(CheatSheet, {}),
7288
- /* @__PURE__ */ jsx18(EmblemaConnect, {}),
7289
- /* @__PURE__ */ jsx18(ConfirmHost, {}),
7290
- /* @__PURE__ */ jsx18(ToastHost, {})
7579
+ return /* @__PURE__ */ jsxs18(DispatchContext.Provider, { value: dispatch, children: [
7580
+ /* @__PURE__ */ jsx19(PickLayer, {}),
7581
+ /* @__PURE__ */ jsx19(PeerCursors, {}),
7582
+ /* @__PURE__ */ jsx19(Toolbar, {}),
7583
+ state.editor?.kind === "copy" && /* @__PURE__ */ jsx19(InlineEditor, { editor: state.editor }),
7584
+ state.editor?.kind === "image" && /* @__PURE__ */ jsx19(ImageEditor, { editor: state.editor }),
7585
+ state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ jsx19(NoteComposer, { editor: state.editor }),
7586
+ /* @__PURE__ */ jsx19(Pins, {}),
7587
+ /* @__PURE__ */ jsx19(QuickComment, {}),
7588
+ /* @__PURE__ */ jsx19(Panel, {}),
7589
+ /* @__PURE__ */ jsx19(Badge, {}),
7590
+ /* @__PURE__ */ jsx19(CoachMark, {}),
7591
+ /* @__PURE__ */ jsx19(CheatSheet, {}),
7592
+ /* @__PURE__ */ jsx19(EmblemaConnect, {}),
7593
+ /* @__PURE__ */ jsx19(TerminalConnect, {}),
7594
+ /* @__PURE__ */ jsx19(ConfirmHost, {}),
7595
+ /* @__PURE__ */ jsx19(ToastHost, {})
7291
7596
  ] });
7292
7597
  }
7293
7598
 
7294
7599
  // src/client/ReviewKit.tsx
7295
- import { jsx as jsx19 } from "react/jsx-runtime";
7600
+ import { jsx as jsx20 } from "react/jsx-runtime";
7296
7601
  function ShadowHost({ config }) {
7297
- const [mount, setMount] = useState13(null);
7298
- useEffect17(() => {
7602
+ const [mount, setMount] = useState14(null);
7603
+ useEffect18(() => {
7299
7604
  const host = document.createElement("div");
7300
7605
  host.setAttribute(HOST_ATTR, "");
7301
7606
  host.style.position = "fixed";
@@ -7331,15 +7636,15 @@ function ShadowHost({ config }) {
7331
7636
  }, []);
7332
7637
  if (!mount) return null;
7333
7638
  return createPortal(
7334
- /* @__PURE__ */ jsx19(StoreProvider, { config, children: /* @__PURE__ */ jsx19(App, {}) }),
7639
+ /* @__PURE__ */ jsx20(StoreProvider, { config, children: /* @__PURE__ */ jsx20(App, {}) }),
7335
7640
  mount
7336
7641
  );
7337
7642
  }
7338
7643
  function ReviewKit(props) {
7339
- const [active, setActive] = useState13(false);
7644
+ const [active, setActive] = useState14(false);
7340
7645
  const { enabled, token, allowedHosts, devAutoOn } = props;
7341
7646
  const hostsKey = allowedHosts?.join("\0") ?? "";
7342
- useEffect17(() => {
7647
+ useEffect18(() => {
7343
7648
  if (enabled === false) {
7344
7649
  setActive(false);
7345
7650
  return;
@@ -7350,7 +7655,7 @@ function ReviewKit(props) {
7350
7655
  return () => window.removeEventListener(DEACTIVATE_EVENT, onDeactivate);
7351
7656
  }, [enabled, token, devAutoOn, hostsKey]);
7352
7657
  if (enabled === false || !active) return null;
7353
- return /* @__PURE__ */ jsx19(ShadowHost, { config: props });
7658
+ return /* @__PURE__ */ jsx20(ShadowHost, { config: props });
7354
7659
  }
7355
7660
 
7356
7661
  export {
@@ -7363,4 +7668,4 @@ export {
7363
7668
  importSessionFile,
7364
7669
  ReviewKit
7365
7670
  };
7366
- //# sourceMappingURL=chunk-NSXRFM75.js.map
7671
+ //# sourceMappingURL=chunk-VNRVRNZX.js.map