@satanwagen/reviewkit 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,18 @@ 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 resolveSyncUrl(config) {
3754
+ return typeof config.syncUrl === "string" && config.syncUrl !== "" ? config.syncUrl : DEFAULT_SYNC_URL;
3755
+ }
3756
+ var MIN_SYNC_TOKEN_LENGTH = 8;
3757
+ var warnedShortToken = false;
3758
+ function resolveSyncToken(config) {
3759
+ if (config.sync === false) return null;
3760
+ const explicit = typeof config.syncToken === "string" && config.syncToken !== "" ? config.syncToken : null;
3761
+ const fallback = typeof config.token === "string" && config.token !== "" ? config.token : null;
3762
+ return explicit ?? fallback;
3763
+ }
3739
3764
  function toPeerState(peer) {
3740
3765
  return {
3741
3766
  id: peer.id,
@@ -3763,20 +3788,43 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
3763
3788
  const stateRef = useRef3(state);
3764
3789
  stateRef.current = state;
3765
3790
  const myIdRef = useRef3("");
3766
- const enabled = typeof config.syncToken === "string" && config.syncToken !== "" && typeof config.syncUrl === "string" && config.syncUrl !== "";
3791
+ const syncToken = resolveSyncToken(config);
3792
+ const syncUrl = resolveSyncUrl(config);
3793
+ const enabled = syncToken !== null;
3767
3794
  const author = state.session?.author ?? "";
3768
3795
  const hasSession = state.session !== null;
3769
3796
  useEffect3(() => {
3770
3797
  if (!enabled || !hasSession) return;
3798
+ const dispatchGuard = rawDispatch;
3799
+ if (syncToken.length < MIN_SYNC_TOKEN_LENGTH) {
3800
+ dispatchGuard({ type: "SYNC_STATUS", status: "offline", reason: "token-short" });
3801
+ if (!warnedShortToken) {
3802
+ warnedShortToken = true;
3803
+ console.warn(
3804
+ `[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.`
3805
+ );
3806
+ }
3807
+ return () => dispatchGuard({ type: "SYNC_STATUS", status: "disabled", reason: null });
3808
+ }
3771
3809
  const id = reviewerId();
3772
3810
  myIdRef.current = id;
3773
3811
  const dispatch = rawDispatch;
3774
3812
  const sync = createRkSync({
3775
- token: config.syncToken,
3776
- url: config.syncUrl,
3813
+ token: syncToken,
3814
+ url: syncUrl,
3777
3815
  user: { id, name: author || "Reviewer", color: colorForReviewer(id) },
3778
3816
  handlers: {
3779
- onStatus: (status) => dispatch({ type: "SYNC_STATUS", status }),
3817
+ onStatus: (status) => dispatch({
3818
+ type: "SYNC_STATUS",
3819
+ status,
3820
+ // Offline with no server verdict = the socket never got through
3821
+ // (server down, CSP connect-src, network); a verdict keeps its reason.
3822
+ ...status === "offline" ? { reason: stateRef.current.syncReason ?? "unreachable" } : {}
3823
+ }),
3824
+ onError: (code) => {
3825
+ if (code === "bad-token") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "bad-token" });
3826
+ else if (code === "room-full") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "room-full" });
3827
+ },
3780
3828
  onSnapshot: (items, peers, deletedIds = []) => {
3781
3829
  const safeItems = items.map(sanitizeRemoteItem).filter((item) => item !== null);
3782
3830
  dispatch({ type: "SYNC_SNAPSHOT", items: safeItems });
@@ -3833,10 +3881,10 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
3833
3881
  if (activeSyncRef === sync) activeSyncRef = null;
3834
3882
  setEffectBroadcaster(null);
3835
3883
  sync.stop();
3836
- dispatch({ type: "SYNC_STATUS", status: "disabled" });
3884
+ dispatch({ type: "SYNC_STATUS", status: "disabled", reason: null });
3837
3885
  dispatch({ type: "PEERS_SET", peers: [] });
3838
3886
  };
3839
- }, [enabled, hasSession, config.syncToken, config.syncUrl, author, rawDispatch]);
3887
+ }, [enabled, hasSession, syncToken, syncUrl, author, rawDispatch]);
3840
3888
  const wrapped = useMemo2(() => {
3841
3889
  return (action) => {
3842
3890
  const prev = stateRef.current;
@@ -4352,10 +4400,10 @@ function EmblemaConnect() {
4352
4400
  state === null && /* @__PURE__ */ jsxs4("div", { className: "rk-modal-text", children: [
4353
4401
  "The local sync agent isn't running. In the project repo run:",
4354
4402
  /* @__PURE__ */ jsxs4("code", { className: "rk-connect-cmd", children: [
4355
- "npx @satanwagen/reviewkit-cli init --origin ",
4403
+ "npx -p @satanwagen/reviewkit-cli reviewkit-emblema init --origin ",
4356
4404
  window.location.origin
4357
4405
  ] }),
4358
- /* @__PURE__ */ jsx5("code", { className: "rk-connect-cmd", children: "npx @satanwagen/reviewkit-cli serve" }),
4406
+ /* @__PURE__ */ jsx5("code", { className: "rk-connect-cmd", children: "npx -p @satanwagen/reviewkit-cli reviewkit-emblema serve" }),
4359
4407
  "then reopen this dialog."
4360
4408
  ] }),
4361
4409
  state !== null && state !== "loading" && /* @__PURE__ */ jsxs4(Fragment2, { children: [
@@ -4442,15 +4490,229 @@ function EmblemaConnect() {
4442
4490
  ] });
4443
4491
  }
4444
4492
 
4493
+ // src/client/components/TerminalConnect.tsx
4494
+ import { useEffect as useEffect5, useState as useState3 } from "react";
4495
+
4496
+ // src/integrations/terminal.ts
4497
+ var TERMINAL_PAIR_PORTS = [48780, 48781, 48782, 48783];
4498
+ async function fetchJson(url, init, timeoutMs) {
4499
+ const abort = new AbortController();
4500
+ const timer = window.setTimeout(() => abort.abort(), timeoutMs);
4501
+ try {
4502
+ return await fetch(url, { ...init, signal: abort.signal });
4503
+ } finally {
4504
+ window.clearTimeout(timer);
4505
+ }
4506
+ }
4507
+ async function probeTerminal() {
4508
+ for (const port of TERMINAL_PAIR_PORTS) {
4509
+ try {
4510
+ const res = await fetchJson(`http://127.0.0.1:${port}/pair`, { method: "GET" }, 900);
4511
+ if (!res.ok) continue;
4512
+ const body = await res.json();
4513
+ if (typeof body === "object" && body !== null && body.app === "reviewkit-cli") {
4514
+ const b = body;
4515
+ return {
4516
+ port,
4517
+ cwd: typeof b.cwd === "string" ? b.cwd : void 0,
4518
+ projects: Array.isArray(b.projects) ? b.projects.filter((p) => typeof p === "string") : []
4519
+ };
4520
+ }
4521
+ } catch {
4522
+ }
4523
+ }
4524
+ return null;
4525
+ }
4526
+ async function pairTerminal(port, key) {
4527
+ try {
4528
+ const res = await fetchJson(
4529
+ `http://127.0.0.1:${port}/pair`,
4530
+ { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ key }) },
4531
+ 5e3
4532
+ );
4533
+ const body = await res.json().catch(() => null);
4534
+ if (res.ok && body?.ok) return { ok: true, name: body.name ?? "", repo: body.repo ?? null };
4535
+ return { ok: false, error: body?.error ?? `HTTP ${res.status}` };
4536
+ } catch {
4537
+ return { ok: false, error: "the terminal stopped answering" };
4538
+ }
4539
+ }
4540
+
4541
+ // src/client/terminalKey.ts
4542
+ var TERMINAL_KEY_PREFIX = "rkc_";
4543
+ var TERMINAL_KEY_VERSION = 1;
4544
+ function base64url(text) {
4545
+ const bytes = new TextEncoder().encode(text);
4546
+ let binary = "";
4547
+ for (const byte of bytes) binary += String.fromCharCode(byte);
4548
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4549
+ }
4550
+ function encodeTerminalKey(fields) {
4551
+ const payload = { v: TERMINAL_KEY_VERSION, ...fields };
4552
+ return TERMINAL_KEY_PREFIX + base64url(JSON.stringify(payload));
4553
+ }
4554
+ function currentTerminalKey(syncUrl, syncToken) {
4555
+ const origin = window.location.origin;
4556
+ let route = "/";
4557
+ try {
4558
+ route = window.location.pathname || "/";
4559
+ } catch {
4560
+ }
4561
+ return encodeTerminalKey({ name: window.location.hostname, origin, syncUrl, syncToken, route });
4562
+ }
4563
+
4564
+ // src/client/components/TerminalConnect.tsx
4565
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4566
+ function shortKey2(key) {
4567
+ return `${key.slice(0, 8)}\u2026${key.slice(-6)}`;
4568
+ }
4569
+ function shortPath(path) {
4570
+ if (!path) return "";
4571
+ const parts = path.split("/").filter(Boolean);
4572
+ return parts.length > 2 ? `\u2026/${parts.slice(-2).join("/")}` : path;
4573
+ }
4574
+ function TerminalConnect() {
4575
+ const { terminalConnectOpen } = useStore();
4576
+ const config = useConfig();
4577
+ const dispatch = useDispatch();
4578
+ const [phase, setPhase] = useState3("probing");
4579
+ const [probe, setProbe] = useState3(null);
4580
+ const [paired, setPaired] = useState3(null);
4581
+ const [showManual, setShowManual] = useState3(false);
4582
+ const syncToken = resolveSyncToken(config);
4583
+ const key = terminalConnectOpen && syncToken ? currentTerminalKey(resolveSyncUrl(config), syncToken) : null;
4584
+ useEffect5(() => {
4585
+ if (!terminalConnectOpen) return;
4586
+ let cancelled = false;
4587
+ setPhase("probing");
4588
+ setProbe(null);
4589
+ setPaired(null);
4590
+ setShowManual(false);
4591
+ const look = () => void probeTerminal().then((found) => {
4592
+ if (cancelled) return;
4593
+ setProbe(found);
4594
+ setPhase((current) => current === "pairing" || current === "paired" ? current : found ? "found" : "missing");
4595
+ });
4596
+ look();
4597
+ const timer = window.setInterval(look, 2e3);
4598
+ return () => {
4599
+ cancelled = true;
4600
+ window.clearInterval(timer);
4601
+ };
4602
+ }, [terminalConnectOpen]);
4603
+ if (!terminalConnectOpen) return null;
4604
+ const close = () => dispatch({ type: "SET_TERMINAL_CONNECT", open: false });
4605
+ const copy = async (value, what) => {
4606
+ try {
4607
+ await navigator.clipboard.writeText(value);
4608
+ toast(dispatch, `${what} copied`, "success");
4609
+ } catch {
4610
+ toast(dispatch, "Copy failed \u2014 select the text manually", "error");
4611
+ }
4612
+ };
4613
+ const connect = () => {
4614
+ if (!probe || !key) return;
4615
+ setPhase("pairing");
4616
+ void pairTerminal(probe.port, key).then((result) => {
4617
+ if (result.ok) {
4618
+ setPaired({ name: result.name, repo: result.repo });
4619
+ setPhase("paired");
4620
+ toast(dispatch, `Terminal paired as \u201C${result.name}\u201D`, "success");
4621
+ } else {
4622
+ setPhase("found");
4623
+ toast(dispatch, `Pairing failed: ${result.error}`, "error");
4624
+ }
4625
+ });
4626
+ };
4627
+ const connectCmd = key ? `npx -y @satanwagen/reviewkit-cli connect ${key}` : "";
4628
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4629
+ /* @__PURE__ */ jsx6("div", { className: "rk-backdrop", onClick: close }),
4630
+ /* @__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: [
4631
+ /* @__PURE__ */ jsxs5("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4632
+ "Connect terminal / agent",
4633
+ /* @__PURE__ */ jsx6("span", { style: { flex: 1 } }),
4634
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-icon-btn", "aria-label": "Close", onClick: close, autoFocus: true, children: /* @__PURE__ */ jsx6(IconClose, { size: 13 }) })
4635
+ ] }),
4636
+ !key && /* @__PURE__ */ jsxs5("div", { className: "rk-modal-text", children: [
4637
+ "Live sync is off on this page (",
4638
+ /* @__PURE__ */ jsx6("code", { children: "sync=false" }),
4639
+ " or no token), so there is no shared room a terminal could read."
4640
+ ] }),
4641
+ key && phase === "paired" && paired && /* @__PURE__ */ jsxs5("div", { className: "rk-modal-text", children: [
4642
+ /* @__PURE__ */ jsx6("span", { className: "rk-emblema-dot", "data-state": "live" }),
4643
+ " Paired as ",
4644
+ /* @__PURE__ */ jsx6("strong", { children: paired.name }),
4645
+ paired.repo ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
4646
+ " in ",
4647
+ /* @__PURE__ */ jsx6("code", { children: shortPath(paired.repo) })
4648
+ ] }) : null,
4649
+ ". The terminal shows this page's comments live, and Claude Code / Cursor can read and resolve them through the ",
4650
+ /* @__PURE__ */ jsx6("code", { children: "reviewkit" }),
4651
+ " MCP tools."
4652
+ ] }),
4653
+ key && phase !== "paired" && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4654
+ /* @__PURE__ */ jsxs5("div", { className: "rk-modal-text", children: [
4655
+ phase === "probing" && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4656
+ "Looking for a ",
4657
+ /* @__PURE__ */ jsx6("code", { children: "reviewkit" }),
4658
+ " terminal on this machine\u2026"
4659
+ ] }),
4660
+ phase === "missing" && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4661
+ "No terminal is listening yet. In the site's repo run",
4662
+ /* @__PURE__ */ jsx6("code", { className: "rk-connect-cmd", children: "npx -y @satanwagen/reviewkit-cli setup" }),
4663
+ "and this dialog will pick it up by itself."
4664
+ ] }),
4665
+ (phase === "found" || phase === "pairing") && probe && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4666
+ /* @__PURE__ */ jsx6("span", { className: "rk-emblema-dot", "data-state": "live" }),
4667
+ " Terminal found",
4668
+ probe.cwd ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
4669
+ " in ",
4670
+ /* @__PURE__ */ jsx6("code", { children: shortPath(probe.cwd) })
4671
+ ] }) : null,
4672
+ ".",
4673
+ probe.projects.length > 0 ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
4674
+ " Already paired: ",
4675
+ probe.projects.join(", "),
4676
+ "."
4677
+ ] }) : null
4678
+ ] })
4679
+ ] }),
4680
+ /* @__PURE__ */ jsxs5("div", { className: "rk-connect-actions", children: [
4681
+ /* @__PURE__ */ jsx6(
4682
+ "button",
4683
+ {
4684
+ type: "button",
4685
+ className: "rk-btn rk-btn-primary",
4686
+ disabled: phase !== "found",
4687
+ onClick: connect,
4688
+ title: "Send this site's connection to the terminal \u2014 nothing to copy",
4689
+ children: phase === "pairing" ? "Connecting\u2026" : "Connect this terminal"
4690
+ }
4691
+ ),
4692
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => setShowManual((v) => !v), children: showManual ? "Hide manual option" : "Another machine\u2026" })
4693
+ ] }),
4694
+ showManual && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4695
+ /* @__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):" }),
4696
+ /* @__PURE__ */ jsxs5("code", { className: "rk-connect-cmd", title: connectCmd, children: [
4697
+ "npx -y @satanwagen/reviewkit-cli connect ",
4698
+ shortKey2(key)
4699
+ ] }),
4700
+ /* @__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" }) })
4701
+ ] })
4702
+ ] })
4703
+ ] }) })
4704
+ ] });
4705
+ }
4706
+
4445
4707
  // src/client/components/CoachMark.tsx
4446
- import { useState as useState3 } from "react";
4447
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4708
+ import { useState as useState4 } from "react";
4709
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4448
4710
  var COACH_KEY = "review-kit:coach-dismissed";
4449
4711
  function CoachMark() {
4450
4712
  const { coachOpen, session } = useStore();
4451
4713
  const dispatch = useDispatch();
4452
4714
  const config = useConfig();
4453
- const [name, setName] = useState3(session?.author ?? config.defaultAuthor ?? "");
4715
+ const [name, setName] = useState4(session?.author ?? config.defaultAuthor ?? "");
4454
4716
  if (!coachOpen) return null;
4455
4717
  const done = (startPicking) => {
4456
4718
  if (name.trim() !== "") dispatch({ type: "SET_AUTHOR", author: name.trim() });
@@ -4461,39 +4723,39 @@ function CoachMark() {
4461
4723
  }
4462
4724
  if (startPicking) dispatch({ type: "SET_MODE", mode: "pick" });
4463
4725
  };
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 }),
4726
+ return /* @__PURE__ */ jsxs6("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
4727
+ /* @__PURE__ */ jsx7("div", { className: "rk-coach-accent" }),
4728
+ /* @__PURE__ */ jsxs6("div", { className: "rk-coach-body", children: [
4729
+ /* @__PURE__ */ jsxs6("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4730
+ /* @__PURE__ */ jsx7(IconTarget, { size: 16 }),
4469
4731
  " Review mode"
4470
4732
  ] }),
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" }),
4733
+ /* @__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." }),
4734
+ /* @__PURE__ */ jsxs6("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
4735
+ /* @__PURE__ */ jsxs6("span", { children: [
4736
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "T" }),
4475
4737
  " rewrite"
4476
4738
  ] }),
4477
- /* @__PURE__ */ jsxs5("span", { children: [
4478
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "C" }),
4739
+ /* @__PURE__ */ jsxs6("span", { children: [
4740
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "C" }),
4479
4741
  " comment"
4480
4742
  ] }),
4481
- /* @__PURE__ */ jsxs5("span", { children: [
4482
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "I" }),
4743
+ /* @__PURE__ */ jsxs6("span", { children: [
4744
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "I" }),
4483
4745
  " image"
4484
4746
  ] }),
4485
- /* @__PURE__ */ jsxs5("span", { children: [
4486
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "V" }),
4747
+ /* @__PURE__ */ jsxs6("span", { children: [
4748
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "V" }),
4487
4749
  " select"
4488
4750
  ] }),
4489
- /* @__PURE__ */ jsxs5("span", { children: [
4490
- /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "?" }),
4751
+ /* @__PURE__ */ jsxs6("span", { children: [
4752
+ /* @__PURE__ */ jsx7("span", { className: "rk-kbd", children: "?" }),
4491
4753
  " all shortcuts"
4492
4754
  ] })
4493
4755
  ] }),
4494
- /* @__PURE__ */ jsxs5("div", { children: [
4495
- /* @__PURE__ */ jsx6("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
4496
- /* @__PURE__ */ jsx6(
4756
+ /* @__PURE__ */ jsxs6("div", { children: [
4757
+ /* @__PURE__ */ jsx7("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
4758
+ /* @__PURE__ */ jsx7(
4497
4759
  "input",
4498
4760
  {
4499
4761
  id: "rk-author",
@@ -4507,36 +4769,36 @@ function CoachMark() {
4507
4769
  }
4508
4770
  )
4509
4771
  ] }),
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" })
4772
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
4773
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
4774
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
4513
4775
  ] })
4514
4776
  ] })
4515
4777
  ] });
4516
4778
  }
4517
4779
 
4518
4780
  // 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";
4781
+ import { useEffect as useEffect6, useRef as useRef5 } from "react";
4782
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4521
4783
  function ConfirmHost() {
4522
4784
  const { confirm } = useStore();
4523
4785
  const dispatch = useDispatch();
4524
4786
  const cancelRef = useRef5(null);
4525
- useEffect5(() => {
4787
+ useEffect6(() => {
4526
4788
  if (confirm) cancelRef.current?.focus();
4527
4789
  }, [confirm]);
4528
4790
  if (!confirm) return null;
4529
4791
  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 })
4792
+ return /* @__PURE__ */ jsxs7(Fragment4, { children: [
4793
+ /* @__PURE__ */ jsx8("div", { className: "rk-backdrop", onClick: close }),
4794
+ /* @__PURE__ */ jsxs7("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
4795
+ /* @__PURE__ */ jsxs7("div", { className: "rk-modal-body", children: [
4796
+ /* @__PURE__ */ jsx8("div", { className: "rk-modal-title", children: confirm.title }),
4797
+ /* @__PURE__ */ jsx8("div", { className: "rk-modal-text", children: confirm.message })
4536
4798
  ] }),
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(
4799
+ /* @__PURE__ */ jsxs7("div", { className: "rk-modal-actions", children: [
4800
+ /* @__PURE__ */ jsx8("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
4801
+ /* @__PURE__ */ jsx8(
4540
4802
  "button",
4541
4803
  {
4542
4804
  type: "button",
@@ -4555,10 +4817,10 @@ function ConfirmHost() {
4555
4817
  }
4556
4818
 
4557
4819
  // src/client/components/ImageEditor.tsx
4558
- import { useEffect as useEffect6, useRef as useRef7, useState as useState5 } from "react";
4820
+ import { useEffect as useEffect7, useRef as useRef7, useState as useState6 } from "react";
4559
4821
 
4560
4822
  // src/client/useAnchored.ts
4561
- import { useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState4 } from "react";
4823
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState5 } from "react";
4562
4824
 
4563
4825
  // src/client/position.ts
4564
4826
  function place(anchor, size, side, offset) {
@@ -4604,7 +4866,7 @@ function computePosition(anchor, size, options = {}) {
4604
4866
  // src/client/useAnchored.ts
4605
4867
  function useAnchored(anchorEl, side = "bottom", offset = 8) {
4606
4868
  const ref = useRef6(null);
4607
- const [pos, setPos] = useState4(null);
4869
+ const [pos, setPos] = useState5(null);
4608
4870
  useLayoutEffect2(() => {
4609
4871
  if (!anchorEl) {
4610
4872
  setPos(null);
@@ -4636,7 +4898,7 @@ function anchoredStyle(pos) {
4636
4898
  }
4637
4899
 
4638
4900
  // src/client/components/ImageEditor.tsx
4639
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4901
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4640
4902
  var MAX_BYTES = 2 * 1024 * 1024;
4641
4903
  var FETCH_TIMEOUT_MS = 12e3;
4642
4904
  var PROBE_TIMEOUT_MS = 8e3;
@@ -4694,12 +4956,12 @@ function urlFromDataTransfer(dt) {
4694
4956
  function ImageEditor({ editor }) {
4695
4957
  const dispatch = useDispatch();
4696
4958
  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);
4959
+ const [replacement, setReplacement] = useState6(null);
4960
+ const [urlValue, setUrlValue] = useState6("");
4961
+ const [loading2, setLoading] = useState6(false);
4962
+ const [description, setDescription] = useState6("");
4963
+ const [priority, setPriority] = useState6("nice");
4964
+ const [over, setOver] = useState6(false);
4703
4965
  const fileInput = useRef7(null);
4704
4966
  const abortRef = useRef7(null);
4705
4967
  const debounceRef = useRef7(null);
@@ -4766,7 +5028,7 @@ function ImageEditor({ editor }) {
4766
5028
  }
4767
5029
  }
4768
5030
  };
4769
- useEffect6(() => {
5031
+ useEffect7(() => {
4770
5032
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
4771
5033
  if (urlValue.trim() === "" || replacement !== null) return;
4772
5034
  debounceRef.current = window.setTimeout(() => {
@@ -4776,8 +5038,8 @@ function ImageEditor({ editor }) {
4776
5038
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
4777
5039
  };
4778
5040
  }, [urlValue, replacement]);
4779
- useEffect6(() => () => abortRef.current?.abort(), []);
4780
- useEffect6(() => {
5041
+ useEffect7(() => () => abortRef.current?.abort(), []);
5042
+ useEffect7(() => {
4781
5043
  const onPaste = (event) => {
4782
5044
  const data = event.clipboardData;
4783
5045
  if (!data) return;
@@ -4855,23 +5117,23 @@ function ImageEditor({ editor }) {
4855
5117
  toast(dispatch, "Image proposal saved", "success");
4856
5118
  };
4857
5119
  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 }),
5120
+ return /* @__PURE__ */ jsxs8("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
5121
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-head", children: [
5122
+ /* @__PURE__ */ jsx9(IconImage, { size: 14 }),
4861
5123
  "Replace image"
4862
5124
  ] }),
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" })
5125
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-body", children: [
5126
+ 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: [
5127
+ /* @__PURE__ */ jsxs8("figure", { className: "rk-thumb", children: [
5128
+ /* @__PURE__ */ jsx9("img", { src: beforeSrc, alt: "Current" }),
5129
+ /* @__PURE__ */ jsx9("figcaption", { children: "Before" })
4868
5130
  ] }),
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" })
5131
+ /* @__PURE__ */ jsxs8("figure", { className: "rk-thumb", children: [
5132
+ /* @__PURE__ */ jsx9("img", { src: previewSrc, alt: "Proposed replacement" }),
5133
+ /* @__PURE__ */ jsx9("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
4872
5134
  ] })
4873
- ] }) }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
4874
- /* @__PURE__ */ jsxs7(
5135
+ ] }) }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
5136
+ /* @__PURE__ */ jsxs8(
4875
5137
  "button",
4876
5138
  {
4877
5139
  type: "button",
@@ -4884,18 +5146,18 @@ function ImageEditor({ editor }) {
4884
5146
  onDragLeave: () => setOver(false),
4885
5147
  onDrop,
4886
5148
  children: [
4887
- /* @__PURE__ */ jsx8(IconUpload, { size: 18 }),
4888
- /* @__PURE__ */ jsxs7("span", { children: [
5149
+ /* @__PURE__ */ jsx9(IconUpload, { size: 18 }),
5150
+ /* @__PURE__ */ jsxs8("span", { children: [
4889
5151
  "Drop an image \u2014 from your disk or another tab \u2014",
4890
- /* @__PURE__ */ jsx8("br", {}),
5152
+ /* @__PURE__ */ jsx9("br", {}),
4891
5153
  "or click to browse (max 2 MB)"
4892
5154
  ] })
4893
5155
  ]
4894
5156
  }
4895
5157
  ),
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(
5158
+ /* @__PURE__ */ jsxs8("div", { className: "rk-url", children: [
5159
+ loading2 ? /* @__PURE__ */ jsx9("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ jsx9(IconLink, { size: 13 }),
5160
+ /* @__PURE__ */ jsx9(
4899
5161
  "input",
4900
5162
  {
4901
5163
  className: "rk-input",
@@ -4910,9 +5172,9 @@ function ImageEditor({ editor }) {
4910
5172
  )
4911
5173
  ] })
4912
5174
  ] }),
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(
5175
+ replacement && /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
5176
+ /* @__PURE__ */ jsx9("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
5177
+ /* @__PURE__ */ jsx9(
4916
5178
  "textarea",
4917
5179
  {
4918
5180
  className: "rk-textarea",
@@ -4922,43 +5184,43 @@ function ImageEditor({ editor }) {
4922
5184
  rows: 2
4923
5185
  }
4924
5186
  ),
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" })
5187
+ /* @__PURE__ */ jsxs8("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5188
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5189
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
4928
5190
  ] })
4929
5191
  ] }),
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" }),
5192
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-foot", children: [
5193
+ /* @__PURE__ */ jsxs8("span", { className: "rk-hint", children: [
5194
+ /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "esc" }),
4933
5195
  " cancel"
4934
5196
  ] }),
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" })
5197
+ /* @__PURE__ */ jsxs8("div", { className: "rk-actions", children: [
5198
+ /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
5199
+ /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
4938
5200
  ] })
4939
5201
  ] })
4940
5202
  ] });
4941
5203
  }
4942
5204
 
4943
5205
  // 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";
5206
+ import { useEffect as useEffect8, useRef as useRef8, useState as useState7 } from "react";
5207
+ import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
4946
5208
  function InlineEditor({ editor }) {
4947
5209
  const dispatch = useDispatch();
4948
5210
  const { route, session } = useStore();
4949
- const [after, setAfter] = useState6(() => editor.element.innerText);
4950
- const [priority, setPriority] = useState6("nice");
4951
- const [note, setNote] = useState6("");
5211
+ const [after, setAfter] = useState7(() => editor.element.innerText);
5212
+ const [priority, setPriority] = useState7("nice");
5213
+ const [note, setNote] = useState7("");
4952
5214
  const original = useRef8(null);
4953
5215
  const baseline = useRef8(null);
4954
- const [sizeDelta, setSizeDelta] = useState6(0);
4955
- const [widthDelta, setWidthDelta] = useState6(0);
4956
- const [clipped, setClipped] = useState6(false);
5216
+ const [sizeDelta, setSizeDelta] = useState7(0);
5217
+ const [widthDelta, setWidthDelta] = useState7(0);
5218
+ const [clipped, setClipped] = useState7(false);
4957
5219
  const ringRef = useRef8(null);
4958
5220
  const afterRef = useRef8(null);
4959
5221
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
4960
5222
  const element = editor.element;
4961
- useEffect7(() => {
5223
+ useEffect8(() => {
4962
5224
  const el = element;
4963
5225
  original.current = { html: el.innerHTML, text: el.innerText, styleAttr: el.getAttribute("style") };
4964
5226
  const rect = el.getBoundingClientRect();
@@ -4989,7 +5251,7 @@ function InlineEditor({ editor }) {
4989
5251
  };
4990
5252
  }, [element]);
4991
5253
  const focusedOnce = useRef8(false);
4992
- useEffect7(() => {
5254
+ useEffect8(() => {
4993
5255
  const field = afterRef.current;
4994
5256
  if (pos === null || focusedOnce.current || !field) return;
4995
5257
  focusedOnce.current = true;
@@ -4997,7 +5259,7 @@ function InlineEditor({ editor }) {
4997
5259
  const end = field.value.length;
4998
5260
  field.setSelectionRange(end, end);
4999
5261
  }, [pos]);
5000
- useEffect7(() => {
5262
+ useEffect8(() => {
5001
5263
  const field = afterRef.current;
5002
5264
  if (!field) return;
5003
5265
  field.style.height = "auto";
@@ -5007,7 +5269,7 @@ function InlineEditor({ editor }) {
5007
5269
  setAfter(value);
5008
5270
  if (element.innerText !== value) setElementText(element, value);
5009
5271
  };
5010
- useEffect7(() => {
5272
+ useEffect8(() => {
5011
5273
  let raf = 0;
5012
5274
  let lastDelta = 0;
5013
5275
  let lastWidth = 0;
@@ -5077,7 +5339,7 @@ function InlineEditor({ editor }) {
5077
5339
  cancel();
5078
5340
  }
5079
5341
  };
5080
- useEffect7(() => {
5342
+ useEffect8(() => {
5081
5343
  const el = element;
5082
5344
  const onKeyDown = (event) => {
5083
5345
  if (event.key === "Enter" && !event.shiftKey) {
@@ -5095,19 +5357,19 @@ function InlineEditor({ editor }) {
5095
5357
  });
5096
5358
  const before = original.current?.text ?? "";
5097
5359
  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 }),
5360
+ return /* @__PURE__ */ jsxs9(Fragment6, { children: [
5361
+ /* @__PURE__ */ jsx10("div", { ref: ringRef, className: "rk-edit-ring" }),
5362
+ /* @__PURE__ */ jsxs9("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
5363
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-head", children: [
5364
+ /* @__PURE__ */ jsx10(IconText, { size: 14 }),
5103
5365
  "Rewrite text",
5104
- /* @__PURE__ */ jsx9("span", { className: "rk-spacer" }),
5105
- /* @__PURE__ */ jsxs8("span", { className: "rk-delta", "aria-live": "polite", children: [
5366
+ /* @__PURE__ */ jsx10("span", { className: "rk-spacer" }),
5367
+ /* @__PURE__ */ jsxs9("span", { className: "rk-delta", "aria-live": "polite", children: [
5106
5368
  before.length,
5107
5369
  " \u2192 ",
5108
5370
  after.length,
5109
5371
  " ",
5110
- delta !== 0 && /* @__PURE__ */ jsxs8("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
5372
+ delta !== 0 && /* @__PURE__ */ jsxs9("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
5111
5373
  "(",
5112
5374
  delta > 0 ? "+" : "",
5113
5375
  delta,
@@ -5115,15 +5377,15 @@ function InlineEditor({ editor }) {
5115
5377
  ] })
5116
5378
  ] })
5117
5379
  ] }),
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 })
5380
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-body", children: [
5381
+ /* @__PURE__ */ jsxs9("div", { className: "rk-diff", "aria-label": "Before and after", children: [
5382
+ /* @__PURE__ */ jsxs9("div", { className: "rk-diff-row rk-diff-before", children: [
5383
+ /* @__PURE__ */ jsx10("span", { className: "rk-diff-tag", children: "Before" }),
5384
+ /* @__PURE__ */ jsx10("span", { className: "rk-diff-text", children: before })
5123
5385
  ] }),
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(
5386
+ /* @__PURE__ */ jsxs9("div", { className: "rk-diff-row rk-diff-after", children: [
5387
+ /* @__PURE__ */ jsx10("span", { className: "rk-diff-tag", children: "After" }),
5388
+ /* @__PURE__ */ jsx10(
5127
5389
  "textarea",
5128
5390
  {
5129
5391
  ref: afterRef,
@@ -5137,11 +5399,11 @@ function InlineEditor({ editor }) {
5137
5399
  )
5138
5400
  ] })
5139
5401
  ] }),
5140
- (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ jsxs8("div", { className: "rk-size-note", role: "status", children: [
5402
+ (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ jsxs9("div", { className: "rk-size-note", role: "status", children: [
5141
5403
  Math.abs(sizeDelta) > 2 && (() => {
5142
5404
  const lines = baseline.current ? Math.round(Math.abs(sizeDelta) / baseline.current.lineHeight) : 0;
5143
5405
  const lineNote = lines >= 1 ? ` (~${lines} line${lines === 1 ? "" : "s"})` : "";
5144
- return /* @__PURE__ */ jsxs8("span", { children: [
5406
+ return /* @__PURE__ */ jsxs9("span", { children: [
5145
5407
  "Block ",
5146
5408
  sizeDelta > 0 ? "grew" : "shrank",
5147
5409
  " by ",
@@ -5151,16 +5413,16 @@ function InlineEditor({ editor }) {
5151
5413
  " \u2014 as it would with this text live."
5152
5414
  ] });
5153
5415
  })(),
5154
- Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ jsxs8("span", { children: [
5416
+ Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ jsxs9("span", { children: [
5155
5417
  "Block got ",
5156
5418
  widthDelta > 0 ? "wider" : "narrower",
5157
5419
  " by ",
5158
5420
  Math.abs(widthDelta),
5159
5421
  " px \u2014 as it would with this text live."
5160
5422
  ] }),
5161
- clipped && /* @__PURE__ */ jsx9("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
5423
+ clipped && /* @__PURE__ */ jsx10("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
5162
5424
  ] }),
5163
- /* @__PURE__ */ jsx9(
5425
+ /* @__PURE__ */ jsx10(
5164
5426
  "input",
5165
5427
  {
5166
5428
  className: "rk-input",
@@ -5169,24 +5431,24 @@ function InlineEditor({ editor }) {
5169
5431
  onChange: (event) => setNote(event.target.value)
5170
5432
  }
5171
5433
  ),
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" })
5434
+ /* @__PURE__ */ jsxs9("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5435
+ /* @__PURE__ */ jsx10("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5436
+ /* @__PURE__ */ jsx10("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5175
5437
  ] })
5176
5438
  ] }),
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" }),
5439
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-foot", children: [
5440
+ /* @__PURE__ */ jsxs9("span", { className: "rk-hint", children: [
5441
+ /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "\u21B5" }),
5180
5442
  " save \xB7 ",
5181
- /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
5443
+ /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
5182
5444
  " newline \xB7",
5183
5445
  " ",
5184
- /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "esc" }),
5446
+ /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "esc" }),
5185
5447
  " cancel"
5186
5448
  ] }),
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" })
5449
+ /* @__PURE__ */ jsxs9("div", { className: "rk-actions", children: [
5450
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
5451
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
5190
5452
  ] })
5191
5453
  ] })
5192
5454
  ] })
@@ -5194,8 +5456,8 @@ function InlineEditor({ editor }) {
5194
5456
  }
5195
5457
 
5196
5458
  // 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";
5459
+ import { useEffect as useEffect9, useRef as useRef9, useState as useState8 } from "react";
5460
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
5199
5461
  var TITLES = {
5200
5462
  comment: "Comment",
5201
5463
  delete: "Mark for deletion",
@@ -5211,14 +5473,14 @@ var PLACEHOLDERS = {
5211
5473
  function NoteComposer({ editor }) {
5212
5474
  const dispatch = useDispatch();
5213
5475
  const { route, session } = useStore();
5214
- const [text, setText] = useState7("");
5215
- const [direction, setDirection] = useState7(void 0);
5216
- const [priority, setPriority] = useState7("nice");
5476
+ const [text, setText] = useState8("");
5477
+ const [direction, setDirection] = useState8(void 0);
5478
+ const [priority, setPriority] = useState8("nice");
5217
5479
  const textareaRef = useRef9(null);
5218
5480
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
5219
5481
  const { kind } = editor;
5220
5482
  const focusedOnce = useRef9(false);
5221
- useEffect8(() => {
5483
+ useEffect9(() => {
5222
5484
  const field = textareaRef.current;
5223
5485
  if (pos === null || focusedOnce.current || !field) return;
5224
5486
  focusedOnce.current = true;
@@ -5277,13 +5539,13 @@ function NoteComposer({ editor }) {
5277
5539
  save();
5278
5540
  }
5279
5541
  };
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: [
5542
+ return /* @__PURE__ */ jsxs10("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
5543
+ /* @__PURE__ */ jsxs10("div", { className: "rk-card-head", children: [
5282
5544
  iconForType(kind),
5283
5545
  TITLES[kind]
5284
5546
  ] }),
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(
5547
+ /* @__PURE__ */ jsxs10("div", { className: "rk-card-body", children: [
5548
+ kind === "move" && /* @__PURE__ */ jsx11("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ jsx11(
5287
5549
  "button",
5288
5550
  {
5289
5551
  type: "button",
@@ -5293,7 +5555,7 @@ function NoteComposer({ editor }) {
5293
5555
  },
5294
5556
  option
5295
5557
  )) }),
5296
- /* @__PURE__ */ jsx10(
5558
+ /* @__PURE__ */ jsx11(
5297
5559
  "textarea",
5298
5560
  {
5299
5561
  ref: textareaRef,
@@ -5305,29 +5567,29 @@ function NoteComposer({ editor }) {
5305
5567
  rows: 3
5306
5568
  }
5307
5569
  ),
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" })
5570
+ /* @__PURE__ */ jsxs10("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5571
+ /* @__PURE__ */ jsx11("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5572
+ /* @__PURE__ */ jsx11("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5311
5573
  ] })
5312
5574
  ] }),
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" }),
5575
+ /* @__PURE__ */ jsxs10("div", { className: "rk-card-foot", children: [
5576
+ /* @__PURE__ */ jsxs10("span", { className: "rk-hint", children: [
5577
+ /* @__PURE__ */ jsx11("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
5316
5578
  " save \xB7 ",
5317
- /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "esc" }),
5579
+ /* @__PURE__ */ jsx11("span", { className: "rk-kbd", children: "esc" }),
5318
5580
  " cancel"
5319
5581
  ] }),
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" })
5582
+ /* @__PURE__ */ jsxs10("div", { className: "rk-actions", children: [
5583
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
5584
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
5323
5585
  ] })
5324
5586
  ] })
5325
5587
  ] });
5326
5588
  }
5327
5589
 
5328
5590
  // 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";
5591
+ import { useEffect as useEffect10, useMemo as useMemo3, useRef as useRef10, useState as useState9 } from "react";
5592
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
5331
5593
  function changeSummary(item) {
5332
5594
  if (isCopyItem(item)) return `\u201C${item.payload.after}\u201D`;
5333
5595
  if (isImageItem(item)) {
@@ -5409,24 +5671,24 @@ function formatBytes(bytes) {
5409
5671
  function CopyDiff({ before, after }) {
5410
5672
  const parts = diffParts(before, after);
5411
5673
  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: [
5674
+ return /* @__PURE__ */ jsxs11("div", { className: "rk-diffview", children: [
5675
+ /* @__PURE__ */ jsxs11("div", { className: "rk-diff-row rk-diff-before", children: [
5676
+ /* @__PURE__ */ jsx12("span", { className: "rk-diff-tag", children: "Before" }),
5677
+ /* @__PURE__ */ jsxs11("span", { className: "rk-diff-text", children: [
5416
5678
  parts.prefix,
5417
- parts.delMid && /* @__PURE__ */ jsx11("mark", { className: "rk-del", children: parts.delMid }),
5679
+ parts.delMid && /* @__PURE__ */ jsx12("mark", { className: "rk-del", children: parts.delMid }),
5418
5680
  parts.suffix
5419
5681
  ] })
5420
5682
  ] }),
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: [
5683
+ /* @__PURE__ */ jsxs11("div", { className: "rk-diff-row rk-diff-after", children: [
5684
+ /* @__PURE__ */ jsx12("span", { className: "rk-diff-tag", children: "After" }),
5685
+ /* @__PURE__ */ jsxs11("span", { className: "rk-diff-text", children: [
5424
5686
  parts.prefix,
5425
- parts.insMid && /* @__PURE__ */ jsx11("mark", { className: "rk-ins", children: parts.insMid }),
5687
+ parts.insMid && /* @__PURE__ */ jsx12("mark", { className: "rk-ins", children: parts.insMid }),
5426
5688
  parts.suffix
5427
5689
  ] })
5428
5690
  ] }),
5429
- /* @__PURE__ */ jsxs10("div", { className: "rk-diff-len", children: [
5691
+ /* @__PURE__ */ jsxs11("div", { className: "rk-diff-len", children: [
5430
5692
  before.length,
5431
5693
  " \u2192 ",
5432
5694
  after.length,
@@ -5437,8 +5699,8 @@ function CopyDiff({ before, after }) {
5437
5699
  }
5438
5700
  function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
5439
5701
  const replacement = afterDataUrl ?? afterSrc;
5440
- const [dims, setDims] = useState8("");
5441
- useEffect9(() => {
5702
+ const [dims, setDims] = useState9("");
5703
+ useEffect10(() => {
5442
5704
  if (!replacement) return;
5443
5705
  const img = new Image();
5444
5706
  img.onload = () => setDims(`${img.naturalWidth}\xD7${img.naturalHeight}px`);
@@ -5463,49 +5725,61 @@ function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
5463
5725
  window.open(replacement, "_blank", "noopener");
5464
5726
  }
5465
5727
  };
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" })
5728
+ return /* @__PURE__ */ jsxs11("div", { className: "rk-imgdetail", children: [
5729
+ /* @__PURE__ */ jsxs11("div", { className: "rk-thumbs", children: [
5730
+ /* @__PURE__ */ jsxs11("figure", { className: "rk-thumb", children: [
5731
+ beforeSrc ? /* @__PURE__ */ jsx12("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ jsx12("div", { className: "rk-thumb-missing", children: "no image" }),
5732
+ /* @__PURE__ */ jsx12("figcaption", { children: "Before" })
5471
5733
  ] }),
5472
- /* @__PURE__ */ jsxs10("figure", { className: "rk-thumb", children: [
5473
- /* @__PURE__ */ jsx11("img", { src: replacement, alt: "Proposed replacement" }),
5474
- /* @__PURE__ */ jsxs10("figcaption", { children: [
5734
+ /* @__PURE__ */ jsxs11("figure", { className: "rk-thumb", children: [
5735
+ /* @__PURE__ */ jsx12("img", { src: replacement, alt: "Proposed replacement" }),
5736
+ /* @__PURE__ */ jsxs11("figcaption", { children: [
5475
5737
  "After",
5476
5738
  embedded ? "" : " \xB7 linked"
5477
5739
  ] })
5478
5740
  ] })
5479
5741
  ] }),
5480
- /* @__PURE__ */ jsxs10("div", { className: "rk-imgdetail-meta", children: [
5742
+ /* @__PURE__ */ jsxs11("div", { className: "rk-imgdetail-meta", children: [
5481
5743
  embedded ? "Embedded image" : "Linked image",
5482
5744
  dims ? ` \xB7 ${dims}` : "",
5483
5745
  bytes !== null ? ` \xB7 ${formatBytes(bytes)}` : ""
5484
5746
  ] }),
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 }),
5747
+ !embedded && afterSrc && /* @__PURE__ */ jsx12("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
5748
+ /* @__PURE__ */ jsxs11("div", { className: "rk-imgdetail-actions", children: [
5749
+ /* @__PURE__ */ jsxs11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
5750
+ /* @__PURE__ */ jsx12(IconDownload, { size: 12 }),
5489
5751
  " Download"
5490
5752
  ] }),
5491
- /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
5753
+ /* @__PURE__ */ jsx12("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
5492
5754
  ] })
5493
5755
  ] });
5494
5756
  }
5757
+ var SYNC_REASON_LABEL = {
5758
+ "token-short": "token too short",
5759
+ "bad-token": "token rejected",
5760
+ "room-full": "room full",
5761
+ unreachable: "server unreachable"
5762
+ };
5763
+ var SYNC_REASON_HELP = {
5764
+ "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.",
5765
+ "bad-token": "The sync server rejected the token. Check `syncToken` / `token` in <ReviewKit />.",
5766
+ "room-full": "This page already has the maximum number of reviewers online (16). Try again later.",
5767
+ 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."
5768
+ };
5495
5769
  function Panel() {
5496
- const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, emblemaPush, peers } = useStore();
5770
+ const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, syncReason, emblemaPush, peers } = useStore();
5497
5771
  const dispatch = useDispatch();
5498
5772
  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");
5773
+ const [sticky, setStickyState] = useState9(() => isStickyEnabled(config.token));
5774
+ const [query, setQuery] = useState9("");
5775
+ const [typeFilter, setTypeFilter] = useState9(/* @__PURE__ */ new Set());
5776
+ const [prioFilter, setPrioFilter] = useState9(/* @__PURE__ */ new Set());
5777
+ const [statusFilter, setStatusFilter] = useState9(/* @__PURE__ */ new Set());
5778
+ const [filterOpen, setFilterOpen] = useState9(false);
5779
+ const [settingsOpen, setSettingsOpen] = useState9(false);
5780
+ const [emblemaAlive, setEmblemaAlive] = useState9(null);
5781
+ const [emblemaSending, setEmblemaSending] = useState9(false);
5782
+ const [emblemaSync, setEmblemaSync] = useState9("unknown");
5509
5783
  const panelRef = useRef10(null);
5510
5784
  const searchRef = useRef10(null);
5511
5785
  const importRef = useRef10(null);
@@ -5526,7 +5800,7 @@ function Panel() {
5526
5800
  () => typeFilter.size + prioFilter.size + statusFilter.size > 0 ? { types: [...typeFilter], priorities: [...prioFilter], statuses: [...statusFilter] } : null,
5527
5801
  [typeFilter, prioFilter, statusFilter]
5528
5802
  );
5529
- useEffect9(() => {
5803
+ useEffect10(() => {
5530
5804
  dispatch({ type: "SET_FILTER", filter: activeFilter });
5531
5805
  return () => dispatch({ type: "SET_FILTER", filter: null });
5532
5806
  }, [activeFilter, dispatch]);
@@ -5563,7 +5837,7 @@ function Panel() {
5563
5837
  }
5564
5838
  return Array.from(map.entries());
5565
5839
  }, [filtered]);
5566
- useEffect9(() => {
5840
+ useEffect10(() => {
5567
5841
  if (!filterOpen && !settingsOpen) return;
5568
5842
  const onDown = (event) => {
5569
5843
  const path = event.composedPath();
@@ -5574,7 +5848,7 @@ function Panel() {
5574
5848
  document.addEventListener("pointerdown", onDown, true);
5575
5849
  return () => document.removeEventListener("pointerdown", onDown, true);
5576
5850
  }, [filterOpen, settingsOpen]);
5577
- useEffect9(() => {
5851
+ useEffect10(() => {
5578
5852
  if (!settingsOpen) return;
5579
5853
  let alive = true;
5580
5854
  const probe = () => {
@@ -5598,7 +5872,7 @@ function Panel() {
5598
5872
  live: "paired",
5599
5873
  off: "not paired"
5600
5874
  }[emblemaPairState];
5601
- useEffect9(() => {
5875
+ useEffect10(() => {
5602
5876
  if (!panelOpen || activeItemId === null) return;
5603
5877
  const row = panelRef.current?.querySelector(`[data-item-id="${activeItemId}"]`);
5604
5878
  row?.scrollIntoView({ block: "nearest" });
@@ -5714,7 +5988,7 @@ function Panel() {
5714
5988
  panel.style.transform = "";
5715
5989
  if (dy > 110) dispatch({ type: "SET_PANEL", open: false });
5716
5990
  };
5717
- return /* @__PURE__ */ jsxs10(
5991
+ return /* @__PURE__ */ jsxs11(
5718
5992
  "div",
5719
5993
  {
5720
5994
  ref: panelRef,
@@ -5724,7 +5998,7 @@ function Panel() {
5724
5998
  "aria-label": "Review feedback panel",
5725
5999
  onKeyDown,
5726
6000
  children: [
5727
- /* @__PURE__ */ jsx11(
6001
+ /* @__PURE__ */ jsx12(
5728
6002
  "div",
5729
6003
  {
5730
6004
  className: "rk-sheet-handle",
@@ -5735,34 +6009,41 @@ function Panel() {
5735
6009
  onPointerCancel: onSheetHandleUp
5736
6010
  }
5737
6011
  ),
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: [
6012
+ /* @__PURE__ */ jsxs11("div", { className: "rk-panel-head", children: [
6013
+ /* @__PURE__ */ jsxs11("div", { children: [
6014
+ /* @__PURE__ */ jsx12("div", { className: "rk-panel-title", children: "Feedback" }),
6015
+ /* @__PURE__ */ jsxs11("div", { className: "rk-panel-sub", children: [
5742
6016
  items.length,
5743
6017
  " item",
5744
6018
  items.length === 1 ? "" : "s",
5745
6019
  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(
6020
+ syncStatus !== "disabled" && /* @__PURE__ */ jsxs11(
6021
+ "span",
6022
+ {
6023
+ className: `rk-sync rk-sync-${syncStatus}${syncReason ? ` rk-sync-reason-${syncReason}` : ""}`,
6024
+ 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"],
6025
+ children: [
6026
+ /* @__PURE__ */ jsx12("span", { className: "rk-sync-dot" }),
6027
+ 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"]}`
6028
+ ]
6029
+ }
6030
+ ),
6031
+ config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ jsxs11(
5751
6032
  "span",
5752
6033
  {
5753
6034
  className: `rk-sync rk-epush-${emblemaPush}`,
5754
6035
  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
6036
  children: [
5756
- /* @__PURE__ */ jsx11("span", { className: "rk-sync-dot" }),
6037
+ /* @__PURE__ */ jsx12("span", { className: "rk-sync-dot" }),
5757
6038
  emblemaPush === "ok" ? "emblema" : emblemaPush === "sending" ? "emblema\u2026" : emblemaPush === "rejected" ? "emblema rejected" : "emblema offline"
5758
6039
  ]
5759
6040
  }
5760
6041
  )
5761
6042
  ] })
5762
6043
  ] }),
5763
- /* @__PURE__ */ jsx11("span", { className: "rk-spacer" }),
5764
- /* @__PURE__ */ jsxs10("div", { className: "rk-menu-scope rk-menu-anchor", children: [
5765
- /* @__PURE__ */ jsx11(
6044
+ /* @__PURE__ */ jsx12("span", { className: "rk-spacer" }),
6045
+ /* @__PURE__ */ jsxs11("div", { className: "rk-menu-scope rk-menu-anchor", children: [
6046
+ /* @__PURE__ */ jsx12(
5766
6047
  "button",
5767
6048
  {
5768
6049
  type: "button",
@@ -5778,11 +6059,11 @@ function Panel() {
5778
6059
  void pingEmblema(config.emblema).then((port) => setEmblemaAlive(port !== null));
5779
6060
  }
5780
6061
  },
5781
- children: /* @__PURE__ */ jsx11(IconGear, { size: 15 })
6062
+ children: /* @__PURE__ */ jsx12(IconGear, { size: 15 })
5782
6063
  }
5783
6064
  ),
5784
- settingsOpen && /* @__PURE__ */ jsxs10("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
5785
- config.token && /* @__PURE__ */ jsxs10(
6065
+ settingsOpen && /* @__PURE__ */ jsxs11("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
6066
+ config.token && /* @__PURE__ */ jsxs11(
5786
6067
  "button",
5787
6068
  {
5788
6069
  type: "button",
@@ -5797,12 +6078,12 @@ function Panel() {
5797
6078
  toast(dispatch, next ? "Review stays on in this tab" : "Review active only via the link", "info");
5798
6079
  },
5799
6080
  children: [
5800
- /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
6081
+ /* @__PURE__ */ jsx12("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
5801
6082
  "Keep review on in this tab"
5802
6083
  ]
5803
6084
  }
5804
6085
  ),
5805
- config.emblema && /* @__PURE__ */ jsxs10(
6086
+ config.emblema && /* @__PURE__ */ jsxs11(
5806
6087
  "button",
5807
6088
  {
5808
6089
  type: "button",
@@ -5832,7 +6113,7 @@ function Panel() {
5832
6113
  }).finally(() => setEmblemaSending(false));
5833
6114
  },
5834
6115
  children: [
5835
- /* @__PURE__ */ jsx11(
6116
+ /* @__PURE__ */ jsx12(
5836
6117
  "span",
5837
6118
  {
5838
6119
  className: "rk-emblema-dot",
@@ -5843,25 +6124,40 @@ function Panel() {
5843
6124
  ]
5844
6125
  }
5845
6126
  ),
5846
- /* @__PURE__ */ jsxs10(
6127
+ /* @__PURE__ */ jsxs11(
5847
6128
  "button",
5848
6129
  {
5849
6130
  type: "button",
5850
6131
  role: "menuitem",
5851
6132
  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",
6133
+ 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
6134
  onClick: () => {
5854
6135
  setSettingsOpen(false);
5855
6136
  dispatch({ type: "SET_EMBLEMA_CONNECT", open: true });
5856
6137
  },
5857
6138
  children: [
5858
- /* @__PURE__ */ jsx11("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
6139
+ /* @__PURE__ */ jsx12("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
5859
6140
  "Connect to Emblema\u2026",
5860
- /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: emblemaPairLabel })
6141
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-count", children: emblemaPairLabel })
5861
6142
  ]
5862
6143
  }
5863
6144
  ),
5864
- /* @__PURE__ */ jsx11(
6145
+ /* @__PURE__ */ jsx12(
6146
+ "button",
6147
+ {
6148
+ type: "button",
6149
+ role: "menuitem",
6150
+ className: "rk-menu-item",
6151
+ 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",
6152
+ disabled: !resolveSyncToken(config),
6153
+ onClick: () => {
6154
+ setSettingsOpen(false);
6155
+ dispatch({ type: "SET_TERMINAL_CONNECT", open: true });
6156
+ },
6157
+ children: "Connect terminal / agent\u2026"
6158
+ }
6159
+ ),
6160
+ /* @__PURE__ */ jsx12(
5865
6161
  "button",
5866
6162
  {
5867
6163
  type: "button",
@@ -5874,31 +6170,31 @@ function Panel() {
5874
6170
  )
5875
6171
  ] })
5876
6172
  ] }),
5877
- /* @__PURE__ */ jsx11(
6173
+ /* @__PURE__ */ jsx12(
5878
6174
  "button",
5879
6175
  {
5880
6176
  type: "button",
5881
6177
  className: "rk-icon-btn",
5882
6178
  "aria-label": "Keyboard shortcuts",
5883
6179
  onClick: () => dispatch({ type: "SET_CHEATSHEET", open: true }),
5884
- children: /* @__PURE__ */ jsx11(IconKeyboard, { size: 15 })
6180
+ children: /* @__PURE__ */ jsx12(IconKeyboard, { size: 15 })
5885
6181
  }
5886
6182
  ),
5887
- /* @__PURE__ */ jsx11(
6183
+ /* @__PURE__ */ jsx12(
5888
6184
  "button",
5889
6185
  {
5890
6186
  type: "button",
5891
6187
  className: "rk-icon-btn",
5892
6188
  "aria-label": "Close panel",
5893
6189
  onClick: () => dispatch({ type: "SET_PANEL", open: false }),
5894
- children: /* @__PURE__ */ jsx11(IconClose, { size: 14 })
6190
+ children: /* @__PURE__ */ jsx12(IconClose, { size: 14 })
5895
6191
  }
5896
6192
  )
5897
6193
  ] }),
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(
6194
+ /* @__PURE__ */ jsxs11("div", { className: "rk-view-row", children: [
6195
+ /* @__PURE__ */ jsx12("div", { className: "rk-view-info", children: /* @__PURE__ */ jsx12("div", { className: "rk-view-title", children: "Page view" }) }),
6196
+ /* @__PURE__ */ jsxs11("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
6197
+ /* @__PURE__ */ jsx12(
5902
6198
  "button",
5903
6199
  {
5904
6200
  type: "button",
@@ -5908,7 +6204,7 @@ function Panel() {
5908
6204
  children: "Original"
5909
6205
  }
5910
6206
  ),
5911
- /* @__PURE__ */ jsx11(
6207
+ /* @__PURE__ */ jsx12(
5912
6208
  "button",
5913
6209
  {
5914
6210
  type: "button",
@@ -5920,8 +6216,8 @@ function Panel() {
5920
6216
  )
5921
6217
  ] })
5922
6218
  ] }),
5923
- /* @__PURE__ */ jsxs10("div", { className: "rk-panel-tools", children: [
5924
- /* @__PURE__ */ jsxs10(
6219
+ /* @__PURE__ */ jsxs11("div", { className: "rk-panel-tools", children: [
6220
+ /* @__PURE__ */ jsxs11(
5925
6221
  "button",
5926
6222
  {
5927
6223
  type: "button",
@@ -5932,15 +6228,15 @@ function Panel() {
5932
6228
  dispatch({ type: "SET_MODE", mode: "pick" });
5933
6229
  },
5934
6230
  children: [
5935
- /* @__PURE__ */ jsx11(IconTarget, { size: 14 }),
6231
+ /* @__PURE__ */ jsx12(IconTarget, { size: 14 }),
5936
6232
  " Pick an element"
5937
6233
  ]
5938
6234
  }
5939
6235
  ),
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(
6236
+ /* @__PURE__ */ jsxs11("div", { className: "rk-search-row", children: [
6237
+ /* @__PURE__ */ jsxs11("div", { className: "rk-search", children: [
6238
+ /* @__PURE__ */ jsx12(IconSearch, { size: 13 }),
6239
+ /* @__PURE__ */ jsx12(
5944
6240
  "input",
5945
6241
  {
5946
6242
  ref: searchRef,
@@ -5952,8 +6248,8 @@ function Panel() {
5952
6248
  }
5953
6249
  )
5954
6250
  ] }),
5955
- /* @__PURE__ */ jsxs10("div", { className: "rk-menu-scope rk-menu-anchor", children: [
5956
- /* @__PURE__ */ jsxs10(
6251
+ /* @__PURE__ */ jsxs11("div", { className: "rk-menu-scope rk-menu-anchor", children: [
6252
+ /* @__PURE__ */ jsxs11(
5957
6253
  "button",
5958
6254
  {
5959
6255
  type: "button",
@@ -5965,15 +6261,15 @@ function Panel() {
5965
6261
  setSettingsOpen(false);
5966
6262
  },
5967
6263
  children: [
5968
- /* @__PURE__ */ jsx11(IconFilter, { size: 13 }),
6264
+ /* @__PURE__ */ jsx12(IconFilter, { size: 13 }),
5969
6265
  "Filter",
5970
- typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ jsx11("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
6266
+ typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ jsx12("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
5971
6267
  ]
5972
6268
  }
5973
6269
  ),
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(
6270
+ filterOpen && /* @__PURE__ */ jsxs11("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
6271
+ /* @__PURE__ */ jsx12("div", { className: "rk-menu-title", children: "Type" }),
6272
+ TYPE_FILTERS.map((type) => /* @__PURE__ */ jsxs11(
5977
6273
  "button",
5978
6274
  {
5979
6275
  type: "button",
@@ -5987,16 +6283,16 @@ function Panel() {
5987
6283
  setTypeFilter(next);
5988
6284
  },
5989
6285
  children: [
5990
- /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
6286
+ /* @__PURE__ */ jsx12("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
5991
6287
  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 })
6288
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-label", children: type }),
6289
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
5994
6290
  ]
5995
6291
  },
5996
6292
  type
5997
6293
  )),
5998
- /* @__PURE__ */ jsx11("div", { className: "rk-menu-title", children: "Priority" }),
5999
- PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ jsxs10(
6294
+ /* @__PURE__ */ jsx12("div", { className: "rk-menu-title", children: "Priority" }),
6295
+ PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ jsxs11(
6000
6296
  "button",
6001
6297
  {
6002
6298
  type: "button",
@@ -6010,15 +6306,15 @@ function Panel() {
6010
6306
  setPrioFilter(next);
6011
6307
  },
6012
6308
  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 })
6309
+ /* @__PURE__ */ jsx12("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
6310
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
6311
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
6016
6312
  ]
6017
6313
  },
6018
6314
  priority
6019
6315
  )),
6020
- /* @__PURE__ */ jsx11("div", { className: "rk-menu-title", children: "Status" }),
6021
- STATUSES.map((status) => /* @__PURE__ */ jsxs10(
6316
+ /* @__PURE__ */ jsx12("div", { className: "rk-menu-title", children: "Status" }),
6317
+ STATUSES.map((status) => /* @__PURE__ */ jsxs11(
6022
6318
  "button",
6023
6319
  {
6024
6320
  type: "button",
@@ -6032,15 +6328,15 @@ function Panel() {
6032
6328
  setStatusFilter(next);
6033
6329
  },
6034
6330
  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 })
6331
+ /* @__PURE__ */ jsx12("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
6332
+ /* @__PURE__ */ jsx12("span", { className: `rk-status-dot rk-st-${status}` }),
6333
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-label", children: status }),
6334
+ /* @__PURE__ */ jsx12("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
6039
6335
  ]
6040
6336
  },
6041
6337
  status
6042
6338
  )),
6043
- /* @__PURE__ */ jsx11(
6339
+ /* @__PURE__ */ jsx12(
6044
6340
  "button",
6045
6341
  {
6046
6342
  type: "button",
@@ -6060,24 +6356,24 @@ function Panel() {
6060
6356
  ] })
6061
6357
  ] })
6062
6358
  ] }),
6063
- preview && unapplied > 0 && /* @__PURE__ */ jsxs10("div", { className: "rk-unplaced", role: "status", children: [
6359
+ preview && unapplied > 0 && /* @__PURE__ */ jsxs11("div", { className: "rk-unplaced", role: "status", children: [
6064
6360
  unapplied,
6065
6361
  " saved change",
6066
6362
  unapplied === 1 ? "" : "s",
6067
6363
  " couldn\u2019t be placed on the current page \u2014 the page has changed since they were captured."
6068
6364
  ] }),
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 }),
6365
+ /* @__PURE__ */ jsx12("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ jsxs11("div", { className: "rk-empty", children: [
6366
+ /* @__PURE__ */ jsx12("div", { className: "rk-empty-art", children: /* @__PURE__ */ jsx12(IconTarget, { size: 22 }) }),
6367
+ /* @__PURE__ */ jsx12("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
6368
+ /* @__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." })
6369
+ ] }) : groups.map(([route, entries]) => /* @__PURE__ */ jsxs11("div", { children: [
6370
+ /* @__PURE__ */ jsx12("div", { className: "rk-group-title", children: route }),
6075
6371
  entries.map(({ item, number }) => {
6076
6372
  const active = item.id === activeItemId;
6077
6373
  const orphan = orphanIds.has(item.id);
6078
6374
  const hiddenEl = hiddenIds.has(item.id);
6079
- return /* @__PURE__ */ jsxs10("div", { children: [
6080
- /* @__PURE__ */ jsxs10(
6375
+ return /* @__PURE__ */ jsxs11("div", { children: [
6376
+ /* @__PURE__ */ jsxs11(
6081
6377
  "div",
6082
6378
  {
6083
6379
  "data-item-id": item.id,
@@ -6092,18 +6388,18 @@ function Panel() {
6092
6388
  }
6093
6389
  },
6094
6390
  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: [
6391
+ /* @__PURE__ */ jsx12("span", { className: "rk-item-num", children: number }),
6392
+ /* @__PURE__ */ jsx12("span", { className: "rk-item-icon", children: iconForType(item.type) }),
6393
+ /* @__PURE__ */ jsxs11("span", { className: "rk-item-body", children: [
6394
+ /* @__PURE__ */ jsxs11("span", { className: "rk-item-snippet", children: [
6099
6395
  item.target.tagName,
6100
6396
  " \xB7 ",
6101
6397
  item.target.textSnippet || item.target.selector
6102
6398
  ] }),
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(
6399
+ /* @__PURE__ */ jsx12("span", { className: "rk-item-change", children: changeSummary(item) }),
6400
+ /* @__PURE__ */ jsxs11("span", { className: "rk-item-meta", children: [
6401
+ /* @__PURE__ */ jsxs11("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
6402
+ /* @__PURE__ */ jsx12(
6107
6403
  "span",
6108
6404
  {
6109
6405
  className: "rk-author-dot",
@@ -6111,15 +6407,15 @@ function Panel() {
6111
6407
  }
6112
6408
  ),
6113
6409
  item.author?.name ?? "Unknown",
6114
- /* @__PURE__ */ jsxs10("span", { className: "rk-time", children: [
6410
+ /* @__PURE__ */ jsxs11("span", { className: "rk-time", children: [
6115
6411
  " \xB7 ",
6116
6412
  timeAgo(item.createdAt)
6117
6413
  ] })
6118
6414
  ] }),
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(
6415
+ item.priority === "must" && /* @__PURE__ */ jsx12("span", { className: "rk-badge-mini rk-must", children: "must" }),
6416
+ item.status !== "open" && /* @__PURE__ */ jsx12("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
6417
+ orphan && /* @__PURE__ */ jsx12("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
6418
+ hiddenEl && /* @__PURE__ */ jsx12(
6123
6419
  "span",
6124
6420
  {
6125
6421
  className: "rk-badge-mini rk-hidden-el",
@@ -6127,14 +6423,14 @@ function Panel() {
6127
6423
  children: "hidden"
6128
6424
  }
6129
6425
  ),
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: [
6426
+ item.note && /* @__PURE__ */ jsx12("span", { className: "rk-badge-mini", children: "note" }),
6427
+ peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ jsxs11("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
6132
6428
  peer.name || "peer",
6133
6429
  " is writing\u2026"
6134
6430
  ] }, peer.id))
6135
6431
  ] })
6136
6432
  ] }),
6137
- /* @__PURE__ */ jsx11("span", { className: "rk-item-actions", children: /* @__PURE__ */ jsx11(
6433
+ /* @__PURE__ */ jsx12("span", { className: "rk-item-actions", children: /* @__PURE__ */ jsx12(
6138
6434
  "button",
6139
6435
  {
6140
6436
  type: "button",
@@ -6149,15 +6445,15 @@ function Panel() {
6149
6445
  "info"
6150
6446
  );
6151
6447
  },
6152
- children: /* @__PURE__ */ jsx11(IconTrash, { size: 13 })
6448
+ children: /* @__PURE__ */ jsx12(IconTrash, { size: 13 })
6153
6449
  }
6154
6450
  ) })
6155
6451
  ]
6156
6452
  }
6157
6453
  ),
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(
6454
+ active && /* @__PURE__ */ jsxs11("div", { className: "rk-item-edit", children: [
6455
+ /* @__PURE__ */ jsxs11("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
6456
+ /* @__PURE__ */ jsx12(
6161
6457
  "span",
6162
6458
  {
6163
6459
  className: "rk-author-dot",
@@ -6168,8 +6464,8 @@ function Panel() {
6168
6464
  " \xB7 ",
6169
6465
  timeAgo(item.createdAt)
6170
6466
  ] }),
6171
- isCopyItem(item) && /* @__PURE__ */ jsx11(CopyDiff, { before: item.payload.before, after: item.payload.after }),
6172
- isImageItem(item) && /* @__PURE__ */ jsx11(
6467
+ isCopyItem(item) && /* @__PURE__ */ jsx12(CopyDiff, { before: item.payload.before, after: item.payload.after }),
6468
+ isImageItem(item) && /* @__PURE__ */ jsx12(
6173
6469
  ImageDetail,
6174
6470
  {
6175
6471
  beforeSrc: item.payload.beforeSrc,
@@ -6177,7 +6473,7 @@ function Panel() {
6177
6473
  afterDataUrl: item.payload.afterDataUrl
6178
6474
  }
6179
6475
  ),
6180
- isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ jsx11(
6476
+ isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ jsx12(
6181
6477
  "textarea",
6182
6478
  {
6183
6479
  className: "rk-textarea",
@@ -6187,7 +6483,7 @@ function Panel() {
6187
6483
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: withPayloadText(item, event.target.value) })
6188
6484
  }
6189
6485
  ),
6190
- /* @__PURE__ */ jsx11(
6486
+ /* @__PURE__ */ jsx12(
6191
6487
  "input",
6192
6488
  {
6193
6489
  className: "rk-input",
@@ -6197,9 +6493,9 @@ function Panel() {
6197
6493
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: { note: event.target.value } })
6198
6494
  }
6199
6495
  ),
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(
6496
+ /* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
6497
+ /* @__PURE__ */ jsxs11("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6498
+ /* @__PURE__ */ jsx12(
6203
6499
  "button",
6204
6500
  {
6205
6501
  type: "button",
@@ -6208,7 +6504,7 @@ function Panel() {
6208
6504
  children: "Must"
6209
6505
  }
6210
6506
  ),
6211
- /* @__PURE__ */ jsx11(
6507
+ /* @__PURE__ */ jsx12(
6212
6508
  "button",
6213
6509
  {
6214
6510
  type: "button",
@@ -6218,7 +6514,7 @@ function Panel() {
6218
6514
  }
6219
6515
  )
6220
6516
  ] }),
6221
- /* @__PURE__ */ jsx11("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ jsx11(
6517
+ /* @__PURE__ */ jsx12("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ jsx12(
6222
6518
  "button",
6223
6519
  {
6224
6520
  type: "button",
@@ -6233,22 +6529,22 @@ function Panel() {
6233
6529
  ] }, item.id);
6234
6530
  })
6235
6531
  ] }, 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 }),
6532
+ /* @__PURE__ */ jsxs11("div", { className: "rk-panel-foot", children: [
6533
+ /* @__PURE__ */ jsxs11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
6534
+ /* @__PURE__ */ jsx12(IconDownload, { size: 13 }),
6239
6535
  " Export"
6240
6536
  ] }),
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 }),
6537
+ /* @__PURE__ */ jsxs11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
6538
+ /* @__PURE__ */ jsx12(IconCopyClipboard, { size: 13 }),
6243
6539
  " Copy"
6244
6540
  ] }),
6245
- /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
6246
- /* @__PURE__ */ jsx11(IconUpload, { size: 13 }),
6541
+ /* @__PURE__ */ jsxs11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
6542
+ /* @__PURE__ */ jsx12(IconUpload, { size: 13 }),
6247
6543
  " Import"
6248
6544
  ] }),
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(
6545
+ /* @__PURE__ */ jsx12("span", { className: "rk-spacer" }),
6546
+ 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 }) }),
6547
+ /* @__PURE__ */ jsx12(
6252
6548
  "button",
6253
6549
  {
6254
6550
  type: "button",
@@ -6259,7 +6555,7 @@ function Panel() {
6259
6555
  children: "Clear"
6260
6556
  }
6261
6557
  ),
6262
- /* @__PURE__ */ jsx11(
6558
+ /* @__PURE__ */ jsx12(
6263
6559
  "button",
6264
6560
  {
6265
6561
  type: "button",
@@ -6269,7 +6565,7 @@ function Panel() {
6269
6565
  children: "Exit review"
6270
6566
  }
6271
6567
  ),
6272
- /* @__PURE__ */ jsx11(
6568
+ /* @__PURE__ */ jsx12(
6273
6569
  "input",
6274
6570
  {
6275
6571
  ref: importRef,
@@ -6290,8 +6586,8 @@ function Panel() {
6290
6586
  }
6291
6587
 
6292
6588
  // 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";
6589
+ import { useEffect as useEffect11, useRef as useRef11 } from "react";
6590
+ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
6295
6591
  function initials(name) {
6296
6592
  const parts = name.trim().split(/\s+/).filter(Boolean);
6297
6593
  if (parts.length === 0) return "\xB7";
@@ -6301,8 +6597,8 @@ function initials(name) {
6301
6597
  function PresenceStrip() {
6302
6598
  const { peers, syncStatus } = useStore();
6303
6599
  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(
6600
+ return /* @__PURE__ */ jsxs12("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
6601
+ peers.slice(0, 5).map((peer) => /* @__PURE__ */ jsx13(
6306
6602
  "span",
6307
6603
  {
6308
6604
  className: "rk-avatar",
@@ -6312,7 +6608,7 @@ function PresenceStrip() {
6312
6608
  },
6313
6609
  peer.id
6314
6610
  )),
6315
- peers.length > 5 && /* @__PURE__ */ jsxs11("span", { className: "rk-avatar rk-avatar-more", children: [
6611
+ peers.length > 5 && /* @__PURE__ */ jsxs12("span", { className: "rk-avatar rk-avatar-more", children: [
6316
6612
  "+",
6317
6613
  peers.length - 5
6318
6614
  ] })
@@ -6336,7 +6632,7 @@ function PeerCursors() {
6336
6632
  }
6337
6633
  }
6338
6634
  peersRef.current = peers;
6339
- useEffect10(() => {
6635
+ useEffect11(() => {
6340
6636
  const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
6341
6637
  let raf = 0;
6342
6638
  let last = performance.now();
@@ -6441,8 +6737,8 @@ function PeerCursors() {
6441
6737
  return () => cancelAnimationFrame(raf);
6442
6738
  }, []);
6443
6739
  if (peers.length === 0) return null;
6444
- return /* @__PURE__ */ jsxs11(Fragment6, { children: [
6445
- peers.map((peer) => /* @__PURE__ */ jsx12(
6740
+ return /* @__PURE__ */ jsxs12(Fragment7, { children: [
6741
+ peers.map((peer) => /* @__PURE__ */ jsx13(
6446
6742
  "div",
6447
6743
  {
6448
6744
  ref: (el) => {
@@ -6454,7 +6750,7 @@ function PeerCursors() {
6454
6750
  },
6455
6751
  `ring-${peer.id}`
6456
6752
  )),
6457
- peers.map((peer, index) => /* @__PURE__ */ jsxs11(
6753
+ peers.map((peer, index) => /* @__PURE__ */ jsxs12(
6458
6754
  "div",
6459
6755
  {
6460
6756
  ref: (el) => {
@@ -6464,8 +6760,8 @@ function PeerCursors() {
6464
6760
  className: "rk-peer-cursor",
6465
6761
  style: { opacity: 0 },
6466
6762
  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(
6763
+ /* @__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" }) }),
6764
+ /* @__PURE__ */ jsxs12(
6469
6765
  "span",
6470
6766
  {
6471
6767
  className: "rk-peer-name",
@@ -6484,19 +6780,19 @@ function PeerCursors() {
6484
6780
  }
6485
6781
 
6486
6782
  // 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";
6783
+ import { useEffect as useEffect12, useRef as useRef12, useState as useState10 } from "react";
6784
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
6489
6785
  function QuickComment() {
6490
6786
  const { quick } = useStore();
6491
6787
  if (!quick) return null;
6492
- return /* @__PURE__ */ jsx13(QuickComposerCard, { quick }, quick.id);
6788
+ return /* @__PURE__ */ jsx14(QuickComposerCard, { quick }, quick.id);
6493
6789
  }
6494
6790
  function QuickComposerCard({ quick }) {
6495
6791
  const { route, session } = useStore();
6496
6792
  const dispatch = useDispatch();
6497
- const [text, setText] = useState9("");
6793
+ const [text, setText] = useState10("");
6498
6794
  const fieldRef = useRef12(null);
6499
- useEffect11(() => {
6795
+ useEffect12(() => {
6500
6796
  const field = fieldRef.current;
6501
6797
  if (!field) return;
6502
6798
  field.focus();
@@ -6549,18 +6845,18 @@ function QuickComposerCard({ quick }) {
6549
6845
  };
6550
6846
  const x = Math.min(Math.max(quick.x, 8), window.innerWidth - 288);
6551
6847
  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" }),
6848
+ return /* @__PURE__ */ jsxs13("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
6849
+ /* @__PURE__ */ jsxs13("div", { className: "rk-quick-head", children: [
6850
+ /* @__PURE__ */ jsx14(IconComment, { size: 12 }),
6851
+ /* @__PURE__ */ jsx14("span", { children: "Comment here" }),
6852
+ /* @__PURE__ */ jsxs13("span", { className: "rk-hint", children: [
6853
+ /* @__PURE__ */ jsx14("span", { className: "rk-kbd", children: "\u21B5" }),
6558
6854
  " save \xB7 ",
6559
- /* @__PURE__ */ jsx13("span", { className: "rk-kbd", children: "esc" }),
6855
+ /* @__PURE__ */ jsx14("span", { className: "rk-kbd", children: "esc" }),
6560
6856
  " cancel"
6561
6857
  ] })
6562
6858
  ] }),
6563
- /* @__PURE__ */ jsx13(
6859
+ /* @__PURE__ */ jsx14(
6564
6860
  "textarea",
6565
6861
  {
6566
6862
  ref: fieldRef,
@@ -6576,8 +6872,8 @@ function QuickComposerCard({ quick }) {
6576
6872
  }
6577
6873
 
6578
6874
  // 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";
6875
+ import { useEffect as useEffect13, useRef as useRef13, useState as useState11 } from "react";
6876
+ import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
6581
6877
  function applyRect(el, rect, pad2 = 3) {
6582
6878
  if (!el) return;
6583
6879
  if (!rect) {
@@ -6592,14 +6888,14 @@ function applyRect(el, rect, pad2 = 3) {
6592
6888
  function PickLayer() {
6593
6889
  const { mode, selection, tool } = useStore();
6594
6890
  const dispatch = useDispatch();
6595
- const [hovered, setHovered] = useState10(null);
6891
+ const [hovered, setHovered] = useState11(null);
6596
6892
  const outlineRef = useRef13(null);
6597
6893
  const labelRef = useRef13(null);
6598
6894
  const selectRef = useRef13(null);
6599
6895
  const hoveredRef = useRef13(null);
6600
6896
  hoveredRef.current = hovered;
6601
6897
  const picking = mode === "pick";
6602
- useEffect12(() => {
6898
+ useEffect13(() => {
6603
6899
  if (!picking) {
6604
6900
  setHovered(null);
6605
6901
  return;
@@ -6643,7 +6939,7 @@ function PickLayer() {
6643
6939
  document.removeEventListener("mousedown", swallow, true);
6644
6940
  };
6645
6941
  }, [picking, tool, dispatch]);
6646
- useEffect12(() => {
6942
+ useEffect13(() => {
6647
6943
  if (!picking) return;
6648
6944
  const body = document.body;
6649
6945
  const prev = body.style.cursor;
@@ -6653,7 +6949,7 @@ function PickLayer() {
6653
6949
  if (body.getAttribute("style") === "") body.removeAttribute("style");
6654
6950
  };
6655
6951
  }, [picking, tool]);
6656
- useEffect12(() => {
6952
+ useEffect13(() => {
6657
6953
  let raf = 0;
6658
6954
  const tick = () => {
6659
6955
  const hoverEl = hoveredRef.current;
@@ -6679,21 +6975,21 @@ function PickLayer() {
6679
6975
  raf = requestAnimationFrame(tick);
6680
6976
  return () => cancelAnimationFrame(raf);
6681
6977
  }, [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" }) })
6978
+ return /* @__PURE__ */ jsxs14(Fragment8, { children: [
6979
+ picking && /* @__PURE__ */ jsxs14(Fragment8, { children: [
6980
+ /* @__PURE__ */ jsx15("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
6981
+ /* @__PURE__ */ jsx15("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ jsxs14(Fragment8, { children: [
6982
+ /* @__PURE__ */ jsx15("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
6983
+ /* @__PURE__ */ jsx15("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
6984
+ ] }) : /* @__PURE__ */ jsx15("span", { children: "Pick an element" }) })
6689
6985
  ] }),
6690
- selection && /* @__PURE__ */ jsx14("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
6986
+ selection && /* @__PURE__ */ jsx15("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
6691
6987
  ] });
6692
6988
  }
6693
6989
 
6694
6990
  // 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";
6991
+ import { useEffect as useEffect14, useMemo as useMemo4, useRef as useRef14, useState as useState12 } from "react";
6992
+ import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
6697
6993
  var STATUS_LABELS = {
6698
6994
  open: "open",
6699
6995
  accepted: "accepted",
@@ -6705,22 +7001,22 @@ var RERESOLVE_MS = 800;
6705
7001
  function Pins() {
6706
7002
  const { session, route, activeItemId, editor, mode, filter } = useStore();
6707
7003
  const dispatch = useDispatch();
6708
- const [clusters, setClusters] = useState11([]);
6709
- const [orphanCount, setOrphanCount] = useState11(0);
7004
+ const [clusters, setClusters] = useState12([]);
7005
+ const [orphanCount, setOrphanCount] = useState12(0);
6710
7006
  const wrapRefs = useRef14(/* @__PURE__ */ new Map());
6711
7007
  const visibleCache = useRef14(/* @__PURE__ */ new Map());
6712
7008
  const highlightRef = useRef14(null);
6713
7009
  const hoveredItems = useRef14(null);
6714
7010
  const elementCache = useRef14(/* @__PURE__ */ new Map());
6715
7011
  const lastResolve = useRef14(0);
6716
- const [newestId, setNewestId] = useState11(null);
7012
+ const [newestId, setNewestId] = useState12(null);
6717
7013
  const prevCount = useRef14(0);
6718
7014
  const items = useMemo4(() => {
6719
7015
  const all = session?.items ?? [];
6720
7016
  return all.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => item.route === route);
6721
7017
  }, [session, route]);
6722
7018
  const byId = new Map(items.map(({ item }) => [item.id, item]));
6723
- useEffect13(() => {
7019
+ useEffect14(() => {
6724
7020
  const count = session?.items.length ?? 0;
6725
7021
  if (count > prevCount.current && session && count > 0) {
6726
7022
  const id = session.items[count - 1].id;
@@ -6731,7 +7027,7 @@ function Pins() {
6731
7027
  }
6732
7028
  prevCount.current = count;
6733
7029
  }, [session]);
6734
- useEffect13(() => {
7030
+ useEffect14(() => {
6735
7031
  let raf = 0;
6736
7032
  let lastSignature = "";
6737
7033
  const tick = (time) => {
@@ -6815,8 +7111,8 @@ function Pins() {
6815
7111
  dispatch({ type: "SET_ACTIVE_ITEM", id });
6816
7112
  dispatch({ type: "SET_PANEL", open: true });
6817
7113
  };
6818
- return /* @__PURE__ */ jsxs14(Fragment8, { children: [
6819
- /* @__PURE__ */ jsx15("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
7114
+ return /* @__PURE__ */ jsxs15(Fragment9, { children: [
7115
+ /* @__PURE__ */ jsx16("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
6820
7116
  clusters.map((cluster) => {
6821
7117
  const isCluster = cluster.ids.length > 1;
6822
7118
  const isActive = activeItemId !== null && cluster.ids.includes(activeItemId);
@@ -6826,7 +7122,7 @@ function Pins() {
6826
7122
  const status = statuses.size === 1 ? members[0]?.status : void 0;
6827
7123
  const dimmed = filter !== null && members.length > 0 && !members.some((item) => itemMatchesFilter(item, filter));
6828
7124
  const statusHint = isCluster ? status ? ` \xB7 all ${STATUS_LABELS[status] ?? status}` : "" : ` \xB7 ${STATUS_LABELS[members[0]?.status ?? "open"] ?? members[0]?.status}`;
6829
- return /* @__PURE__ */ jsx15(
7125
+ return /* @__PURE__ */ jsx16(
6830
7126
  "div",
6831
7127
  {
6832
7128
  ref: (el) => {
@@ -6834,7 +7130,7 @@ function Pins() {
6834
7130
  else wrapRefs.current.delete(cluster.key);
6835
7131
  },
6836
7132
  className: `rk-pin-wrap${picking ? " rk-pin-ghost" : ""}`,
6837
- children: /* @__PURE__ */ jsx15(
7133
+ children: /* @__PURE__ */ jsx16(
6838
7134
  "button",
6839
7135
  {
6840
7136
  type: "button",
@@ -6856,7 +7152,7 @@ function Pins() {
6856
7152
  cluster.key
6857
7153
  );
6858
7154
  }),
6859
- orphanCount > 0 && /* @__PURE__ */ jsxs14(
7155
+ orphanCount > 0 && /* @__PURE__ */ jsxs15(
6860
7156
  "button",
6861
7157
  {
6862
7158
  type: "button",
@@ -6864,7 +7160,7 @@ function Pins() {
6864
7160
  style: { bottom: 16, left: "50%", transform: "translateX(-50%)" },
6865
7161
  onClick: () => dispatch({ type: "SET_PANEL", open: true }),
6866
7162
  children: [
6867
- /* @__PURE__ */ jsx15(IconUnlink, { size: 13 }),
7163
+ /* @__PURE__ */ jsx16(IconUnlink, { size: 13 }),
6868
7164
  orphanCount,
6869
7165
  " unanchored item",
6870
7166
  orphanCount === 1 ? "" : "s"
@@ -6875,13 +7171,13 @@ function Pins() {
6875
7171
  }
6876
7172
 
6877
7173
  // 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";
7174
+ import { useEffect as useEffect15, useLayoutEffect as useLayoutEffect3, useRef as useRef15, useState as useState13 } from "react";
7175
+ import { Fragment as Fragment10, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6880
7176
  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 }) }
7177
+ { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ jsx17(IconTarget, { size: n }) },
7178
+ { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ jsx17(IconText, { size: n }) },
7179
+ { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ jsx17(IconComment, { size: n }) },
7180
+ { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ jsx17(IconImage, { size: n }) }
6885
7181
  ];
6886
7182
  var POS_KEY2 = "review-kit:toolbar-pos";
6887
7183
  var DRAG_THRESHOLD2 = 6;
@@ -6890,9 +7186,9 @@ function Toolbar() {
6890
7186
  const hasPeers = syncStatus !== "disabled" && peers.length > 0;
6891
7187
  const dispatch = useDispatch();
6892
7188
  const ref = useRef15(null);
6893
- const [pos, setPos] = useState12(() => readStoredPos(POS_KEY2));
7189
+ const [pos, setPos] = useState13(() => readStoredPos(POS_KEY2));
6894
7190
  const drag = useRef15(null);
6895
- const [dragging, setDragging] = useState12(false);
7191
+ const [dragging, setDragging] = useState13(false);
6896
7192
  const justDragged = useRef15(false);
6897
7193
  const visible = editor === null || selection !== null;
6898
7194
  const applyPos = (next, persist) => {
@@ -6906,7 +7202,7 @@ function Toolbar() {
6906
7202
  const clamped = clampPos(pos.x, pos.y, el.offsetWidth, el.offsetHeight);
6907
7203
  if (clamped.x !== pos.x || clamped.y !== pos.y) applyPos(clamped, true);
6908
7204
  }, [pos, selection, hasPeers, visible]);
6909
- useEffect14(() => {
7205
+ useEffect15(() => {
6910
7206
  const onResize = () => {
6911
7207
  const el = ref.current;
6912
7208
  const current = readStoredPos(POS_KEY2);
@@ -7019,7 +7315,7 @@ function Toolbar() {
7019
7315
  mode === "pick" && selection === null ? "rk-ghost" : ""
7020
7316
  ].filter(Boolean).join(" ");
7021
7317
  const style = pos !== null ? { left: 0, top: 0, translate: `${pos.x}px ${pos.y}px` } : void 0;
7022
- return /* @__PURE__ */ jsxs15(
7318
+ return /* @__PURE__ */ jsxs16(
7023
7319
  "div",
7024
7320
  {
7025
7321
  ref,
@@ -7030,7 +7326,7 @@ function Toolbar() {
7030
7326
  onPointerDown,
7031
7327
  onClickCapture,
7032
7328
  children: [
7033
- /* @__PURE__ */ jsx16(
7329
+ /* @__PURE__ */ jsx17(
7034
7330
  "button",
7035
7331
  {
7036
7332
  type: "button",
@@ -7038,19 +7334,19 @@ function Toolbar() {
7038
7334
  "aria-label": "Move the toolbar \u2014 drag, or use arrow keys; double-click to reset its position",
7039
7335
  onKeyDown: onGripKeyDown,
7040
7336
  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" })
7337
+ children: /* @__PURE__ */ jsxs16("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
7338
+ /* @__PURE__ */ jsx17("circle", { cx: "2", cy: "2", r: "1.1" }),
7339
+ /* @__PURE__ */ jsx17("circle", { cx: "6", cy: "2", r: "1.1" }),
7340
+ /* @__PURE__ */ jsx17("circle", { cx: "2", cy: "7", r: "1.1" }),
7341
+ /* @__PURE__ */ jsx17("circle", { cx: "6", cy: "7", r: "1.1" }),
7342
+ /* @__PURE__ */ jsx17("circle", { cx: "2", cy: "12", r: "1.1" }),
7343
+ /* @__PURE__ */ jsx17("circle", { cx: "6", cy: "12", r: "1.1" })
7048
7344
  ] })
7049
7345
  }
7050
7346
  ),
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(
7347
+ /* @__PURE__ */ jsx17(PresenceStrip, {}),
7348
+ hasPeers && /* @__PURE__ */ jsx17("span", { className: "rk-toolbar-sep" }),
7349
+ /* @__PURE__ */ jsx17("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ jsx17(
7054
7350
  "button",
7055
7351
  {
7056
7352
  type: "button",
@@ -7062,9 +7358,9 @@ function Toolbar() {
7062
7358
  },
7063
7359
  entry.tool
7064
7360
  )) }),
7065
- selection && /* @__PURE__ */ jsx16("span", { className: "rk-toolbar-sep" }),
7066
- selection && /* @__PURE__ */ jsxs15(Fragment9, { children: [
7067
- /* @__PURE__ */ jsxs15(
7361
+ selection && /* @__PURE__ */ jsx17("span", { className: "rk-toolbar-sep" }),
7362
+ selection && /* @__PURE__ */ jsxs16(Fragment10, { children: [
7363
+ /* @__PURE__ */ jsxs16(
7068
7364
  "button",
7069
7365
  {
7070
7366
  type: "button",
@@ -7073,12 +7369,12 @@ function Toolbar() {
7073
7369
  title: `Rewrite text${keyHint("tool-copy")}`,
7074
7370
  onClick: () => open("copy"),
7075
7371
  children: [
7076
- /* @__PURE__ */ jsx16(IconText, { size: 14 }),
7372
+ /* @__PURE__ */ jsx17(IconText, { size: 14 }),
7077
7373
  " Rewrite"
7078
7374
  ]
7079
7375
  }
7080
7376
  ),
7081
- hasImage && /* @__PURE__ */ jsxs15(
7377
+ hasImage && /* @__PURE__ */ jsxs16(
7082
7378
  "button",
7083
7379
  {
7084
7380
  type: "button",
@@ -7087,12 +7383,12 @@ function Toolbar() {
7087
7383
  title: `Replace image${keyHint("tool-image")}`,
7088
7384
  onClick: () => open("image"),
7089
7385
  children: [
7090
- /* @__PURE__ */ jsx16(IconImage, { size: 14 }),
7386
+ /* @__PURE__ */ jsx17(IconImage, { size: 14 }),
7091
7387
  " Image"
7092
7388
  ]
7093
7389
  }
7094
7390
  ),
7095
- /* @__PURE__ */ jsxs15(
7391
+ /* @__PURE__ */ jsxs16(
7096
7392
  "button",
7097
7393
  {
7098
7394
  type: "button",
@@ -7101,32 +7397,32 @@ function Toolbar() {
7101
7397
  title: `Comment${keyHint("tool-comment")}`,
7102
7398
  onClick: () => open("comment"),
7103
7399
  children: [
7104
- /* @__PURE__ */ jsx16(IconComment, { size: 14 }),
7400
+ /* @__PURE__ */ jsx17(IconComment, { size: 14 }),
7105
7401
  " Comment"
7106
7402
  ]
7107
7403
  }
7108
7404
  ),
7109
- /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
7110
- /* @__PURE__ */ jsx16(IconStyle, { size: 14 }),
7405
+ /* @__PURE__ */ jsxs16("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
7406
+ /* @__PURE__ */ jsx17(IconStyle, { size: 14 }),
7111
7407
  " Style"
7112
7408
  ] }),
7113
- /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
7114
- /* @__PURE__ */ jsx16(IconMove, { size: 14 }),
7409
+ /* @__PURE__ */ jsxs16("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
7410
+ /* @__PURE__ */ jsx17(IconMove, { size: 14 }),
7115
7411
  " Move"
7116
7412
  ] }),
7117
- /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
7118
- /* @__PURE__ */ jsx16(IconTrash, { size: 14 }),
7413
+ /* @__PURE__ */ jsxs16("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
7414
+ /* @__PURE__ */ jsx17(IconTrash, { size: 14 }),
7119
7415
  " Delete"
7120
7416
  ] }),
7121
- /* @__PURE__ */ jsx16("span", { className: "rk-toolbar-sep" }),
7122
- /* @__PURE__ */ jsx16(
7417
+ /* @__PURE__ */ jsx17("span", { className: "rk-toolbar-sep" }),
7418
+ /* @__PURE__ */ jsx17(
7123
7419
  "button",
7124
7420
  {
7125
7421
  type: "button",
7126
7422
  className: "rk-icon-btn",
7127
7423
  "aria-label": "Deselect",
7128
7424
  onClick: () => dispatch({ type: "SELECT", selection: null }),
7129
- children: /* @__PURE__ */ jsx16(IconClose, { size: 13 })
7425
+ children: /* @__PURE__ */ jsx17(IconClose, { size: 13 })
7130
7426
  }
7131
7427
  )
7132
7428
  ] })
@@ -7136,27 +7432,27 @@ function Toolbar() {
7136
7432
  }
7137
7433
 
7138
7434
  // src/client/components/Toast.tsx
7139
- import { useEffect as useEffect15 } from "react";
7140
- import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
7435
+ import { useEffect as useEffect16 } from "react";
7436
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
7141
7437
  function ToastRow({ toast: toast2 }) {
7142
7438
  const dispatch = useDispatch();
7143
- useEffect15(() => {
7439
+ useEffect16(() => {
7144
7440
  const timer = window.setTimeout(() => dispatch({ type: "DISMISS_TOAST", id: toast2.id }), 3500);
7145
7441
  return () => window.clearTimeout(timer);
7146
7442
  }, [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 }),
7443
+ return /* @__PURE__ */ jsxs17("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
7444
+ toast2.tone === "success" ? /* @__PURE__ */ jsx18(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ jsx18(IconWarning, { size: 13 }) : /* @__PURE__ */ jsx18(IconComment, { size: 13 }),
7149
7445
  toast2.message
7150
7446
  ] });
7151
7447
  }
7152
7448
  function ToastHost() {
7153
7449
  const { toasts } = useStore();
7154
7450
  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)) });
7451
+ return /* @__PURE__ */ jsx18("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ jsx18(ToastRow, { toast: toast2 }, toast2.id)) });
7156
7452
  }
7157
7453
 
7158
7454
  // src/client/App.tsx
7159
- import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
7455
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
7160
7456
  function App() {
7161
7457
  const state = useStore();
7162
7458
  const rawDispatch = useDispatch();
@@ -7166,10 +7462,10 @@ function App() {
7166
7462
  const storageKey = config.storageKey ?? defaultStorageKey();
7167
7463
  useAppliedPreview(state, dispatch);
7168
7464
  useEmblemaAutoPush(state, dispatch, config);
7169
- useEffect16(() => {
7465
+ useEffect17(() => {
7170
7466
  setEffectAssetsUrl(config.fxAssetsUrl);
7171
7467
  }, [config.fxAssetsUrl]);
7172
- useEffect16(() => {
7468
+ useEffect17(() => {
7173
7469
  let cancelled = false;
7174
7470
  void loadSession(storageKey).then((stored) => {
7175
7471
  if (cancelled) return;
@@ -7187,7 +7483,7 @@ function App() {
7187
7483
  };
7188
7484
  }, [storageKey, config.defaultAuthor, dispatch]);
7189
7485
  const { session } = state;
7190
- useEffect16(() => {
7486
+ useEffect17(() => {
7191
7487
  if (!session) return;
7192
7488
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
7193
7489
  saveTimer.current = window.setTimeout(() => {
@@ -7198,9 +7494,9 @@ function App() {
7198
7494
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
7199
7495
  };
7200
7496
  }, [session, storageKey, config]);
7201
- useEffect16(() => trackPointer(), []);
7497
+ useEffect17(() => trackPointer(), []);
7202
7498
  const focusHandled = useRef16(false);
7203
- useEffect16(() => {
7499
+ useEffect17(() => {
7204
7500
  if (!session || focusHandled.current) return;
7205
7501
  focusHandled.current = true;
7206
7502
  const id = takeFocusItem();
@@ -7225,7 +7521,7 @@ function App() {
7225
7521
  cancelled = true;
7226
7522
  };
7227
7523
  }, [session, dispatch]);
7228
- useEffect16(() => {
7524
+ useEffect17(() => {
7229
7525
  let last = window.location.pathname;
7230
7526
  const check = () => {
7231
7527
  if (window.location.pathname !== last) {
@@ -7242,7 +7538,7 @@ function App() {
7242
7538
  }, [dispatch]);
7243
7539
  const stateRef = useRef16(state);
7244
7540
  stateRef.current = state;
7245
- useEffect16(() => {
7541
+ useEffect17(() => {
7246
7542
  const onKeyDown = (event) => {
7247
7543
  const current = stateRef.current;
7248
7544
  for (const def of SHORTCUTS) {
@@ -7257,7 +7553,7 @@ function App() {
7257
7553
  document.addEventListener("keydown", onKeyDown);
7258
7554
  return () => document.removeEventListener("keydown", onKeyDown);
7259
7555
  }, [dispatch]);
7260
- useEffect16(() => {
7556
+ useEffect17(() => {
7261
7557
  if (!state.editor) return;
7262
7558
  const editorEl = state.editor.element;
7263
7559
  const onPointerDown = (event) => {
@@ -7270,32 +7566,33 @@ function App() {
7270
7566
  }, [state.editor, dispatch]);
7271
7567
  if (!session) return null;
7272
7568
  if (state.hidden) {
7273
- return /* @__PURE__ */ jsx18(DispatchContext.Provider, { value: dispatch, children: null });
7569
+ return /* @__PURE__ */ jsx19(DispatchContext.Provider, { value: dispatch, children: null });
7274
7570
  }
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, {})
7571
+ return /* @__PURE__ */ jsxs18(DispatchContext.Provider, { value: dispatch, children: [
7572
+ /* @__PURE__ */ jsx19(PickLayer, {}),
7573
+ /* @__PURE__ */ jsx19(PeerCursors, {}),
7574
+ /* @__PURE__ */ jsx19(Toolbar, {}),
7575
+ state.editor?.kind === "copy" && /* @__PURE__ */ jsx19(InlineEditor, { editor: state.editor }),
7576
+ state.editor?.kind === "image" && /* @__PURE__ */ jsx19(ImageEditor, { editor: state.editor }),
7577
+ state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ jsx19(NoteComposer, { editor: state.editor }),
7578
+ /* @__PURE__ */ jsx19(Pins, {}),
7579
+ /* @__PURE__ */ jsx19(QuickComment, {}),
7580
+ /* @__PURE__ */ jsx19(Panel, {}),
7581
+ /* @__PURE__ */ jsx19(Badge, {}),
7582
+ /* @__PURE__ */ jsx19(CoachMark, {}),
7583
+ /* @__PURE__ */ jsx19(CheatSheet, {}),
7584
+ /* @__PURE__ */ jsx19(EmblemaConnect, {}),
7585
+ /* @__PURE__ */ jsx19(TerminalConnect, {}),
7586
+ /* @__PURE__ */ jsx19(ConfirmHost, {}),
7587
+ /* @__PURE__ */ jsx19(ToastHost, {})
7291
7588
  ] });
7292
7589
  }
7293
7590
 
7294
7591
  // src/client/ReviewKit.tsx
7295
- import { jsx as jsx19 } from "react/jsx-runtime";
7592
+ import { jsx as jsx20 } from "react/jsx-runtime";
7296
7593
  function ShadowHost({ config }) {
7297
- const [mount, setMount] = useState13(null);
7298
- useEffect17(() => {
7594
+ const [mount, setMount] = useState14(null);
7595
+ useEffect18(() => {
7299
7596
  const host = document.createElement("div");
7300
7597
  host.setAttribute(HOST_ATTR, "");
7301
7598
  host.style.position = "fixed";
@@ -7331,15 +7628,15 @@ function ShadowHost({ config }) {
7331
7628
  }, []);
7332
7629
  if (!mount) return null;
7333
7630
  return createPortal(
7334
- /* @__PURE__ */ jsx19(StoreProvider, { config, children: /* @__PURE__ */ jsx19(App, {}) }),
7631
+ /* @__PURE__ */ jsx20(StoreProvider, { config, children: /* @__PURE__ */ jsx20(App, {}) }),
7335
7632
  mount
7336
7633
  );
7337
7634
  }
7338
7635
  function ReviewKit(props) {
7339
- const [active, setActive] = useState13(false);
7636
+ const [active, setActive] = useState14(false);
7340
7637
  const { enabled, token, allowedHosts, devAutoOn } = props;
7341
7638
  const hostsKey = allowedHosts?.join("\0") ?? "";
7342
- useEffect17(() => {
7639
+ useEffect18(() => {
7343
7640
  if (enabled === false) {
7344
7641
  setActive(false);
7345
7642
  return;
@@ -7350,7 +7647,7 @@ function ReviewKit(props) {
7350
7647
  return () => window.removeEventListener(DEACTIVATE_EVENT, onDeactivate);
7351
7648
  }, [enabled, token, devAutoOn, hostsKey]);
7352
7649
  if (enabled === false || !active) return null;
7353
- return /* @__PURE__ */ jsx19(ShadowHost, { config: props });
7650
+ return /* @__PURE__ */ jsx20(ShadowHost, { config: props });
7354
7651
  }
7355
7652
 
7356
7653
  export {
@@ -7363,4 +7660,4 @@ export {
7363
7660
  importSessionFile,
7364
7661
  ReviewKit
7365
7662
  };
7366
- //# sourceMappingURL=chunk-NSXRFM75.js.map
7663
+ //# sourceMappingURL=chunk-5MAFDRUI.js.map