@satanwagen/reviewkit 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/next.cjs CHANGED
@@ -1045,6 +1045,8 @@ input, textarea, select {
1045
1045
  .rk-sync-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--rk-fg-faint); }
1046
1046
  .rk-sync-online .rk-sync-dot { background: var(--rk-success); }
1047
1047
  .rk-sync-connecting .rk-sync-dot { background: var(--rk-warn); }
1048
+ .rk-sync-reason-token-short .rk-sync-dot, .rk-sync-reason-bad-token .rk-sync-dot { background: var(--rk-danger); }
1049
+ .rk-sync[title] { cursor: help; }
1048
1050
  /* Automatic Emblema push hint (same quiet language) */
1049
1051
  .rk-epush-ok .rk-sync-dot { background: var(--rk-success); }
1050
1052
  .rk-epush-sending .rk-sync-dot { background: var(--rk-warn); }
@@ -2645,7 +2647,8 @@ function reducer(state, action) {
2645
2647
  panelOpen: false,
2646
2648
  confirm: null,
2647
2649
  cheatsheetOpen: false,
2648
- emblemaConnectOpen: false
2650
+ emblemaConnectOpen: false,
2651
+ terminalConnectOpen: false
2649
2652
  } : { ...state, hidden: false };
2650
2653
  case "SELECT":
2651
2654
  return { ...state, selection: action.selection };
@@ -2755,12 +2758,16 @@ function reducer(state, action) {
2755
2758
  return { ...state, cheatsheetOpen: action.open };
2756
2759
  case "SET_EMBLEMA_CONNECT":
2757
2760
  return { ...state, emblemaConnectOpen: action.open };
2761
+ case "SET_TERMINAL_CONNECT":
2762
+ return { ...state, terminalConnectOpen: action.open };
2758
2763
  case "SET_COACH":
2759
2764
  return { ...state, coachOpen: action.open };
2760
2765
  case "SET_CORNER":
2761
2766
  return { ...state, badgeCorner: action.corner };
2762
- case "SYNC_STATUS":
2763
- return state.syncStatus === action.status ? state : { ...state, syncStatus: action.status };
2767
+ case "SYNC_STATUS": {
2768
+ const reason = action.reason === void 0 ? action.status === "online" ? null : state.syncReason : action.reason;
2769
+ return state.syncStatus === action.status && state.syncReason === reason ? state : { ...state, syncStatus: action.status, syncReason: reason };
2770
+ }
2764
2771
  case "EMBLEMA_PUSH_STATUS":
2765
2772
  return state.emblemaPush === action.status ? state : { ...state, emblemaPush: action.status };
2766
2773
  /*
@@ -2888,9 +2895,11 @@ function initialState() {
2888
2895
  confirm: null,
2889
2896
  cheatsheetOpen: false,
2890
2897
  emblemaConnectOpen: false,
2898
+ terminalConnectOpen: false,
2891
2899
  coachOpen: false,
2892
2900
  undoStack: [],
2893
2901
  syncStatus: "disabled",
2902
+ syncReason: null,
2894
2903
  emblemaPush: "idle",
2895
2904
  peers: [],
2896
2905
  quick: null,
@@ -2902,7 +2911,7 @@ function StoreProvider({ config, children }) {
2902
2911
  const configValue = (0, import_react.useMemo)(
2903
2912
  () => config,
2904
2913
  // eslint-disable-next-line react-hooks/exhaustive-deps -- config object identity is per-render; depend on its fields
2905
- [config.enabled, config.token, config.devAutoOn, config.syncToken, config.syncUrl, config.defaultAuthor, config.fxAssetsUrl, config.position, config.storageKey, config.onSessionChange, config.emblema]
2914
+ [config.enabled, config.token, config.devAutoOn, config.sync, config.syncToken, config.syncUrl, config.defaultAuthor, config.fxAssetsUrl, config.position, config.storageKey, config.onSessionChange, config.emblema]
2906
2915
  );
2907
2916
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigContext.Provider, { value: configValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StateContext.Provider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DispatchContext.Provider, { value: dispatch, children }) }) });
2908
2917
  }
@@ -4838,9 +4847,13 @@ function createRkSync(options) {
4838
4847
  pongTimer = null;
4839
4848
  }
4840
4849
  break;
4841
- case "error":
4850
+ case "error": {
4842
4851
  log("server error:", msg.code, msg.message);
4852
+ const code = typeof msg.code === "string" ? msg.code : "error";
4853
+ const message = typeof msg.message === "string" ? msg.message : "";
4854
+ safely(() => handlers.onError?.(code, message));
4843
4855
  break;
4856
+ }
4844
4857
  case "presence:join": {
4845
4858
  const peer = msg.peer;
4846
4859
  if (peer && typeof peer.id === "string") safely(() => handlers.onPeerJoin?.(peer));
@@ -5062,6 +5075,15 @@ var init_identity = __esm({
5062
5075
  });
5063
5076
 
5064
5077
  // src/client/sync.ts
5078
+ function resolveSyncUrl(config) {
5079
+ return typeof config.syncUrl === "string" && config.syncUrl !== "" ? config.syncUrl : DEFAULT_SYNC_URL;
5080
+ }
5081
+ function resolveSyncToken(config) {
5082
+ if (config.sync === false) return null;
5083
+ const explicit = typeof config.syncToken === "string" && config.syncToken !== "" ? config.syncToken : null;
5084
+ const fallback = typeof config.token === "string" && config.token !== "" ? config.token : null;
5085
+ return explicit ?? fallback;
5086
+ }
5065
5087
  function toPeerState(peer) {
5066
5088
  return {
5067
5089
  id: peer.id,
@@ -5087,20 +5109,43 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
5087
5109
  const stateRef = (0, import_react4.useRef)(state);
5088
5110
  stateRef.current = state;
5089
5111
  const myIdRef = (0, import_react4.useRef)("");
5090
- const enabled = typeof config.syncToken === "string" && config.syncToken !== "" && typeof config.syncUrl === "string" && config.syncUrl !== "";
5112
+ const syncToken = resolveSyncToken(config);
5113
+ const syncUrl = resolveSyncUrl(config);
5114
+ const enabled = syncToken !== null;
5091
5115
  const author = state.session?.author ?? "";
5092
5116
  const hasSession = state.session !== null;
5093
5117
  (0, import_react4.useEffect)(() => {
5094
5118
  if (!enabled || !hasSession) return;
5119
+ const dispatchGuard = rawDispatch;
5120
+ if (syncToken.length < MIN_SYNC_TOKEN_LENGTH) {
5121
+ dispatchGuard({ type: "SYNC_STATUS", status: "offline", reason: "token-short" });
5122
+ if (!warnedShortToken) {
5123
+ warnedShortToken = true;
5124
+ console.warn(
5125
+ `[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.`
5126
+ );
5127
+ }
5128
+ return () => dispatchGuard({ type: "SYNC_STATUS", status: "disabled", reason: null });
5129
+ }
5095
5130
  const id = reviewerId();
5096
5131
  myIdRef.current = id;
5097
5132
  const dispatch = rawDispatch;
5098
5133
  const sync = createRkSync({
5099
- token: config.syncToken,
5100
- url: config.syncUrl,
5134
+ token: syncToken,
5135
+ url: syncUrl,
5101
5136
  user: { id, name: author || "Reviewer", color: colorForReviewer(id) },
5102
5137
  handlers: {
5103
- onStatus: (status) => dispatch({ type: "SYNC_STATUS", status }),
5138
+ onStatus: (status) => dispatch({
5139
+ type: "SYNC_STATUS",
5140
+ status,
5141
+ // Offline with no server verdict = the socket never got through
5142
+ // (server down, CSP connect-src, network); a verdict keeps its reason.
5143
+ ...status === "offline" ? { reason: stateRef.current.syncReason ?? "unreachable" } : {}
5144
+ }),
5145
+ onError: (code) => {
5146
+ if (code === "bad-token") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "bad-token" });
5147
+ else if (code === "room-full") dispatch({ type: "SYNC_STATUS", status: "offline", reason: "room-full" });
5148
+ },
5104
5149
  onSnapshot: (items, peers, deletedIds = []) => {
5105
5150
  const safeItems = items.map(sanitizeRemoteItem).filter((item) => item !== null);
5106
5151
  dispatch({ type: "SYNC_SNAPSHOT", items: safeItems });
@@ -5157,10 +5202,10 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
5157
5202
  if (activeSyncRef === sync) activeSyncRef = null;
5158
5203
  setEffectBroadcaster2(null);
5159
5204
  sync.stop();
5160
- dispatch({ type: "SYNC_STATUS", status: "disabled" });
5205
+ dispatch({ type: "SYNC_STATUS", status: "disabled", reason: null });
5161
5206
  dispatch({ type: "PEERS_SET", peers: [] });
5162
5207
  };
5163
- }, [enabled, hasSession, config.syncToken, config.syncUrl, author, rawDispatch]);
5208
+ }, [enabled, hasSession, syncToken, syncUrl, author, rawDispatch]);
5164
5209
  const wrapped = (0, import_react4.useMemo)(() => {
5165
5210
  return (action) => {
5166
5211
  const prev = stateRef.current;
@@ -5223,7 +5268,7 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
5223
5268
  }, [enabled, editorOpen]);
5224
5269
  return enabled ? wrapped : rawDispatch;
5225
5270
  }
5226
- var import_react4, activeSyncRef, MIRRORED_EXCLUDED;
5271
+ var import_react4, DEFAULT_SYNC_URL, MIN_SYNC_TOKEN_LENGTH, warnedShortToken, activeSyncRef, MIRRORED_EXCLUDED;
5227
5272
  var init_sync = __esm({
5228
5273
  "src/client/sync.ts"() {
5229
5274
  "use strict";
@@ -5235,6 +5280,9 @@ var init_sync = __esm({
5235
5280
  init_schema();
5236
5281
  init_store();
5237
5282
  init_identity();
5283
+ DEFAULT_SYNC_URL = "wss://2-59-219-26.sslip.io/rk-sync";
5284
+ MIN_SYNC_TOKEN_LENGTH = 8;
5285
+ warnedShortToken = false;
5238
5286
  activeSyncRef = null;
5239
5287
  MIRRORED_EXCLUDED = /* @__PURE__ */ new Set(["SYNC_SNAPSHOT", "REMOTE_ADD", "REMOTE_PATCH", "REMOTE_STATUS", "REMOTE_DELETE", "INIT_SESSION"]);
5240
5288
  }
@@ -5717,10 +5765,10 @@ function EmblemaConnect() {
5717
5765
  state === null && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "rk-modal-text", children: [
5718
5766
  "The local sync agent isn't running. In the project repo run:",
5719
5767
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("code", { className: "rk-connect-cmd", children: [
5720
- "npx @satanwagen/reviewkit-cli init --origin ",
5768
+ "npx -p @satanwagen/reviewkit-cli reviewkit-emblema init --origin ",
5721
5769
  window.location.origin
5722
5770
  ] }),
5723
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "rk-connect-cmd", children: "npx @satanwagen/reviewkit-cli serve" }),
5771
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "rk-connect-cmd", children: "npx -p @satanwagen/reviewkit-cli reviewkit-emblema serve" }),
5724
5772
  "then reopen this dialog."
5725
5773
  ] }),
5726
5774
  state !== null && state !== "loading" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
@@ -5818,12 +5866,247 @@ var init_EmblemaConnect = __esm({
5818
5866
  }
5819
5867
  });
5820
5868
 
5869
+ // src/integrations/terminal.ts
5870
+ async function fetchJson2(url, init, timeoutMs) {
5871
+ const abort = new AbortController();
5872
+ const timer = window.setTimeout(() => abort.abort(), timeoutMs);
5873
+ try {
5874
+ return await fetch(url, { ...init, signal: abort.signal });
5875
+ } finally {
5876
+ window.clearTimeout(timer);
5877
+ }
5878
+ }
5879
+ async function probeTerminal() {
5880
+ for (const port of TERMINAL_PAIR_PORTS) {
5881
+ try {
5882
+ const res = await fetchJson2(`http://127.0.0.1:${port}/pair`, { method: "GET" }, 900);
5883
+ if (!res.ok) continue;
5884
+ const body = await res.json();
5885
+ if (typeof body === "object" && body !== null && body.app === "reviewkit-cli") {
5886
+ const b = body;
5887
+ return {
5888
+ port,
5889
+ cwd: typeof b.cwd === "string" ? b.cwd : void 0,
5890
+ projects: Array.isArray(b.projects) ? b.projects.filter((p) => typeof p === "string") : []
5891
+ };
5892
+ }
5893
+ } catch {
5894
+ }
5895
+ }
5896
+ return null;
5897
+ }
5898
+ async function pairTerminal(port, key) {
5899
+ try {
5900
+ const res = await fetchJson2(
5901
+ `http://127.0.0.1:${port}/pair`,
5902
+ { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ key }) },
5903
+ 5e3
5904
+ );
5905
+ const body = await res.json().catch(() => null);
5906
+ if (res.ok && body?.ok) return { ok: true, name: body.name ?? "", repo: body.repo ?? null };
5907
+ return { ok: false, error: body?.error ?? `HTTP ${res.status}` };
5908
+ } catch {
5909
+ return { ok: false, error: "the terminal stopped answering" };
5910
+ }
5911
+ }
5912
+ var TERMINAL_PAIR_PORTS;
5913
+ var init_terminal = __esm({
5914
+ "src/integrations/terminal.ts"() {
5915
+ "use strict";
5916
+ TERMINAL_PAIR_PORTS = [48780, 48781, 48782, 48783];
5917
+ }
5918
+ });
5919
+
5920
+ // src/client/terminalKey.ts
5921
+ function base64url(text) {
5922
+ const bytes = new TextEncoder().encode(text);
5923
+ let binary = "";
5924
+ for (const byte of bytes) binary += String.fromCharCode(byte);
5925
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
5926
+ }
5927
+ function encodeTerminalKey(fields) {
5928
+ const payload = { v: TERMINAL_KEY_VERSION, ...fields };
5929
+ return TERMINAL_KEY_PREFIX + base64url(JSON.stringify(payload));
5930
+ }
5931
+ function currentTerminalKey(syncUrl, syncToken) {
5932
+ const origin = window.location.origin;
5933
+ let route = "/";
5934
+ try {
5935
+ route = window.location.pathname || "/";
5936
+ } catch {
5937
+ }
5938
+ return encodeTerminalKey({ name: window.location.hostname, origin, syncUrl, syncToken, route });
5939
+ }
5940
+ var TERMINAL_KEY_PREFIX, TERMINAL_KEY_VERSION;
5941
+ var init_terminalKey = __esm({
5942
+ "src/client/terminalKey.ts"() {
5943
+ "use strict";
5944
+ TERMINAL_KEY_PREFIX = "rkc_";
5945
+ TERMINAL_KEY_VERSION = 1;
5946
+ }
5947
+ });
5948
+
5949
+ // src/client/components/TerminalConnect.tsx
5950
+ function shortKey2(key) {
5951
+ return `${key.slice(0, 8)}\u2026${key.slice(-6)}`;
5952
+ }
5953
+ function shortPath(path) {
5954
+ if (!path) return "";
5955
+ const parts = path.split("/").filter(Boolean);
5956
+ return parts.length > 2 ? `\u2026/${parts.slice(-2).join("/")}` : path;
5957
+ }
5958
+ function TerminalConnect() {
5959
+ const { terminalConnectOpen } = useStore();
5960
+ const config = useConfig();
5961
+ const dispatch = useDispatch();
5962
+ const [phase, setPhase] = (0, import_react7.useState)("probing");
5963
+ const [probe, setProbe] = (0, import_react7.useState)(null);
5964
+ const [paired, setPaired] = (0, import_react7.useState)(null);
5965
+ const [showManual, setShowManual] = (0, import_react7.useState)(false);
5966
+ const syncToken = resolveSyncToken(config);
5967
+ const key = terminalConnectOpen && syncToken ? currentTerminalKey(resolveSyncUrl(config), syncToken) : null;
5968
+ (0, import_react7.useEffect)(() => {
5969
+ if (!terminalConnectOpen) return;
5970
+ let cancelled = false;
5971
+ setPhase("probing");
5972
+ setProbe(null);
5973
+ setPaired(null);
5974
+ setShowManual(false);
5975
+ const look = () => void probeTerminal().then((found) => {
5976
+ if (cancelled) return;
5977
+ setProbe(found);
5978
+ setPhase((current) => current === "pairing" || current === "paired" ? current : found ? "found" : "missing");
5979
+ });
5980
+ look();
5981
+ const timer = window.setInterval(look, 2e3);
5982
+ return () => {
5983
+ cancelled = true;
5984
+ window.clearInterval(timer);
5985
+ };
5986
+ }, [terminalConnectOpen]);
5987
+ if (!terminalConnectOpen) return null;
5988
+ const close = () => dispatch({ type: "SET_TERMINAL_CONNECT", open: false });
5989
+ const copy = async (value, what) => {
5990
+ try {
5991
+ await navigator.clipboard.writeText(value);
5992
+ toast(dispatch, `${what} copied`, "success");
5993
+ } catch {
5994
+ toast(dispatch, "Copy failed \u2014 select the text manually", "error");
5995
+ }
5996
+ };
5997
+ const connect = () => {
5998
+ if (!probe || !key) return;
5999
+ setPhase("pairing");
6000
+ void pairTerminal(probe.port, key).then((result) => {
6001
+ if (result.ok) {
6002
+ setPaired({ name: result.name, repo: result.repo });
6003
+ setPhase("paired");
6004
+ toast(dispatch, `Terminal paired as \u201C${result.name}\u201D`, "success");
6005
+ } else {
6006
+ setPhase("found");
6007
+ toast(dispatch, `Pairing failed: ${result.error}`, "error");
6008
+ }
6009
+ });
6010
+ };
6011
+ const connectCmd = key ? `npx -y @satanwagen/reviewkit-cli connect ${key}` : "";
6012
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6013
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-backdrop", onClick: close }),
6014
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-modal", role: "dialog", "aria-modal": "true", "aria-label": "Connect terminal or agent", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-body", children: [
6015
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
6016
+ "Connect terminal / agent",
6017
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { flex: 1 } }),
6018
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-icon-btn", "aria-label": "Close", onClick: close, autoFocus: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconClose, { size: 13 }) })
6019
+ ] }),
6020
+ !key && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
6021
+ "Live sync is off on this page (",
6022
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "sync=false" }),
6023
+ " or no token), so there is no shared room a terminal could read."
6024
+ ] }),
6025
+ key && phase === "paired" && paired && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
6026
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-emblema-dot", "data-state": "live" }),
6027
+ " Paired as ",
6028
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: paired.name }),
6029
+ paired.repo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6030
+ " in ",
6031
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: shortPath(paired.repo) })
6032
+ ] }) : null,
6033
+ ". The terminal shows this page's comments live, and Claude Code / Cursor can read and resolve them through the ",
6034
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "reviewkit" }),
6035
+ " MCP tools."
6036
+ ] }),
6037
+ key && phase !== "paired" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6038
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-text", children: [
6039
+ phase === "probing" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6040
+ "Looking for a ",
6041
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "reviewkit" }),
6042
+ " terminal on this machine\u2026"
6043
+ ] }),
6044
+ phase === "missing" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6045
+ "No terminal is listening yet. In the site's repo run",
6046
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { className: "rk-connect-cmd", children: "npx -y @satanwagen/reviewkit-cli setup" }),
6047
+ "and this dialog will pick it up by itself."
6048
+ ] }),
6049
+ (phase === "found" || phase === "pairing") && probe && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6050
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-emblema-dot", "data-state": "live" }),
6051
+ " Terminal found",
6052
+ probe.cwd ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6053
+ " in ",
6054
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: shortPath(probe.cwd) })
6055
+ ] }) : null,
6056
+ ".",
6057
+ probe.projects.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6058
+ " Already paired: ",
6059
+ probe.projects.join(", "),
6060
+ "."
6061
+ ] }) : null
6062
+ ] })
6063
+ ] }),
6064
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-connect-actions", children: [
6065
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6066
+ "button",
6067
+ {
6068
+ type: "button",
6069
+ className: "rk-btn rk-btn-primary",
6070
+ disabled: phase !== "found",
6071
+ onClick: connect,
6072
+ title: "Send this site's connection to the terminal \u2014 nothing to copy",
6073
+ children: phase === "pairing" ? "Connecting\u2026" : "Connect this terminal"
6074
+ }
6075
+ ),
6076
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => setShowManual((v) => !v), children: showManual ? "Hide manual option" : "Another machine\u2026" })
6077
+ ] }),
6078
+ showManual && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
6079
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-modal-text", style: { marginTop: 10 }, children: "On a machine without a listening terminal, run this once (the key carries the sync server and its token \u2014 treat it like a review link):" }),
6080
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("code", { className: "rk-connect-cmd", title: connectCmd, children: [
6081
+ "npx -y @satanwagen/reviewkit-cli connect ",
6082
+ shortKey2(key)
6083
+ ] }),
6084
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-connect-actions", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void copy(connectCmd, "Command"), children: "Copy command" }) })
6085
+ ] })
6086
+ ] })
6087
+ ] }) })
6088
+ ] });
6089
+ }
6090
+ var import_react7, import_jsx_runtime6;
6091
+ var init_TerminalConnect = __esm({
6092
+ "src/client/components/TerminalConnect.tsx"() {
6093
+ "use strict";
6094
+ import_react7 = require("react");
6095
+ init_terminal();
6096
+ init_store();
6097
+ init_terminalKey();
6098
+ init_sync();
6099
+ init_icons();
6100
+ import_jsx_runtime6 = require("react/jsx-runtime");
6101
+ }
6102
+ });
6103
+
5821
6104
  // src/client/components/CoachMark.tsx
5822
6105
  function CoachMark() {
5823
6106
  const { coachOpen, session } = useStore();
5824
6107
  const dispatch = useDispatch();
5825
6108
  const config = useConfig();
5826
- const [name, setName] = (0, import_react7.useState)(session?.author ?? config.defaultAuthor ?? "");
6109
+ const [name, setName] = (0, import_react8.useState)(session?.author ?? config.defaultAuthor ?? "");
5827
6110
  if (!coachOpen) return null;
5828
6111
  const done = (startPicking) => {
5829
6112
  if (name.trim() !== "") dispatch({ type: "SET_AUTHOR", author: name.trim() });
@@ -5834,39 +6117,39 @@ function CoachMark() {
5834
6117
  }
5835
6118
  if (startPicking) dispatch({ type: "SET_MODE", mode: "pick" });
5836
6119
  };
5837
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
5838
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-coach-accent" }),
5839
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-coach-body", children: [
5840
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5841
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconTarget, { size: 16 }),
6120
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
6121
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-coach-accent" }),
6122
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach-body", children: [
6123
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
6124
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconTarget, { size: 16 }),
5842
6125
  " Review mode"
5843
6126
  ] }),
5844
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rk-modal-text", children: "Click anything on the page to suggest a change \u2014 rewrite the copy, swap an image, or leave a note. The live site stays untouched; everything you add is collected for the team." }),
5845
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
5846
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5847
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "T" }),
6127
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-modal-text", children: "Click anything on the page to suggest a change \u2014 rewrite the copy, swap an image, or leave a note. The live site stays untouched; everything you add is collected for the team." }),
6128
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
6129
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
6130
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "T" }),
5848
6131
  " rewrite"
5849
6132
  ] }),
5850
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5851
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "C" }),
6133
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
6134
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "C" }),
5852
6135
  " comment"
5853
6136
  ] }),
5854
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5855
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "I" }),
6137
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
6138
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "I" }),
5856
6139
  " image"
5857
6140
  ] }),
5858
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5859
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "V" }),
6141
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
6142
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "V" }),
5860
6143
  " select"
5861
6144
  ] }),
5862
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
5863
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rk-kbd", children: "?" }),
6145
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
6146
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rk-kbd", children: "?" }),
5864
6147
  " all shortcuts"
5865
6148
  ] })
5866
6149
  ] }),
5867
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
5868
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
5869
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6150
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
6151
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
6152
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5870
6153
  "input",
5871
6154
  {
5872
6155
  id: "rk-author",
@@ -5880,21 +6163,21 @@ function CoachMark() {
5880
6163
  }
5881
6164
  )
5882
6165
  ] }),
5883
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
5884
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
5885
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
6166
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
6167
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
6168
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
5886
6169
  ] })
5887
6170
  ] })
5888
6171
  ] });
5889
6172
  }
5890
- var import_react7, import_jsx_runtime6, COACH_KEY;
6173
+ var import_react8, import_jsx_runtime7, COACH_KEY;
5891
6174
  var init_CoachMark = __esm({
5892
6175
  "src/client/components/CoachMark.tsx"() {
5893
6176
  "use strict";
5894
- import_react7 = require("react");
6177
+ import_react8 = require("react");
5895
6178
  init_store();
5896
6179
  init_icons();
5897
- import_jsx_runtime6 = require("react/jsx-runtime");
6180
+ import_jsx_runtime7 = require("react/jsx-runtime");
5898
6181
  COACH_KEY = "review-kit:coach-dismissed";
5899
6182
  }
5900
6183
  });
@@ -5903,22 +6186,22 @@ var init_CoachMark = __esm({
5903
6186
  function ConfirmHost() {
5904
6187
  const { confirm } = useStore();
5905
6188
  const dispatch = useDispatch();
5906
- const cancelRef = (0, import_react8.useRef)(null);
5907
- (0, import_react8.useEffect)(() => {
6189
+ const cancelRef = (0, import_react9.useRef)(null);
6190
+ (0, import_react9.useEffect)(() => {
5908
6191
  if (confirm) cancelRef.current?.focus();
5909
6192
  }, [confirm]);
5910
6193
  if (!confirm) return null;
5911
6194
  const close = () => dispatch({ type: "SET_CONFIRM", confirm: null });
5912
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
5913
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-backdrop", onClick: close }),
5914
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
5915
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal-body", children: [
5916
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-modal-title", children: confirm.title }),
5917
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rk-modal-text", children: confirm.message })
6195
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
6196
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-backdrop", onClick: close }),
6197
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
6198
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal-body", children: [
6199
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-modal-title", children: confirm.title }),
6200
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-modal-text", children: confirm.message })
5918
6201
  ] }),
5919
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rk-modal-actions", children: [
5920
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
5921
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6202
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-modal-actions", children: [
6203
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
6204
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5922
6205
  "button",
5923
6206
  {
5924
6207
  type: "button",
@@ -5935,13 +6218,13 @@ function ConfirmHost() {
5935
6218
  ] })
5936
6219
  ] });
5937
6220
  }
5938
- var import_react8, import_jsx_runtime7;
6221
+ var import_react9, import_jsx_runtime8;
5939
6222
  var init_Confirm = __esm({
5940
6223
  "src/client/components/Confirm.tsx"() {
5941
6224
  "use strict";
5942
- import_react8 = require("react");
6225
+ import_react9 = require("react");
5943
6226
  init_store();
5944
- import_jsx_runtime7 = require("react/jsx-runtime");
6227
+ import_jsx_runtime8 = require("react/jsx-runtime");
5945
6228
  }
5946
6229
  });
5947
6230
 
@@ -5994,9 +6277,9 @@ var init_position = __esm({
5994
6277
 
5995
6278
  // src/client/useAnchored.ts
5996
6279
  function useAnchored(anchorEl, side = "bottom", offset = 8) {
5997
- const ref = (0, import_react9.useRef)(null);
5998
- const [pos, setPos] = (0, import_react9.useState)(null);
5999
- (0, import_react9.useLayoutEffect)(() => {
6280
+ const ref = (0, import_react10.useRef)(null);
6281
+ const [pos, setPos] = (0, import_react10.useState)(null);
6282
+ (0, import_react10.useLayoutEffect)(() => {
6000
6283
  if (!anchorEl) {
6001
6284
  setPos(null);
6002
6285
  return;
@@ -6025,11 +6308,11 @@ function anchoredStyle(pos) {
6025
6308
  if (!pos) return { visibility: "hidden", transform: "translate3d(-9999px, -9999px, 0)" };
6026
6309
  return { transform: `translate3d(${pos.x}px, ${pos.y}px, 0)`, top: 0, left: 0 };
6027
6310
  }
6028
- var import_react9;
6311
+ var import_react10;
6029
6312
  var init_useAnchored = __esm({
6030
6313
  "src/client/useAnchored.ts"() {
6031
6314
  "use strict";
6032
- import_react9 = require("react");
6315
+ import_react10 = require("react");
6033
6316
  init_position();
6034
6317
  }
6035
6318
  });
@@ -6089,15 +6372,15 @@ function urlFromDataTransfer(dt) {
6089
6372
  function ImageEditor({ editor }) {
6090
6373
  const dispatch = useDispatch();
6091
6374
  const { route, session } = useStore();
6092
- const [replacement, setReplacement] = (0, import_react10.useState)(null);
6093
- const [urlValue, setUrlValue] = (0, import_react10.useState)("");
6094
- const [loading2, setLoading] = (0, import_react10.useState)(false);
6095
- const [description, setDescription] = (0, import_react10.useState)("");
6096
- const [priority, setPriority] = (0, import_react10.useState)("nice");
6097
- const [over, setOver] = (0, import_react10.useState)(false);
6098
- const fileInput = (0, import_react10.useRef)(null);
6099
- const abortRef = (0, import_react10.useRef)(null);
6100
- const debounceRef = (0, import_react10.useRef)(null);
6375
+ const [replacement, setReplacement] = (0, import_react11.useState)(null);
6376
+ const [urlValue, setUrlValue] = (0, import_react11.useState)("");
6377
+ const [loading2, setLoading] = (0, import_react11.useState)(false);
6378
+ const [description, setDescription] = (0, import_react11.useState)("");
6379
+ const [priority, setPriority] = (0, import_react11.useState)("nice");
6380
+ const [over, setOver] = (0, import_react11.useState)(false);
6381
+ const fileInput = (0, import_react11.useRef)(null);
6382
+ const abortRef = (0, import_react11.useRef)(null);
6383
+ const debounceRef = (0, import_react11.useRef)(null);
6101
6384
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
6102
6385
  const image = findImage(editor.element);
6103
6386
  const beforeSrc = image?.currentSrc || image?.src || "";
@@ -6161,7 +6444,7 @@ function ImageEditor({ editor }) {
6161
6444
  }
6162
6445
  }
6163
6446
  };
6164
- (0, import_react10.useEffect)(() => {
6447
+ (0, import_react11.useEffect)(() => {
6165
6448
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
6166
6449
  if (urlValue.trim() === "" || replacement !== null) return;
6167
6450
  debounceRef.current = window.setTimeout(() => {
@@ -6171,8 +6454,8 @@ function ImageEditor({ editor }) {
6171
6454
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
6172
6455
  };
6173
6456
  }, [urlValue, replacement]);
6174
- (0, import_react10.useEffect)(() => () => abortRef.current?.abort(), []);
6175
- (0, import_react10.useEffect)(() => {
6457
+ (0, import_react11.useEffect)(() => () => abortRef.current?.abort(), []);
6458
+ (0, import_react11.useEffect)(() => {
6176
6459
  const onPaste = (event) => {
6177
6460
  const data = event.clipboardData;
6178
6461
  if (!data) return;
@@ -6250,23 +6533,23 @@ function ImageEditor({ editor }) {
6250
6533
  toast(dispatch, "Image proposal saved", "success");
6251
6534
  };
6252
6535
  const previewSrc = replacement?.kind === "data" ? replacement.dataUrl : replacement?.url;
6253
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
6254
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-card-head", children: [
6255
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconImage, { size: 14 }),
6536
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
6537
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-head", children: [
6538
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconImage, { size: 14 }),
6256
6539
  "Replace image"
6257
6540
  ] }),
6258
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-card-body", children: [
6259
- replacement && previewSrc ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rk-thumbs", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-thumb-pair", title: "Hover to compare with the current image", children: [
6260
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("figure", { className: "rk-thumb", children: [
6261
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: beforeSrc, alt: "Current" }),
6262
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("figcaption", { children: "Before" })
6541
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-body", children: [
6542
+ replacement && previewSrc ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "rk-thumbs", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-thumb-pair", title: "Hover to compare with the current image", children: [
6543
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("figure", { className: "rk-thumb", children: [
6544
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: beforeSrc, alt: "Current" }),
6545
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("figcaption", { children: "Before" })
6263
6546
  ] }),
6264
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("figure", { className: "rk-thumb", children: [
6265
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: previewSrc, alt: "Proposed replacement" }),
6266
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
6547
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("figure", { className: "rk-thumb", children: [
6548
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: previewSrc, alt: "Proposed replacement" }),
6549
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
6267
6550
  ] })
6268
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
6269
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
6551
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
6552
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
6270
6553
  "button",
6271
6554
  {
6272
6555
  type: "button",
@@ -6279,18 +6562,18 @@ function ImageEditor({ editor }) {
6279
6562
  onDragLeave: () => setOver(false),
6280
6563
  onDrop,
6281
6564
  children: [
6282
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconUpload, { size: 18 }),
6283
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
6565
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconUpload, { size: 18 }),
6566
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6284
6567
  "Drop an image \u2014 from your disk or another tab \u2014",
6285
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
6568
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("br", {}),
6286
6569
  "or click to browse (max 2 MB)"
6287
6570
  ] })
6288
6571
  ]
6289
6572
  }
6290
6573
  ),
6291
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-url", children: [
6292
- loading2 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconLink, { size: 13 }),
6293
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
6574
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-url", children: [
6575
+ loading2 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconLink, { size: 13 }),
6576
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6294
6577
  "input",
6295
6578
  {
6296
6579
  className: "rk-input",
@@ -6305,9 +6588,9 @@ function ImageEditor({ editor }) {
6305
6588
  )
6306
6589
  ] })
6307
6590
  ] }),
6308
- replacement && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
6309
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
6310
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
6591
+ replacement && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
6592
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
6593
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6311
6594
  "textarea",
6312
6595
  {
6313
6596
  className: "rk-textarea",
@@ -6317,35 +6600,35 @@ function ImageEditor({ editor }) {
6317
6600
  rows: 2
6318
6601
  }
6319
6602
  ),
6320
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6321
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6322
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6603
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6604
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6605
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6323
6606
  ] })
6324
6607
  ] }),
6325
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-card-foot", children: [
6326
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "rk-hint", children: [
6327
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "rk-kbd", children: "esc" }),
6608
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-foot", children: [
6609
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "rk-hint", children: [
6610
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "esc" }),
6328
6611
  " cancel"
6329
6612
  ] }),
6330
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rk-actions", children: [
6331
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6332
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6613
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-actions", children: [
6614
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6615
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6333
6616
  ] })
6334
6617
  ] })
6335
6618
  ] });
6336
6619
  }
6337
- var import_react10, import_jsx_runtime8, MAX_BYTES, FETCH_TIMEOUT_MS, PROBE_TIMEOUT_MS;
6620
+ var import_react11, import_jsx_runtime9, MAX_BYTES, FETCH_TIMEOUT_MS, PROBE_TIMEOUT_MS;
6338
6621
  var init_ImageEditor = __esm({
6339
6622
  "src/client/components/ImageEditor.tsx"() {
6340
6623
  "use strict";
6341
- import_react10 = require("react");
6624
+ import_react11 = require("react");
6342
6625
  init_store();
6343
6626
  init_id();
6344
6627
  init_identity();
6345
6628
  init_target();
6346
6629
  init_useAnchored();
6347
6630
  init_icons();
6348
- import_jsx_runtime8 = require("react/jsx-runtime");
6631
+ import_jsx_runtime9 = require("react/jsx-runtime");
6349
6632
  MAX_BYTES = 2 * 1024 * 1024;
6350
6633
  FETCH_TIMEOUT_MS = 12e3;
6351
6634
  PROBE_TIMEOUT_MS = 8e3;
@@ -6356,19 +6639,19 @@ var init_ImageEditor = __esm({
6356
6639
  function InlineEditor({ editor }) {
6357
6640
  const dispatch = useDispatch();
6358
6641
  const { route, session } = useStore();
6359
- const [after, setAfter] = (0, import_react11.useState)(() => editor.element.innerText);
6360
- const [priority, setPriority] = (0, import_react11.useState)("nice");
6361
- const [note, setNote] = (0, import_react11.useState)("");
6362
- const original = (0, import_react11.useRef)(null);
6363
- const baseline = (0, import_react11.useRef)(null);
6364
- const [sizeDelta, setSizeDelta] = (0, import_react11.useState)(0);
6365
- const [widthDelta, setWidthDelta] = (0, import_react11.useState)(0);
6366
- const [clipped, setClipped] = (0, import_react11.useState)(false);
6367
- const ringRef = (0, import_react11.useRef)(null);
6368
- const afterRef = (0, import_react11.useRef)(null);
6642
+ const [after, setAfter] = (0, import_react12.useState)(() => editor.element.innerText);
6643
+ const [priority, setPriority] = (0, import_react12.useState)("nice");
6644
+ const [note, setNote] = (0, import_react12.useState)("");
6645
+ const original = (0, import_react12.useRef)(null);
6646
+ const baseline = (0, import_react12.useRef)(null);
6647
+ const [sizeDelta, setSizeDelta] = (0, import_react12.useState)(0);
6648
+ const [widthDelta, setWidthDelta] = (0, import_react12.useState)(0);
6649
+ const [clipped, setClipped] = (0, import_react12.useState)(false);
6650
+ const ringRef = (0, import_react12.useRef)(null);
6651
+ const afterRef = (0, import_react12.useRef)(null);
6369
6652
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
6370
6653
  const element = editor.element;
6371
- (0, import_react11.useEffect)(() => {
6654
+ (0, import_react12.useEffect)(() => {
6372
6655
  const el = element;
6373
6656
  original.current = { html: el.innerHTML, text: el.innerText, styleAttr: el.getAttribute("style") };
6374
6657
  const rect = el.getBoundingClientRect();
@@ -6398,8 +6681,8 @@ function InlineEditor({ editor }) {
6398
6681
  }
6399
6682
  };
6400
6683
  }, [element]);
6401
- const focusedOnce = (0, import_react11.useRef)(false);
6402
- (0, import_react11.useEffect)(() => {
6684
+ const focusedOnce = (0, import_react12.useRef)(false);
6685
+ (0, import_react12.useEffect)(() => {
6403
6686
  const field = afterRef.current;
6404
6687
  if (pos === null || focusedOnce.current || !field) return;
6405
6688
  focusedOnce.current = true;
@@ -6407,7 +6690,7 @@ function InlineEditor({ editor }) {
6407
6690
  const end = field.value.length;
6408
6691
  field.setSelectionRange(end, end);
6409
6692
  }, [pos]);
6410
- (0, import_react11.useEffect)(() => {
6693
+ (0, import_react12.useEffect)(() => {
6411
6694
  const field = afterRef.current;
6412
6695
  if (!field) return;
6413
6696
  field.style.height = "auto";
@@ -6417,7 +6700,7 @@ function InlineEditor({ editor }) {
6417
6700
  setAfter(value);
6418
6701
  if (element.innerText !== value) setElementText(element, value);
6419
6702
  };
6420
- (0, import_react11.useEffect)(() => {
6703
+ (0, import_react12.useEffect)(() => {
6421
6704
  let raf = 0;
6422
6705
  let lastDelta = 0;
6423
6706
  let lastWidth = 0;
@@ -6487,7 +6770,7 @@ function InlineEditor({ editor }) {
6487
6770
  cancel();
6488
6771
  }
6489
6772
  };
6490
- (0, import_react11.useEffect)(() => {
6773
+ (0, import_react12.useEffect)(() => {
6491
6774
  const el = element;
6492
6775
  const onKeyDown = (event) => {
6493
6776
  if (event.key === "Enter" && !event.shiftKey) {
@@ -6505,19 +6788,19 @@ function InlineEditor({ editor }) {
6505
6788
  });
6506
6789
  const before = original.current?.text ?? "";
6507
6790
  const delta = after.length - before.length;
6508
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
6509
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: ringRef, className: "rk-edit-ring" }),
6510
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
6511
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-head", children: [
6512
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconText, { size: 14 }),
6791
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
6792
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: ringRef, className: "rk-edit-ring" }),
6793
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
6794
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-head", children: [
6795
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconText, { size: 14 }),
6513
6796
  "Rewrite text",
6514
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-spacer" }),
6515
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "rk-delta", "aria-live": "polite", children: [
6797
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-spacer" }),
6798
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "rk-delta", "aria-live": "polite", children: [
6516
6799
  before.length,
6517
6800
  " \u2192 ",
6518
6801
  after.length,
6519
6802
  " ",
6520
- delta !== 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
6803
+ delta !== 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
6521
6804
  "(",
6522
6805
  delta > 0 ? "+" : "",
6523
6806
  delta,
@@ -6525,15 +6808,15 @@ function InlineEditor({ editor }) {
6525
6808
  ] })
6526
6809
  ] })
6527
6810
  ] }),
6528
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-body", children: [
6529
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-diff", "aria-label": "Before and after", children: [
6530
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6531
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6532
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-diff-text", children: before })
6811
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-body", children: [
6812
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff", "aria-label": "Before and after", children: [
6813
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6814
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6815
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-text", children: before })
6533
6816
  ] }),
6534
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6535
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6536
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6817
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6818
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6819
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6537
6820
  "textarea",
6538
6821
  {
6539
6822
  ref: afterRef,
@@ -6547,11 +6830,11 @@ function InlineEditor({ editor }) {
6547
6830
  )
6548
6831
  ] })
6549
6832
  ] }),
6550
- (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-size-note", role: "status", children: [
6833
+ (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-size-note", role: "status", children: [
6551
6834
  Math.abs(sizeDelta) > 2 && (() => {
6552
6835
  const lines = baseline.current ? Math.round(Math.abs(sizeDelta) / baseline.current.lineHeight) : 0;
6553
6836
  const lineNote = lines >= 1 ? ` (~${lines} line${lines === 1 ? "" : "s"})` : "";
6554
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6837
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
6555
6838
  "Block ",
6556
6839
  sizeDelta > 0 ? "grew" : "shrank",
6557
6840
  " by ",
@@ -6561,16 +6844,16 @@ function InlineEditor({ editor }) {
6561
6844
  " \u2014 as it would with this text live."
6562
6845
  ] });
6563
6846
  })(),
6564
- Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
6847
+ Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
6565
6848
  "Block got ",
6566
6849
  widthDelta > 0 ? "wider" : "narrower",
6567
6850
  " by ",
6568
6851
  Math.abs(widthDelta),
6569
6852
  " px \u2014 as it would with this text live."
6570
6853
  ] }),
6571
- clipped && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
6854
+ clipped && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
6572
6855
  ] }),
6573
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6856
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6574
6857
  "input",
6575
6858
  {
6576
6859
  className: "rk-input",
@@ -6579,41 +6862,41 @@ function InlineEditor({ editor }) {
6579
6862
  onChange: (event) => setNote(event.target.value)
6580
6863
  }
6581
6864
  ),
6582
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6583
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6584
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6865
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6866
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6867
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6585
6868
  ] })
6586
6869
  ] }),
6587
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-card-foot", children: [
6588
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "rk-hint", children: [
6589
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
6870
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-foot", children: [
6871
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "rk-hint", children: [
6872
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
6590
6873
  " save \xB7 ",
6591
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
6874
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
6592
6875
  " newline \xB7",
6593
6876
  " ",
6594
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "rk-kbd", children: "esc" }),
6877
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "esc" }),
6595
6878
  " cancel"
6596
6879
  ] }),
6597
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "rk-actions", children: [
6598
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
6599
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
6880
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-actions", children: [
6881
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
6882
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
6600
6883
  ] })
6601
6884
  ] })
6602
6885
  ] })
6603
6886
  ] });
6604
6887
  }
6605
- var import_react11, import_jsx_runtime9;
6888
+ var import_react12, import_jsx_runtime10;
6606
6889
  var init_InlineEditor = __esm({
6607
6890
  "src/client/components/InlineEditor.tsx"() {
6608
6891
  "use strict";
6609
- import_react11 = require("react");
6892
+ import_react12 = require("react");
6610
6893
  init_store();
6611
6894
  init_id();
6612
6895
  init_identity();
6613
6896
  init_preview();
6614
6897
  init_useAnchored();
6615
6898
  init_icons();
6616
- import_jsx_runtime9 = require("react/jsx-runtime");
6899
+ import_jsx_runtime10 = require("react/jsx-runtime");
6617
6900
  }
6618
6901
  });
6619
6902
 
@@ -6621,14 +6904,14 @@ var init_InlineEditor = __esm({
6621
6904
  function NoteComposer({ editor }) {
6622
6905
  const dispatch = useDispatch();
6623
6906
  const { route, session } = useStore();
6624
- const [text, setText] = (0, import_react12.useState)("");
6625
- const [direction, setDirection] = (0, import_react12.useState)(void 0);
6626
- const [priority, setPriority] = (0, import_react12.useState)("nice");
6627
- const textareaRef = (0, import_react12.useRef)(null);
6907
+ const [text, setText] = (0, import_react13.useState)("");
6908
+ const [direction, setDirection] = (0, import_react13.useState)(void 0);
6909
+ const [priority, setPriority] = (0, import_react13.useState)("nice");
6910
+ const textareaRef = (0, import_react13.useRef)(null);
6628
6911
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
6629
6912
  const { kind } = editor;
6630
- const focusedOnce = (0, import_react12.useRef)(false);
6631
- (0, import_react12.useEffect)(() => {
6913
+ const focusedOnce = (0, import_react13.useRef)(false);
6914
+ (0, import_react13.useEffect)(() => {
6632
6915
  const field = textareaRef.current;
6633
6916
  if (pos === null || focusedOnce.current || !field) return;
6634
6917
  focusedOnce.current = true;
@@ -6687,13 +6970,13 @@ function NoteComposer({ editor }) {
6687
6970
  save();
6688
6971
  }
6689
6972
  };
6690
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
6691
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-head", children: [
6973
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
6974
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-head", children: [
6692
6975
  iconForType(kind),
6693
6976
  TITLES[kind]
6694
6977
  ] }),
6695
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-body", children: [
6696
- kind === "move" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6978
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-body", children: [
6979
+ kind === "move" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6697
6980
  "button",
6698
6981
  {
6699
6982
  type: "button",
@@ -6703,7 +6986,7 @@ function NoteComposer({ editor }) {
6703
6986
  },
6704
6987
  option
6705
6988
  )) }),
6706
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6989
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6707
6990
  "textarea",
6708
6991
  {
6709
6992
  ref: textareaRef,
@@ -6715,36 +6998,36 @@ function NoteComposer({ editor }) {
6715
6998
  rows: 3
6716
6999
  }
6717
7000
  ),
6718
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6719
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6720
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
7001
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
7002
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
7003
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6721
7004
  ] })
6722
7005
  ] }),
6723
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-card-foot", children: [
6724
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "rk-hint", children: [
6725
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
7006
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-card-foot", children: [
7007
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-hint", children: [
7008
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
6726
7009
  " save \xB7 ",
6727
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "rk-kbd", children: "esc" }),
7010
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-kbd", children: "esc" }),
6728
7011
  " cancel"
6729
7012
  ] }),
6730
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "rk-actions", children: [
6731
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6732
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
7013
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-actions", children: [
7014
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
7015
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6733
7016
  ] })
6734
7017
  ] })
6735
7018
  ] });
6736
7019
  }
6737
- var import_react12, import_jsx_runtime10, TITLES, PLACEHOLDERS;
7020
+ var import_react13, import_jsx_runtime11, TITLES, PLACEHOLDERS;
6738
7021
  var init_NoteComposer = __esm({
6739
7022
  "src/client/components/NoteComposer.tsx"() {
6740
7023
  "use strict";
6741
- import_react12 = require("react");
7024
+ import_react13 = require("react");
6742
7025
  init_store();
6743
7026
  init_id();
6744
7027
  init_identity();
6745
7028
  init_useAnchored();
6746
7029
  init_icons();
6747
- import_jsx_runtime10 = require("react/jsx-runtime");
7030
+ import_jsx_runtime11 = require("react/jsx-runtime");
6748
7031
  TITLES = {
6749
7032
  comment: "Comment",
6750
7033
  delete: "Mark for deletion",
@@ -6838,24 +7121,24 @@ function formatBytes(bytes) {
6838
7121
  function CopyDiff({ before, after }) {
6839
7122
  const parts = diffParts(before, after);
6840
7123
  const delta = after.length - before.length;
6841
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diffview", children: [
6842
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
6843
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
6844
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-diff-text", children: [
7124
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diffview", children: [
7125
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-row rk-diff-before", children: [
7126
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-diff-tag", children: "Before" }),
7127
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-diff-text", children: [
6845
7128
  parts.prefix,
6846
- parts.delMid && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("mark", { className: "rk-del", children: parts.delMid }),
7129
+ parts.delMid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("mark", { className: "rk-del", children: parts.delMid }),
6847
7130
  parts.suffix
6848
7131
  ] })
6849
7132
  ] }),
6850
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
6851
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-diff-tag", children: "After" }),
6852
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-diff-text", children: [
7133
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-row rk-diff-after", children: [
7134
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-diff-tag", children: "After" }),
7135
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-diff-text", children: [
6853
7136
  parts.prefix,
6854
- parts.insMid && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("mark", { className: "rk-ins", children: parts.insMid }),
7137
+ parts.insMid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("mark", { className: "rk-ins", children: parts.insMid }),
6855
7138
  parts.suffix
6856
7139
  ] })
6857
7140
  ] }),
6858
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-diff-len", children: [
7141
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-diff-len", children: [
6859
7142
  before.length,
6860
7143
  " \u2192 ",
6861
7144
  after.length,
@@ -6866,8 +7149,8 @@ function CopyDiff({ before, after }) {
6866
7149
  }
6867
7150
  function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
6868
7151
  const replacement = afterDataUrl ?? afterSrc;
6869
- const [dims, setDims] = (0, import_react13.useState)("");
6870
- (0, import_react13.useEffect)(() => {
7152
+ const [dims, setDims] = (0, import_react14.useState)("");
7153
+ (0, import_react14.useEffect)(() => {
6871
7154
  if (!replacement) return;
6872
7155
  const img = new Image();
6873
7156
  img.onload = () => setDims(`${img.naturalWidth}\xD7${img.naturalHeight}px`);
@@ -6892,55 +7175,55 @@ function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
6892
7175
  window.open(replacement, "_blank", "noopener");
6893
7176
  }
6894
7177
  };
6895
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-imgdetail", children: [
6896
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-thumbs", children: [
6897
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "rk-thumb", children: [
6898
- beforeSrc ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-thumb-missing", children: "no image" }),
6899
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { children: "Before" })
7178
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail", children: [
7179
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-thumbs", children: [
7180
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "rk-thumb", children: [
7181
+ beforeSrc ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-thumb-missing", children: "no image" }),
7182
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { children: "Before" })
6900
7183
  ] }),
6901
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "rk-thumb", children: [
6902
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: replacement, alt: "Proposed replacement" }),
6903
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figcaption", { children: [
7184
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "rk-thumb", children: [
7185
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: replacement, alt: "Proposed replacement" }),
7186
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figcaption", { children: [
6904
7187
  "After",
6905
7188
  embedded ? "" : " \xB7 linked"
6906
7189
  ] })
6907
7190
  ] })
6908
7191
  ] }),
6909
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-imgdetail-meta", children: [
7192
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail-meta", children: [
6910
7193
  embedded ? "Embedded image" : "Linked image",
6911
7194
  dims ? ` \xB7 ${dims}` : "",
6912
7195
  bytes !== null ? ` \xB7 ${formatBytes(bytes)}` : ""
6913
7196
  ] }),
6914
- !embedded && afterSrc && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
6915
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-imgdetail-actions", children: [
6916
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
6917
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconDownload, { size: 12 }),
7197
+ !embedded && afterSrc && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
7198
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-imgdetail-actions", children: [
7199
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
7200
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconDownload, { size: 12 }),
6918
7201
  " Download"
6919
7202
  ] }),
6920
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
7203
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
6921
7204
  ] })
6922
7205
  ] });
6923
7206
  }
6924
7207
  function Panel() {
6925
- const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, emblemaPush, peers } = useStore();
7208
+ const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, syncReason, emblemaPush, peers } = useStore();
6926
7209
  const dispatch = useDispatch();
6927
7210
  const config = useConfig();
6928
- const [sticky, setStickyState] = (0, import_react13.useState)(() => isStickyEnabled(config.token));
6929
- const [query, setQuery] = (0, import_react13.useState)("");
6930
- const [typeFilter, setTypeFilter] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
6931
- const [prioFilter, setPrioFilter] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
6932
- const [statusFilter, setStatusFilter] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
6933
- const [filterOpen, setFilterOpen] = (0, import_react13.useState)(false);
6934
- const [settingsOpen, setSettingsOpen] = (0, import_react13.useState)(false);
6935
- const [emblemaAlive, setEmblemaAlive] = (0, import_react13.useState)(null);
6936
- const [emblemaSending, setEmblemaSending] = (0, import_react13.useState)(false);
6937
- const [emblemaSync, setEmblemaSync] = (0, import_react13.useState)("unknown");
6938
- const panelRef = (0, import_react13.useRef)(null);
6939
- const searchRef = (0, import_react13.useRef)(null);
6940
- const importRef = (0, import_react13.useRef)(null);
6941
- const sheetDrag = (0, import_react13.useRef)(null);
6942
- const items = (0, import_react13.useMemo)(() => session?.items ?? [], [session]);
6943
- const { orphanIds, hiddenIds } = (0, import_react13.useMemo)(() => {
7211
+ const [sticky, setStickyState] = (0, import_react14.useState)(() => isStickyEnabled(config.token));
7212
+ const [query, setQuery] = (0, import_react14.useState)("");
7213
+ const [typeFilter, setTypeFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
7214
+ const [prioFilter, setPrioFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
7215
+ const [statusFilter, setStatusFilter] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
7216
+ const [filterOpen, setFilterOpen] = (0, import_react14.useState)(false);
7217
+ const [settingsOpen, setSettingsOpen] = (0, import_react14.useState)(false);
7218
+ const [emblemaAlive, setEmblemaAlive] = (0, import_react14.useState)(null);
7219
+ const [emblemaSending, setEmblemaSending] = (0, import_react14.useState)(false);
7220
+ const [emblemaSync, setEmblemaSync] = (0, import_react14.useState)("unknown");
7221
+ const panelRef = (0, import_react14.useRef)(null);
7222
+ const searchRef = (0, import_react14.useRef)(null);
7223
+ const importRef = (0, import_react14.useRef)(null);
7224
+ const sheetDrag = (0, import_react14.useRef)(null);
7225
+ const items = (0, import_react14.useMemo)(() => session?.items ?? [], [session]);
7226
+ const { orphanIds, hiddenIds } = (0, import_react14.useMemo)(() => {
6944
7227
  const orphans = /* @__PURE__ */ new Set();
6945
7228
  const hiddens = /* @__PURE__ */ new Set();
6946
7229
  for (const item of items) {
@@ -6951,15 +7234,15 @@ function Panel() {
6951
7234
  }
6952
7235
  return { orphanIds: orphans, hiddenIds: hiddens };
6953
7236
  }, [items, panelOpen]);
6954
- const activeFilter = (0, import_react13.useMemo)(
7237
+ const activeFilter = (0, import_react14.useMemo)(
6955
7238
  () => typeFilter.size + prioFilter.size + statusFilter.size > 0 ? { types: [...typeFilter], priorities: [...prioFilter], statuses: [...statusFilter] } : null,
6956
7239
  [typeFilter, prioFilter, statusFilter]
6957
7240
  );
6958
- (0, import_react13.useEffect)(() => {
7241
+ (0, import_react14.useEffect)(() => {
6959
7242
  dispatch({ type: "SET_FILTER", filter: activeFilter });
6960
7243
  return () => dispatch({ type: "SET_FILTER", filter: null });
6961
7244
  }, [activeFilter, dispatch]);
6962
- const filtered = (0, import_react13.useMemo)(() => {
7245
+ const filtered = (0, import_react14.useMemo)(() => {
6963
7246
  const q = query.trim().toLowerCase();
6964
7247
  return items.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => {
6965
7248
  if (!itemMatchesFilter(item, activeFilter)) return false;
@@ -6968,22 +7251,22 @@ function Panel() {
6968
7251
  return haystack.includes(q);
6969
7252
  });
6970
7253
  }, [items, query, activeFilter]);
6971
- const typeCounts = (0, import_react13.useMemo)(() => {
7254
+ const typeCounts = (0, import_react14.useMemo)(() => {
6972
7255
  const counts = /* @__PURE__ */ new Map();
6973
7256
  for (const item of items) counts.set(item.type, (counts.get(item.type) ?? 0) + 1);
6974
7257
  return counts;
6975
7258
  }, [items]);
6976
- const prioCounts = (0, import_react13.useMemo)(() => {
7259
+ const prioCounts = (0, import_react14.useMemo)(() => {
6977
7260
  const counts = /* @__PURE__ */ new Map();
6978
7261
  for (const item of items) counts.set(item.priority, (counts.get(item.priority) ?? 0) + 1);
6979
7262
  return counts;
6980
7263
  }, [items]);
6981
- const statusCounts = (0, import_react13.useMemo)(() => {
7264
+ const statusCounts = (0, import_react14.useMemo)(() => {
6982
7265
  const counts = /* @__PURE__ */ new Map();
6983
7266
  for (const item of items) counts.set(item.status, (counts.get(item.status) ?? 0) + 1);
6984
7267
  return counts;
6985
7268
  }, [items]);
6986
- const groups = (0, import_react13.useMemo)(() => {
7269
+ const groups = (0, import_react14.useMemo)(() => {
6987
7270
  const map = /* @__PURE__ */ new Map();
6988
7271
  for (const entry of filtered) {
6989
7272
  const list = map.get(entry.item.route) ?? [];
@@ -6992,7 +7275,7 @@ function Panel() {
6992
7275
  }
6993
7276
  return Array.from(map.entries());
6994
7277
  }, [filtered]);
6995
- (0, import_react13.useEffect)(() => {
7278
+ (0, import_react14.useEffect)(() => {
6996
7279
  if (!filterOpen && !settingsOpen) return;
6997
7280
  const onDown = (event) => {
6998
7281
  const path = event.composedPath();
@@ -7003,7 +7286,7 @@ function Panel() {
7003
7286
  document.addEventListener("pointerdown", onDown, true);
7004
7287
  return () => document.removeEventListener("pointerdown", onDown, true);
7005
7288
  }, [filterOpen, settingsOpen]);
7006
- (0, import_react13.useEffect)(() => {
7289
+ (0, import_react14.useEffect)(() => {
7007
7290
  if (!settingsOpen) return;
7008
7291
  let alive = true;
7009
7292
  const probe = () => {
@@ -7027,7 +7310,7 @@ function Panel() {
7027
7310
  live: "paired",
7028
7311
  off: "not paired"
7029
7312
  }[emblemaPairState];
7030
- (0, import_react13.useEffect)(() => {
7313
+ (0, import_react14.useEffect)(() => {
7031
7314
  if (!panelOpen || activeItemId === null) return;
7032
7315
  const row = panelRef.current?.querySelector(`[data-item-id="${activeItemId}"]`);
7033
7316
  row?.scrollIntoView({ block: "nearest" });
@@ -7143,7 +7426,7 @@ function Panel() {
7143
7426
  panel.style.transform = "";
7144
7427
  if (dy > 110) dispatch({ type: "SET_PANEL", open: false });
7145
7428
  };
7146
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7429
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7147
7430
  "div",
7148
7431
  {
7149
7432
  ref: panelRef,
@@ -7153,7 +7436,7 @@ function Panel() {
7153
7436
  "aria-label": "Review feedback panel",
7154
7437
  onKeyDown,
7155
7438
  children: [
7156
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7439
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7157
7440
  "div",
7158
7441
  {
7159
7442
  className: "rk-sheet-handle",
@@ -7164,34 +7447,41 @@ function Panel() {
7164
7447
  onPointerCancel: onSheetHandleUp
7165
7448
  }
7166
7449
  ),
7167
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-head", children: [
7168
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7169
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-panel-title", children: "Feedback" }),
7170
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-sub", children: [
7450
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-head", children: [
7451
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7452
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-panel-title", children: "Feedback" }),
7453
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-sub", children: [
7171
7454
  items.length,
7172
7455
  " item",
7173
7456
  items.length === 1 ? "" : "s",
7174
7457
  session.author ? ` \xB7 ${session.author}` : "",
7175
- syncStatus !== "disabled" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: `rk-sync rk-sync-${syncStatus}`, children: [
7176
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-sync-dot" }),
7177
- syncStatus === "online" ? `live${peers.length > 0 ? ` \xB7 ${peers.length} other${peers.length === 1 ? "" : "s"}` : ""}` : syncStatus === "connecting" ? "connecting\u2026" : "local only"
7178
- ] }),
7179
- config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7458
+ syncStatus !== "disabled" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7459
+ "span",
7460
+ {
7461
+ className: `rk-sync rk-sync-${syncStatus}${syncReason ? ` rk-sync-reason-${syncReason}` : ""}`,
7462
+ 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"],
7463
+ children: [
7464
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-sync-dot" }),
7465
+ 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"]}`
7466
+ ]
7467
+ }
7468
+ ),
7469
+ config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7180
7470
  "span",
7181
7471
  {
7182
7472
  className: `rk-sync rk-epush-${emblemaPush}`,
7183
7473
  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",
7184
7474
  children: [
7185
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-sync-dot" }),
7475
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-sync-dot" }),
7186
7476
  emblemaPush === "ok" ? "emblema" : emblemaPush === "sending" ? "emblema\u2026" : emblemaPush === "rejected" ? "emblema rejected" : "emblema offline"
7187
7477
  ]
7188
7478
  }
7189
7479
  )
7190
7480
  ] })
7191
7481
  ] }),
7192
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-spacer" }),
7193
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7194
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7482
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-spacer" }),
7483
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7484
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7195
7485
  "button",
7196
7486
  {
7197
7487
  type: "button",
@@ -7207,11 +7497,11 @@ function Panel() {
7207
7497
  void pingEmblema(config.emblema).then((port) => setEmblemaAlive(port !== null));
7208
7498
  }
7209
7499
  },
7210
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconGear, { size: 15 })
7500
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconGear, { size: 15 })
7211
7501
  }
7212
7502
  ),
7213
- settingsOpen && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
7214
- config.token && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7503
+ settingsOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
7504
+ config.token && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7215
7505
  "button",
7216
7506
  {
7217
7507
  type: "button",
@@ -7226,12 +7516,12 @@ function Panel() {
7226
7516
  toast(dispatch, next ? "Review stays on in this tab" : "Review active only via the link", "info");
7227
7517
  },
7228
7518
  children: [
7229
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
7519
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
7230
7520
  "Keep review on in this tab"
7231
7521
  ]
7232
7522
  }
7233
7523
  ),
7234
- config.emblema && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7524
+ config.emblema && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7235
7525
  "button",
7236
7526
  {
7237
7527
  type: "button",
@@ -7261,7 +7551,7 @@ function Panel() {
7261
7551
  }).finally(() => setEmblemaSending(false));
7262
7552
  },
7263
7553
  children: [
7264
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7554
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7265
7555
  "span",
7266
7556
  {
7267
7557
  className: "rk-emblema-dot",
@@ -7272,25 +7562,40 @@ function Panel() {
7272
7562
  ]
7273
7563
  }
7274
7564
  ),
7275
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7565
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7276
7566
  "button",
7277
7567
  {
7278
7568
  type: "button",
7279
7569
  role: "menuitem",
7280
7570
  className: "rk-menu-item",
7281
- 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",
7571
+ 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",
7282
7572
  onClick: () => {
7283
7573
  setSettingsOpen(false);
7284
7574
  dispatch({ type: "SET_EMBLEMA_CONNECT", open: true });
7285
7575
  },
7286
7576
  children: [
7287
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
7577
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
7288
7578
  "Connect to Emblema\u2026",
7289
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: emblemaPairLabel })
7579
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: emblemaPairLabel })
7290
7580
  ]
7291
7581
  }
7292
7582
  ),
7293
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7583
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7584
+ "button",
7585
+ {
7586
+ type: "button",
7587
+ role: "menuitem",
7588
+ className: "rk-menu-item",
7589
+ 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",
7590
+ disabled: !resolveSyncToken(config),
7591
+ onClick: () => {
7592
+ setSettingsOpen(false);
7593
+ dispatch({ type: "SET_TERMINAL_CONNECT", open: true });
7594
+ },
7595
+ children: "Connect terminal / agent\u2026"
7596
+ }
7597
+ ),
7598
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7294
7599
  "button",
7295
7600
  {
7296
7601
  type: "button",
@@ -7303,31 +7608,31 @@ function Panel() {
7303
7608
  )
7304
7609
  ] })
7305
7610
  ] }),
7306
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7611
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7307
7612
  "button",
7308
7613
  {
7309
7614
  type: "button",
7310
7615
  className: "rk-icon-btn",
7311
7616
  "aria-label": "Keyboard shortcuts",
7312
7617
  onClick: () => dispatch({ type: "SET_CHEATSHEET", open: true }),
7313
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconKeyboard, { size: 15 })
7618
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconKeyboard, { size: 15 })
7314
7619
  }
7315
7620
  ),
7316
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7621
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7317
7622
  "button",
7318
7623
  {
7319
7624
  type: "button",
7320
7625
  className: "rk-icon-btn",
7321
7626
  "aria-label": "Close panel",
7322
7627
  onClick: () => dispatch({ type: "SET_PANEL", open: false }),
7323
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconClose, { size: 14 })
7628
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconClose, { size: 14 })
7324
7629
  }
7325
7630
  )
7326
7631
  ] }),
7327
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-view-row", children: [
7328
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-view-info", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-view-title", children: "Page view" }) }),
7329
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
7330
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7632
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-view-row", children: [
7633
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-view-info", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-view-title", children: "Page view" }) }),
7634
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
7635
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7331
7636
  "button",
7332
7637
  {
7333
7638
  type: "button",
@@ -7337,7 +7642,7 @@ function Panel() {
7337
7642
  children: "Original"
7338
7643
  }
7339
7644
  ),
7340
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7645
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7341
7646
  "button",
7342
7647
  {
7343
7648
  type: "button",
@@ -7349,8 +7654,8 @@ function Panel() {
7349
7654
  )
7350
7655
  ] })
7351
7656
  ] }),
7352
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-tools", children: [
7353
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7657
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-tools", children: [
7658
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7354
7659
  "button",
7355
7660
  {
7356
7661
  type: "button",
@@ -7361,15 +7666,15 @@ function Panel() {
7361
7666
  dispatch({ type: "SET_MODE", mode: "pick" });
7362
7667
  },
7363
7668
  children: [
7364
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconTarget, { size: 14 }),
7669
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTarget, { size: 14 }),
7365
7670
  " Pick an element"
7366
7671
  ]
7367
7672
  }
7368
7673
  ),
7369
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-search-row", children: [
7370
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-search", children: [
7371
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconSearch, { size: 13 }),
7372
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7674
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-search-row", children: [
7675
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-search", children: [
7676
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconSearch, { size: 13 }),
7677
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7373
7678
  "input",
7374
7679
  {
7375
7680
  ref: searchRef,
@@ -7381,8 +7686,8 @@ function Panel() {
7381
7686
  }
7382
7687
  )
7383
7688
  ] }),
7384
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7385
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7689
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu-scope rk-menu-anchor", children: [
7690
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7386
7691
  "button",
7387
7692
  {
7388
7693
  type: "button",
@@ -7394,15 +7699,15 @@ function Panel() {
7394
7699
  setSettingsOpen(false);
7395
7700
  },
7396
7701
  children: [
7397
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconFilter, { size: 13 }),
7702
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFilter, { size: 13 }),
7398
7703
  "Filter",
7399
- typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
7704
+ typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
7400
7705
  ]
7401
7706
  }
7402
7707
  ),
7403
- filterOpen && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
7404
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-menu-title", children: "Type" }),
7405
- TYPE_FILTERS.map((type) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7708
+ filterOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
7709
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Type" }),
7710
+ TYPE_FILTERS.map((type) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7406
7711
  "button",
7407
7712
  {
7408
7713
  type: "button",
@@ -7416,16 +7721,16 @@ function Panel() {
7416
7721
  setTypeFilter(next);
7417
7722
  },
7418
7723
  children: [
7419
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
7724
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
7420
7725
  iconForType(type, 12),
7421
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-label", children: type }),
7422
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
7726
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: type }),
7727
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
7423
7728
  ]
7424
7729
  },
7425
7730
  type
7426
7731
  )),
7427
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-menu-title", children: "Priority" }),
7428
- PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7732
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Priority" }),
7733
+ PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7429
7734
  "button",
7430
7735
  {
7431
7736
  type: "button",
@@ -7439,15 +7744,15 @@ function Panel() {
7439
7744
  setPrioFilter(next);
7440
7745
  },
7441
7746
  children: [
7442
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
7443
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
7444
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
7747
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
7748
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
7749
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
7445
7750
  ]
7446
7751
  },
7447
7752
  priority
7448
7753
  )),
7449
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-menu-title", children: "Status" }),
7450
- STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7754
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-menu-title", children: "Status" }),
7755
+ STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7451
7756
  "button",
7452
7757
  {
7453
7758
  type: "button",
@@ -7461,15 +7766,15 @@ function Panel() {
7461
7766
  setStatusFilter(next);
7462
7767
  },
7463
7768
  children: [
7464
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
7465
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-status-dot rk-st-${status}` }),
7466
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-label", children: status }),
7467
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
7769
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
7770
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-status-dot rk-st-${status}` }),
7771
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-label", children: status }),
7772
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
7468
7773
  ]
7469
7774
  },
7470
7775
  status
7471
7776
  )),
7472
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7777
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7473
7778
  "button",
7474
7779
  {
7475
7780
  type: "button",
@@ -7489,24 +7794,24 @@ function Panel() {
7489
7794
  ] })
7490
7795
  ] })
7491
7796
  ] }),
7492
- preview && unapplied > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-unplaced", role: "status", children: [
7797
+ preview && unapplied > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-unplaced", role: "status", children: [
7493
7798
  unapplied,
7494
7799
  " saved change",
7495
7800
  unapplied === 1 ? "" : "s",
7496
7801
  " couldn\u2019t be placed on the current page \u2014 the page has changed since they were captured."
7497
7802
  ] }),
7498
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-empty", children: [
7499
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-empty-art", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconTarget, { size: 22 }) }),
7500
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
7501
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-empty-text", children: items.length === 0 ? "Choose \u201CPick an element\u201D, then click anything on the page. Your suggestions collect here, ready to hand off." : "Try a different search, or clear the filters." })
7502
- ] }) : groups.map(([route, entries]) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7503
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-group-title", children: route }),
7803
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-empty", children: [
7804
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-empty-art", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTarget, { size: 22 }) }),
7805
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
7806
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-empty-text", children: items.length === 0 ? "Choose \u201CPick an element\u201D, then click anything on the page. Your suggestions collect here, ready to hand off." : "Try a different search, or clear the filters." })
7807
+ ] }) : groups.map(([route, entries]) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7808
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-group-title", children: route }),
7504
7809
  entries.map(({ item, number }) => {
7505
7810
  const active2 = item.id === activeItemId;
7506
7811
  const orphan = orphanIds.has(item.id);
7507
7812
  const hiddenEl = hiddenIds.has(item.id);
7508
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
7509
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7813
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
7814
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7510
7815
  "div",
7511
7816
  {
7512
7817
  "data-item-id": item.id,
@@ -7521,18 +7826,18 @@ function Panel() {
7521
7826
  }
7522
7827
  },
7523
7828
  children: [
7524
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-num", children: number }),
7525
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-icon", children: iconForType(item.type) }),
7526
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-item-body", children: [
7527
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-item-snippet", children: [
7829
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-num", children: number }),
7830
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-icon", children: iconForType(item.type) }),
7831
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-body", children: [
7832
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-snippet", children: [
7528
7833
  item.target.tagName,
7529
7834
  " \xB7 ",
7530
7835
  item.target.textSnippet || item.target.selector
7531
7836
  ] }),
7532
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-change", children: changeSummary(item) }),
7533
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-item-meta", children: [
7534
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
7535
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7837
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-change", children: changeSummary(item) }),
7838
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-item-meta", children: [
7839
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
7840
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7536
7841
  "span",
7537
7842
  {
7538
7843
  className: "rk-author-dot",
@@ -7540,15 +7845,15 @@ function Panel() {
7540
7845
  }
7541
7846
  ),
7542
7847
  item.author?.name ?? "Unknown",
7543
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-time", children: [
7848
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-time", children: [
7544
7849
  " \xB7 ",
7545
7850
  timeAgo(item.createdAt)
7546
7851
  ] })
7547
7852
  ] }),
7548
- item.priority === "must" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-badge-mini rk-must", children: "must" }),
7549
- item.status !== "open" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
7550
- orphan && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
7551
- hiddenEl && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7853
+ item.priority === "must" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini rk-must", children: "must" }),
7854
+ item.status !== "open" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
7855
+ orphan && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
7856
+ hiddenEl && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7552
7857
  "span",
7553
7858
  {
7554
7859
  className: "rk-badge-mini rk-hidden-el",
@@ -7556,14 +7861,14 @@ function Panel() {
7556
7861
  children: "hidden"
7557
7862
  }
7558
7863
  ),
7559
- item.note && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-badge-mini", children: "note" }),
7560
- peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
7864
+ item.note && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-badge-mini", children: "note" }),
7865
+ peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
7561
7866
  peer.name || "peer",
7562
7867
  " is writing\u2026"
7563
7868
  ] }, peer.id))
7564
7869
  ] })
7565
7870
  ] }),
7566
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-item-actions", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7871
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-item-actions", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7567
7872
  "button",
7568
7873
  {
7569
7874
  type: "button",
@@ -7578,15 +7883,15 @@ function Panel() {
7578
7883
  "info"
7579
7884
  );
7580
7885
  },
7581
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconTrash, { size: 13 })
7886
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconTrash, { size: 13 })
7582
7887
  }
7583
7888
  ) })
7584
7889
  ]
7585
7890
  }
7586
7891
  ),
7587
- active2 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-item-edit", children: [
7588
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
7589
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7892
+ active2 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-item-edit", children: [
7893
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
7894
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7590
7895
  "span",
7591
7896
  {
7592
7897
  className: "rk-author-dot",
@@ -7597,8 +7902,8 @@ function Panel() {
7597
7902
  " \xB7 ",
7598
7903
  timeAgo(item.createdAt)
7599
7904
  ] }),
7600
- isCopyItem(item) && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CopyDiff, { before: item.payload.before, after: item.payload.after }),
7601
- isImageItem(item) && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7905
+ isCopyItem(item) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CopyDiff, { before: item.payload.before, after: item.payload.after }),
7906
+ isImageItem(item) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7602
7907
  ImageDetail,
7603
7908
  {
7604
7909
  beforeSrc: item.payload.beforeSrc,
@@ -7606,7 +7911,7 @@ function Panel() {
7606
7911
  afterDataUrl: item.payload.afterDataUrl
7607
7912
  }
7608
7913
  ),
7609
- isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7914
+ isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7610
7915
  "textarea",
7611
7916
  {
7612
7917
  className: "rk-textarea",
@@ -7616,7 +7921,7 @@ function Panel() {
7616
7921
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: withPayloadText(item, event.target.value) })
7617
7922
  }
7618
7923
  ),
7619
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7924
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7620
7925
  "input",
7621
7926
  {
7622
7927
  className: "rk-input",
@@ -7626,9 +7931,9 @@ function Panel() {
7626
7931
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: { note: event.target.value } })
7627
7932
  }
7628
7933
  ),
7629
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
7630
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
7631
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7934
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
7935
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
7936
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7632
7937
  "button",
7633
7938
  {
7634
7939
  type: "button",
@@ -7637,7 +7942,7 @@ function Panel() {
7637
7942
  children: "Must"
7638
7943
  }
7639
7944
  ),
7640
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7945
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7641
7946
  "button",
7642
7947
  {
7643
7948
  type: "button",
@@ -7647,7 +7952,7 @@ function Panel() {
7647
7952
  }
7648
7953
  )
7649
7954
  ] }),
7650
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7955
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7651
7956
  "button",
7652
7957
  {
7653
7958
  type: "button",
@@ -7662,22 +7967,22 @@ function Panel() {
7662
7967
  ] }, item.id);
7663
7968
  })
7664
7969
  ] }, route)) }),
7665
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rk-panel-foot", children: [
7666
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
7667
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconDownload, { size: 13 }),
7970
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-panel-foot", children: [
7971
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
7972
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconDownload, { size: 13 }),
7668
7973
  " Export"
7669
7974
  ] }),
7670
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
7671
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconCopyClipboard, { size: 13 }),
7975
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
7976
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconCopyClipboard, { size: 13 }),
7672
7977
  " Copy"
7673
7978
  ] }),
7674
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
7675
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconUpload, { size: 13 }),
7979
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
7980
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconUpload, { size: 13 }),
7676
7981
  " Import"
7677
7982
  ] }),
7678
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rk-spacer" }),
7679
- undoStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "rk-icon-btn", "aria-label": "Undo last change (\u2318Z)", onClick: () => dispatch({ type: "UNDO" }), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconUndo, { size: 14 }) }),
7680
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7983
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rk-spacer" }),
7984
+ undoStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "rk-icon-btn", "aria-label": "Undo last change (\u2318Z)", onClick: () => dispatch({ type: "UNDO" }), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconUndo, { size: 14 }) }),
7985
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7681
7986
  "button",
7682
7987
  {
7683
7988
  type: "button",
@@ -7688,7 +7993,7 @@ function Panel() {
7688
7993
  children: "Clear"
7689
7994
  }
7690
7995
  ),
7691
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7996
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7692
7997
  "button",
7693
7998
  {
7694
7999
  type: "button",
@@ -7698,7 +8003,7 @@ function Panel() {
7698
8003
  children: "Exit review"
7699
8004
  }
7700
8005
  ),
7701
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8006
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7702
8007
  "input",
7703
8008
  {
7704
8009
  ref: importRef,
@@ -7717,13 +8022,14 @@ function Panel() {
7717
8022
  }
7718
8023
  );
7719
8024
  }
7720
- var import_react13, import_jsx_runtime11, TYPE_FILTERS, PRIORITY_FILTERS, STATUSES, PRIORITY_LABELS;
8025
+ var import_react14, import_jsx_runtime12, TYPE_FILTERS, PRIORITY_FILTERS, STATUSES, PRIORITY_LABELS, SYNC_REASON_LABEL, SYNC_REASON_HELP;
7721
8026
  var init_Panel = __esm({
7722
8027
  "src/client/components/Panel.tsx"() {
7723
8028
  "use strict";
7724
- import_react13 = require("react");
8029
+ import_react14 = require("react");
7725
8030
  init_schema();
7726
8031
  init_env();
8032
+ init_sync();
7727
8033
  init_emblema();
7728
8034
  init_exportSession();
7729
8035
  init_shortcuts();
@@ -7731,11 +8037,23 @@ var init_Panel = __esm({
7731
8037
  init_target();
7732
8038
  init_store();
7733
8039
  init_icons();
7734
- import_jsx_runtime11 = require("react/jsx-runtime");
8040
+ import_jsx_runtime12 = require("react/jsx-runtime");
7735
8041
  TYPE_FILTERS = ["copy", "image", "comment", "delete", "move", "style"];
7736
8042
  PRIORITY_FILTERS = ["must", "nice"];
7737
8043
  STATUSES = ["open", "accepted", "rejected", "applied"];
7738
8044
  PRIORITY_LABELS = { must: "Must", nice: "Nice to have" };
8045
+ SYNC_REASON_LABEL = {
8046
+ "token-short": "token too short",
8047
+ "bad-token": "token rejected",
8048
+ "room-full": "room full",
8049
+ unreachable: "server unreachable"
8050
+ };
8051
+ SYNC_REASON_HELP = {
8052
+ "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.",
8053
+ "bad-token": "The sync server rejected the token. Check `syncToken` / `token` in <ReviewKit />.",
8054
+ "room-full": "This page already has the maximum number of reviewers online (16). Try again later.",
8055
+ 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."
8056
+ };
7739
8057
  }
7740
8058
  });
7741
8059
 
@@ -7749,8 +8067,8 @@ function initials(name) {
7749
8067
  function PresenceStrip() {
7750
8068
  const { peers, syncStatus } = useStore();
7751
8069
  if (syncStatus === "disabled" || peers.length === 0) return null;
7752
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
7753
- peers.slice(0, 5).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
8070
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
8071
+ peers.slice(0, 5).map((peer) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7754
8072
  "span",
7755
8073
  {
7756
8074
  className: "rk-avatar",
@@ -7760,7 +8078,7 @@ function PresenceStrip() {
7760
8078
  },
7761
8079
  peer.id
7762
8080
  )),
7763
- peers.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "rk-avatar rk-avatar-more", children: [
8081
+ peers.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "rk-avatar rk-avatar-more", children: [
7764
8082
  "+",
7765
8083
  peers.length - 5
7766
8084
  ] })
@@ -7768,13 +8086,13 @@ function PresenceStrip() {
7768
8086
  }
7769
8087
  function PeerCursors() {
7770
8088
  const { peers } = useStore();
7771
- const ringRefs = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
7772
- const cursorRefs = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
7773
- const motion = (0, import_react14.useRef)(
8089
+ const ringRefs = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
8090
+ const cursorRefs = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
8091
+ const motion = (0, import_react15.useRef)(
7774
8092
  /* @__PURE__ */ new Map()
7775
8093
  );
7776
- const peersRef = (0, import_react14.useRef)(peers);
7777
- const seen = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
8094
+ const peersRef = (0, import_react15.useRef)(peers);
8095
+ const seen = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
7778
8096
  for (const peer of peers) {
7779
8097
  const sig = peer.cursor ? `${peer.cursor.x},${peer.cursor.y},${peer.cursor.ox},${peer.cursor.oy}` : "";
7780
8098
  if (seen.current.get(peer.id) !== sig) {
@@ -7784,7 +8102,7 @@ function PeerCursors() {
7784
8102
  }
7785
8103
  }
7786
8104
  peersRef.current = peers;
7787
- (0, import_react14.useEffect)(() => {
8105
+ (0, import_react15.useEffect)(() => {
7788
8106
  const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
7789
8107
  let raf = 0;
7790
8108
  let last = performance.now();
@@ -7889,8 +8207,8 @@ function PeerCursors() {
7889
8207
  return () => cancelAnimationFrame(raf);
7890
8208
  }, []);
7891
8209
  if (peers.length === 0) return null;
7892
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
7893
- peers.map((peer) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
8210
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
8211
+ peers.map((peer) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7894
8212
  "div",
7895
8213
  {
7896
8214
  ref: (el) => {
@@ -7902,7 +8220,7 @@ function PeerCursors() {
7902
8220
  },
7903
8221
  `ring-${peer.id}`
7904
8222
  )),
7905
- peers.map((peer, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8223
+ peers.map((peer, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7906
8224
  "div",
7907
8225
  {
7908
8226
  ref: (el) => {
@@ -7912,8 +8230,8 @@ function PeerCursors() {
7912
8230
  className: "rk-peer-cursor",
7913
8231
  style: { opacity: 0 },
7914
8232
  children: [
7915
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("svg", { width: "14", height: "18", viewBox: "0 0 14 18", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M1 1l12 7.5-5.2 1.2L5 16z", fill: peer.color, stroke: "rgba(0,0,0,0.25)", strokeWidth: "1" }) }),
7916
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8233
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { width: "14", height: "18", viewBox: "0 0 14 18", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M1 1l12 7.5-5.2 1.2L5 16z", fill: peer.color, stroke: "rgba(0,0,0,0.25)", strokeWidth: "1" }) }),
8234
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7917
8235
  "span",
7918
8236
  {
7919
8237
  className: "rk-peer-name",
@@ -7930,14 +8248,14 @@ function PeerCursors() {
7930
8248
  ))
7931
8249
  ] });
7932
8250
  }
7933
- var import_react14, import_jsx_runtime12;
8251
+ var import_react15, import_jsx_runtime13;
7934
8252
  var init_Presence = __esm({
7935
8253
  "src/client/components/Presence.tsx"() {
7936
8254
  "use strict";
7937
- import_react14 = require("react");
8255
+ import_react15 = require("react");
7938
8256
  init_store();
7939
8257
  init_target();
7940
- import_jsx_runtime12 = require("react/jsx-runtime");
8258
+ import_jsx_runtime13 = require("react/jsx-runtime");
7941
8259
  }
7942
8260
  });
7943
8261
 
@@ -7945,14 +8263,14 @@ var init_Presence = __esm({
7945
8263
  function QuickComment() {
7946
8264
  const { quick } = useStore();
7947
8265
  if (!quick) return null;
7948
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(QuickComposerCard, { quick }, quick.id);
8266
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(QuickComposerCard, { quick }, quick.id);
7949
8267
  }
7950
8268
  function QuickComposerCard({ quick }) {
7951
8269
  const { route, session } = useStore();
7952
8270
  const dispatch = useDispatch();
7953
- const [text, setText] = (0, import_react15.useState)("");
7954
- const fieldRef = (0, import_react15.useRef)(null);
7955
- (0, import_react15.useEffect)(() => {
8271
+ const [text, setText] = (0, import_react16.useState)("");
8272
+ const fieldRef = (0, import_react16.useRef)(null);
8273
+ (0, import_react16.useEffect)(() => {
7956
8274
  const field = fieldRef.current;
7957
8275
  if (!field) return;
7958
8276
  field.focus();
@@ -8005,18 +8323,18 @@ function QuickComposerCard({ quick }) {
8005
8323
  };
8006
8324
  const x = Math.min(Math.max(quick.x, 8), window.innerWidth - 288);
8007
8325
  const y = Math.min(Math.max(quick.y + 12, 8), window.innerHeight - 108);
8008
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
8009
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rk-quick-head", children: [
8010
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconComment, { size: 12 }),
8011
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Comment here" }),
8012
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "rk-hint", children: [
8013
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
8326
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
8327
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rk-quick-head", children: [
8328
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconComment, { size: 12 }),
8329
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Comment here" }),
8330
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "rk-hint", children: [
8331
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "rk-kbd", children: "\u21B5" }),
8014
8332
  " save \xB7 ",
8015
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "rk-kbd", children: "esc" }),
8333
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "rk-kbd", children: "esc" }),
8016
8334
  " cancel"
8017
8335
  ] })
8018
8336
  ] }),
8019
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
8337
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
8020
8338
  "textarea",
8021
8339
  {
8022
8340
  ref: fieldRef,
@@ -8030,11 +8348,11 @@ function QuickComposerCard({ quick }) {
8030
8348
  )
8031
8349
  ] });
8032
8350
  }
8033
- var import_react15, import_jsx_runtime13;
8351
+ var import_react16, import_jsx_runtime14;
8034
8352
  var init_QuickComment = __esm({
8035
8353
  "src/client/components/QuickComment.tsx"() {
8036
8354
  "use strict";
8037
- import_react15 = require("react");
8355
+ import_react16 = require("react");
8038
8356
  init_effectCatalog();
8039
8357
  init_effectsLazy();
8040
8358
  init_id();
@@ -8043,7 +8361,7 @@ var init_QuickComment = __esm({
8043
8361
  init_sync();
8044
8362
  init_store();
8045
8363
  init_icons();
8046
- import_jsx_runtime13 = require("react/jsx-runtime");
8364
+ import_jsx_runtime14 = require("react/jsx-runtime");
8047
8365
  }
8048
8366
  });
8049
8367
 
@@ -8062,14 +8380,14 @@ function applyRect(el, rect, pad2 = 3) {
8062
8380
  function PickLayer() {
8063
8381
  const { mode, selection, tool } = useStore();
8064
8382
  const dispatch = useDispatch();
8065
- const [hovered, setHovered] = (0, import_react16.useState)(null);
8066
- const outlineRef = (0, import_react16.useRef)(null);
8067
- const labelRef = (0, import_react16.useRef)(null);
8068
- const selectRef = (0, import_react16.useRef)(null);
8069
- const hoveredRef = (0, import_react16.useRef)(null);
8383
+ const [hovered, setHovered] = (0, import_react17.useState)(null);
8384
+ const outlineRef = (0, import_react17.useRef)(null);
8385
+ const labelRef = (0, import_react17.useRef)(null);
8386
+ const selectRef = (0, import_react17.useRef)(null);
8387
+ const hoveredRef = (0, import_react17.useRef)(null);
8070
8388
  hoveredRef.current = hovered;
8071
8389
  const picking = mode === "pick";
8072
- (0, import_react16.useEffect)(() => {
8390
+ (0, import_react17.useEffect)(() => {
8073
8391
  if (!picking) {
8074
8392
  setHovered(null);
8075
8393
  return;
@@ -8113,7 +8431,7 @@ function PickLayer() {
8113
8431
  document.removeEventListener("mousedown", swallow, true);
8114
8432
  };
8115
8433
  }, [picking, tool, dispatch]);
8116
- (0, import_react16.useEffect)(() => {
8434
+ (0, import_react17.useEffect)(() => {
8117
8435
  if (!picking) return;
8118
8436
  const body = document.body;
8119
8437
  const prev = body.style.cursor;
@@ -8123,7 +8441,7 @@ function PickLayer() {
8123
8441
  if (body.getAttribute("style") === "") body.removeAttribute("style");
8124
8442
  };
8125
8443
  }, [picking, tool]);
8126
- (0, import_react16.useEffect)(() => {
8444
+ (0, import_react17.useEffect)(() => {
8127
8445
  let raf = 0;
8128
8446
  const tick = () => {
8129
8447
  const hoverEl = hoveredRef.current;
@@ -8149,26 +8467,26 @@ function PickLayer() {
8149
8467
  raf = requestAnimationFrame(tick);
8150
8468
  return () => cancelAnimationFrame(raf);
8151
8469
  }, [picking, selection]);
8152
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
8153
- picking && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
8154
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
8155
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
8156
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
8157
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
8158
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Pick an element" }) })
8470
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8471
+ picking && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8472
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
8473
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8474
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
8475
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
8476
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Pick an element" }) })
8159
8477
  ] }),
8160
- selection && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
8478
+ selection && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
8161
8479
  ] });
8162
8480
  }
8163
- var import_react16, import_jsx_runtime14;
8481
+ var import_react17, import_jsx_runtime15;
8164
8482
  var init_PickLayer = __esm({
8165
8483
  "src/client/components/PickLayer.tsx"() {
8166
8484
  "use strict";
8167
- import_react16 = require("react");
8485
+ import_react17 = require("react");
8168
8486
  init_shortcuts();
8169
8487
  init_target();
8170
8488
  init_store();
8171
- import_jsx_runtime14 = require("react/jsx-runtime");
8489
+ import_jsx_runtime15 = require("react/jsx-runtime");
8172
8490
  }
8173
8491
  });
8174
8492
 
@@ -8176,22 +8494,22 @@ var init_PickLayer = __esm({
8176
8494
  function Pins() {
8177
8495
  const { session, route, activeItemId, editor, mode, filter } = useStore();
8178
8496
  const dispatch = useDispatch();
8179
- const [clusters, setClusters] = (0, import_react17.useState)([]);
8180
- const [orphanCount, setOrphanCount] = (0, import_react17.useState)(0);
8181
- const wrapRefs = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
8182
- const visibleCache = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
8183
- const highlightRef = (0, import_react17.useRef)(null);
8184
- const hoveredItems = (0, import_react17.useRef)(null);
8185
- const elementCache = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
8186
- const lastResolve = (0, import_react17.useRef)(0);
8187
- const [newestId, setNewestId] = (0, import_react17.useState)(null);
8188
- const prevCount = (0, import_react17.useRef)(0);
8189
- const items = (0, import_react17.useMemo)(() => {
8497
+ const [clusters, setClusters] = (0, import_react18.useState)([]);
8498
+ const [orphanCount, setOrphanCount] = (0, import_react18.useState)(0);
8499
+ const wrapRefs = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8500
+ const visibleCache = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8501
+ const highlightRef = (0, import_react18.useRef)(null);
8502
+ const hoveredItems = (0, import_react18.useRef)(null);
8503
+ const elementCache = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
8504
+ const lastResolve = (0, import_react18.useRef)(0);
8505
+ const [newestId, setNewestId] = (0, import_react18.useState)(null);
8506
+ const prevCount = (0, import_react18.useRef)(0);
8507
+ const items = (0, import_react18.useMemo)(() => {
8190
8508
  const all = session?.items ?? [];
8191
8509
  return all.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => item.route === route);
8192
8510
  }, [session, route]);
8193
8511
  const byId = new Map(items.map(({ item }) => [item.id, item]));
8194
- (0, import_react17.useEffect)(() => {
8512
+ (0, import_react18.useEffect)(() => {
8195
8513
  const count = session?.items.length ?? 0;
8196
8514
  if (count > prevCount.current && session && count > 0) {
8197
8515
  const id = session.items[count - 1].id;
@@ -8202,7 +8520,7 @@ function Pins() {
8202
8520
  }
8203
8521
  prevCount.current = count;
8204
8522
  }, [session]);
8205
- (0, import_react17.useEffect)(() => {
8523
+ (0, import_react18.useEffect)(() => {
8206
8524
  let raf = 0;
8207
8525
  let lastSignature = "";
8208
8526
  const tick = (time) => {
@@ -8286,8 +8604,8 @@ function Pins() {
8286
8604
  dispatch({ type: "SET_ACTIVE_ITEM", id });
8287
8605
  dispatch({ type: "SET_PANEL", open: true });
8288
8606
  };
8289
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8290
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
8607
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
8608
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
8291
8609
  clusters.map((cluster) => {
8292
8610
  const isCluster = cluster.ids.length > 1;
8293
8611
  const isActive = activeItemId !== null && cluster.ids.includes(activeItemId);
@@ -8297,7 +8615,7 @@ function Pins() {
8297
8615
  const status = statuses.size === 1 ? members[0]?.status : void 0;
8298
8616
  const dimmed = filter !== null && members.length > 0 && !members.some((item) => itemMatchesFilter(item, filter));
8299
8617
  const statusHint = isCluster ? status ? ` \xB7 all ${STATUS_LABELS[status] ?? status}` : "" : ` \xB7 ${STATUS_LABELS[members[0]?.status ?? "open"] ?? members[0]?.status}`;
8300
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8618
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8301
8619
  "div",
8302
8620
  {
8303
8621
  ref: (el) => {
@@ -8305,7 +8623,7 @@ function Pins() {
8305
8623
  else wrapRefs.current.delete(cluster.key);
8306
8624
  },
8307
8625
  className: `rk-pin-wrap${picking ? " rk-pin-ghost" : ""}`,
8308
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8626
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8309
8627
  "button",
8310
8628
  {
8311
8629
  type: "button",
@@ -8327,7 +8645,7 @@ function Pins() {
8327
8645
  cluster.key
8328
8646
  );
8329
8647
  }),
8330
- orphanCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8648
+ orphanCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8331
8649
  "button",
8332
8650
  {
8333
8651
  type: "button",
@@ -8335,7 +8653,7 @@ function Pins() {
8335
8653
  style: { bottom: 16, left: "50%", transform: "translateX(-50%)" },
8336
8654
  onClick: () => dispatch({ type: "SET_PANEL", open: true }),
8337
8655
  children: [
8338
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconUnlink, { size: 13 }),
8656
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconUnlink, { size: 13 }),
8339
8657
  orphanCount,
8340
8658
  " unanchored item",
8341
8659
  orphanCount === 1 ? "" : "s"
@@ -8344,15 +8662,15 @@ function Pins() {
8344
8662
  )
8345
8663
  ] });
8346
8664
  }
8347
- var import_react17, import_jsx_runtime15, STATUS_LABELS, CLUSTER_RADIUS, RERESOLVE_MS;
8665
+ var import_react18, import_jsx_runtime16, STATUS_LABELS, CLUSTER_RADIUS, RERESOLVE_MS;
8348
8666
  var init_Pins = __esm({
8349
8667
  "src/client/components/Pins.tsx"() {
8350
8668
  "use strict";
8351
- import_react17 = require("react");
8669
+ import_react18 = require("react");
8352
8670
  init_target();
8353
8671
  init_store();
8354
8672
  init_icons();
8355
- import_jsx_runtime15 = require("react/jsx-runtime");
8673
+ import_jsx_runtime16 = require("react/jsx-runtime");
8356
8674
  STATUS_LABELS = {
8357
8675
  open: "open",
8358
8676
  accepted: "accepted",
@@ -8369,24 +8687,24 @@ function Toolbar() {
8369
8687
  const { selection, editor, peers, syncStatus, mode, tool } = useStore();
8370
8688
  const hasPeers = syncStatus !== "disabled" && peers.length > 0;
8371
8689
  const dispatch = useDispatch();
8372
- const ref = (0, import_react18.useRef)(null);
8373
- const [pos, setPos] = (0, import_react18.useState)(() => readStoredPos(POS_KEY2));
8374
- const drag = (0, import_react18.useRef)(null);
8375
- const [dragging, setDragging] = (0, import_react18.useState)(false);
8376
- const justDragged = (0, import_react18.useRef)(false);
8690
+ const ref = (0, import_react19.useRef)(null);
8691
+ const [pos, setPos] = (0, import_react19.useState)(() => readStoredPos(POS_KEY2));
8692
+ const drag = (0, import_react19.useRef)(null);
8693
+ const [dragging, setDragging] = (0, import_react19.useState)(false);
8694
+ const justDragged = (0, import_react19.useRef)(false);
8377
8695
  const visible = editor === null || selection !== null;
8378
8696
  const applyPos = (next, persist) => {
8379
8697
  if (next === null && ref.current) ref.current.style.bottom = "";
8380
8698
  setPos(next);
8381
8699
  if (persist) storePos(POS_KEY2, next);
8382
8700
  };
8383
- (0, import_react18.useLayoutEffect)(() => {
8701
+ (0, import_react19.useLayoutEffect)(() => {
8384
8702
  const el = ref.current;
8385
8703
  if (!el || pos === null) return;
8386
8704
  const clamped = clampPos(pos.x, pos.y, el.offsetWidth, el.offsetHeight);
8387
8705
  if (clamped.x !== pos.x || clamped.y !== pos.y) applyPos(clamped, true);
8388
8706
  }, [pos, selection, hasPeers, visible]);
8389
- (0, import_react18.useEffect)(() => {
8707
+ (0, import_react19.useEffect)(() => {
8390
8708
  const onResize = () => {
8391
8709
  const el = ref.current;
8392
8710
  const current = readStoredPos(POS_KEY2);
@@ -8499,7 +8817,7 @@ function Toolbar() {
8499
8817
  mode === "pick" && selection === null ? "rk-ghost" : ""
8500
8818
  ].filter(Boolean).join(" ");
8501
8819
  const style = pos !== null ? { left: 0, top: 0, translate: `${pos.x}px ${pos.y}px` } : void 0;
8502
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8820
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8503
8821
  "div",
8504
8822
  {
8505
8823
  ref,
@@ -8510,7 +8828,7 @@ function Toolbar() {
8510
8828
  onPointerDown,
8511
8829
  onClickCapture,
8512
8830
  children: [
8513
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8831
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8514
8832
  "button",
8515
8833
  {
8516
8834
  type: "button",
@@ -8518,19 +8836,19 @@ function Toolbar() {
8518
8836
  "aria-label": "Move the toolbar \u2014 drag, or use arrow keys; double-click to reset its position",
8519
8837
  onKeyDown: onGripKeyDown,
8520
8838
  onDoubleClick: () => applyPos(null, true),
8521
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
8522
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "2", cy: "2", r: "1.1" }),
8523
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "6", cy: "2", r: "1.1" }),
8524
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "2", cy: "7", r: "1.1" }),
8525
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "6", cy: "7", r: "1.1" }),
8526
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "2", cy: "12", r: "1.1" }),
8527
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "6", cy: "12", r: "1.1" })
8839
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
8840
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "2", r: "1.1" }),
8841
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "2", r: "1.1" }),
8842
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "7", r: "1.1" }),
8843
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "7", r: "1.1" }),
8844
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "2", cy: "12", r: "1.1" }),
8845
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "6", cy: "12", r: "1.1" })
8528
8846
  ] })
8529
8847
  }
8530
8848
  ),
8531
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PresenceStrip, {}),
8532
- hasPeers && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "rk-toolbar-sep" }),
8533
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8849
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PresenceStrip, {}),
8850
+ hasPeers && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8851
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8534
8852
  "button",
8535
8853
  {
8536
8854
  type: "button",
@@ -8542,9 +8860,9 @@ function Toolbar() {
8542
8860
  },
8543
8861
  entry.tool
8544
8862
  )) }),
8545
- selection && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "rk-toolbar-sep" }),
8546
- selection && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
8547
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8863
+ selection && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8864
+ selection && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
8865
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8548
8866
  "button",
8549
8867
  {
8550
8868
  type: "button",
@@ -8553,12 +8871,12 @@ function Toolbar() {
8553
8871
  title: `Rewrite text${keyHint("tool-copy")}`,
8554
8872
  onClick: () => open("copy"),
8555
8873
  children: [
8556
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconText, { size: 14 }),
8874
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconText, { size: 14 }),
8557
8875
  " Rewrite"
8558
8876
  ]
8559
8877
  }
8560
8878
  ),
8561
- hasImage && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8879
+ hasImage && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8562
8880
  "button",
8563
8881
  {
8564
8882
  type: "button",
@@ -8567,12 +8885,12 @@ function Toolbar() {
8567
8885
  title: `Replace image${keyHint("tool-image")}`,
8568
8886
  onClick: () => open("image"),
8569
8887
  children: [
8570
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconImage, { size: 14 }),
8888
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconImage, { size: 14 }),
8571
8889
  " Image"
8572
8890
  ]
8573
8891
  }
8574
8892
  ),
8575
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8893
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8576
8894
  "button",
8577
8895
  {
8578
8896
  type: "button",
@@ -8581,32 +8899,32 @@ function Toolbar() {
8581
8899
  title: `Comment${keyHint("tool-comment")}`,
8582
8900
  onClick: () => open("comment"),
8583
8901
  children: [
8584
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconComment, { size: 14 }),
8902
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconComment, { size: 14 }),
8585
8903
  " Comment"
8586
8904
  ]
8587
8905
  }
8588
8906
  ),
8589
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
8590
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconStyle, { size: 14 }),
8907
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
8908
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconStyle, { size: 14 }),
8591
8909
  " Style"
8592
8910
  ] }),
8593
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
8594
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconMove, { size: 14 }),
8911
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
8912
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconMove, { size: 14 }),
8595
8913
  " Move"
8596
8914
  ] }),
8597
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
8598
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconTrash, { size: 14 }),
8915
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
8916
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconTrash, { size: 14 }),
8599
8917
  " Delete"
8600
8918
  ] }),
8601
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "rk-toolbar-sep" }),
8602
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8919
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "rk-toolbar-sep" }),
8920
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8603
8921
  "button",
8604
8922
  {
8605
8923
  type: "button",
8606
8924
  className: "rk-icon-btn",
8607
8925
  "aria-label": "Deselect",
8608
8926
  onClick: () => dispatch({ type: "SELECT", selection: null }),
8609
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconClose, { size: 13 })
8927
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconClose, { size: 13 })
8610
8928
  }
8611
8929
  )
8612
8930
  ] })
@@ -8614,11 +8932,11 @@ function Toolbar() {
8614
8932
  }
8615
8933
  );
8616
8934
  }
8617
- var import_react18, import_jsx_runtime16, TOOLS, POS_KEY2, DRAG_THRESHOLD2;
8935
+ var import_react19, import_jsx_runtime17, TOOLS, POS_KEY2, DRAG_THRESHOLD2;
8618
8936
  var init_Toolbar = __esm({
8619
8937
  "src/client/components/Toolbar.tsx"() {
8620
8938
  "use strict";
8621
- import_react18 = require("react");
8939
+ import_react19 = require("react");
8622
8940
  init_dragPosition();
8623
8941
  init_store();
8624
8942
  init_preview();
@@ -8626,12 +8944,12 @@ var init_Toolbar = __esm({
8626
8944
  init_target();
8627
8945
  init_Presence();
8628
8946
  init_icons();
8629
- import_jsx_runtime16 = require("react/jsx-runtime");
8947
+ import_jsx_runtime17 = require("react/jsx-runtime");
8630
8948
  TOOLS = [
8631
- { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconTarget, { size: n }) },
8632
- { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconText, { size: n }) },
8633
- { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconComment, { size: n }) },
8634
- { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconImage, { size: n }) }
8949
+ { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconTarget, { size: n }) },
8950
+ { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconText, { size: n }) },
8951
+ { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconComment, { size: n }) },
8952
+ { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconImage, { size: n }) }
8635
8953
  ];
8636
8954
  POS_KEY2 = "review-kit:toolbar-pos";
8637
8955
  DRAG_THRESHOLD2 = 6;
@@ -8641,28 +8959,28 @@ var init_Toolbar = __esm({
8641
8959
  // src/client/components/Toast.tsx
8642
8960
  function ToastRow({ toast: toast2 }) {
8643
8961
  const dispatch = useDispatch();
8644
- (0, import_react19.useEffect)(() => {
8962
+ (0, import_react20.useEffect)(() => {
8645
8963
  const timer = window.setTimeout(() => dispatch({ type: "DISMISS_TOAST", id: toast2.id }), 3500);
8646
8964
  return () => window.clearTimeout(timer);
8647
8965
  }, [toast2.id, dispatch]);
8648
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
8649
- toast2.tone === "success" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconWarning, { size: 13 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconComment, { size: 13 }),
8966
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
8967
+ toast2.tone === "success" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconWarning, { size: 13 }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconComment, { size: 13 }),
8650
8968
  toast2.message
8651
8969
  ] });
8652
8970
  }
8653
8971
  function ToastHost() {
8654
8972
  const { toasts } = useStore();
8655
8973
  if (toasts.length === 0) return null;
8656
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ToastRow, { toast: toast2 }, toast2.id)) });
8974
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ToastRow, { toast: toast2 }, toast2.id)) });
8657
8975
  }
8658
- var import_react19, import_jsx_runtime17;
8976
+ var import_react20, import_jsx_runtime18;
8659
8977
  var init_Toast = __esm({
8660
8978
  "src/client/components/Toast.tsx"() {
8661
8979
  "use strict";
8662
- import_react19 = require("react");
8980
+ import_react20 = require("react");
8663
8981
  init_store();
8664
8982
  init_icons();
8665
- import_jsx_runtime17 = require("react/jsx-runtime");
8983
+ import_jsx_runtime18 = require("react/jsx-runtime");
8666
8984
  }
8667
8985
  });
8668
8986
 
@@ -8672,14 +8990,14 @@ function App() {
8672
8990
  const rawDispatch = useDispatch();
8673
8991
  const config = useConfig();
8674
8992
  const dispatch = useRealtimeSync(state, rawDispatch, config, reducer);
8675
- const saveTimer = (0, import_react20.useRef)(null);
8993
+ const saveTimer = (0, import_react21.useRef)(null);
8676
8994
  const storageKey = config.storageKey ?? defaultStorageKey();
8677
8995
  useAppliedPreview(state, dispatch);
8678
8996
  useEmblemaAutoPush(state, dispatch, config);
8679
- (0, import_react20.useEffect)(() => {
8997
+ (0, import_react21.useEffect)(() => {
8680
8998
  setEffectAssetsUrl(config.fxAssetsUrl);
8681
8999
  }, [config.fxAssetsUrl]);
8682
- (0, import_react20.useEffect)(() => {
9000
+ (0, import_react21.useEffect)(() => {
8683
9001
  let cancelled = false;
8684
9002
  void loadSession(storageKey).then((stored) => {
8685
9003
  if (cancelled) return;
@@ -8697,7 +9015,7 @@ function App() {
8697
9015
  };
8698
9016
  }, [storageKey, config.defaultAuthor, dispatch]);
8699
9017
  const { session } = state;
8700
- (0, import_react20.useEffect)(() => {
9018
+ (0, import_react21.useEffect)(() => {
8701
9019
  if (!session) return;
8702
9020
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
8703
9021
  saveTimer.current = window.setTimeout(() => {
@@ -8708,9 +9026,9 @@ function App() {
8708
9026
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
8709
9027
  };
8710
9028
  }, [session, storageKey, config]);
8711
- (0, import_react20.useEffect)(() => trackPointer(), []);
8712
- const focusHandled = (0, import_react20.useRef)(false);
8713
- (0, import_react20.useEffect)(() => {
9029
+ (0, import_react21.useEffect)(() => trackPointer(), []);
9030
+ const focusHandled = (0, import_react21.useRef)(false);
9031
+ (0, import_react21.useEffect)(() => {
8714
9032
  if (!session || focusHandled.current) return;
8715
9033
  focusHandled.current = true;
8716
9034
  const id = takeFocusItem();
@@ -8735,7 +9053,7 @@ function App() {
8735
9053
  cancelled = true;
8736
9054
  };
8737
9055
  }, [session, dispatch]);
8738
- (0, import_react20.useEffect)(() => {
9056
+ (0, import_react21.useEffect)(() => {
8739
9057
  let last = window.location.pathname;
8740
9058
  const check = () => {
8741
9059
  if (window.location.pathname !== last) {
@@ -8750,9 +9068,9 @@ function App() {
8750
9068
  window.removeEventListener("popstate", check);
8751
9069
  };
8752
9070
  }, [dispatch]);
8753
- const stateRef = (0, import_react20.useRef)(state);
9071
+ const stateRef = (0, import_react21.useRef)(state);
8754
9072
  stateRef.current = state;
8755
- (0, import_react20.useEffect)(() => {
9073
+ (0, import_react21.useEffect)(() => {
8756
9074
  const onKeyDown = (event) => {
8757
9075
  const current = stateRef.current;
8758
9076
  for (const def of SHORTCUTS) {
@@ -8767,7 +9085,7 @@ function App() {
8767
9085
  document.addEventListener("keydown", onKeyDown);
8768
9086
  return () => document.removeEventListener("keydown", onKeyDown);
8769
9087
  }, [dispatch]);
8770
- (0, import_react20.useEffect)(() => {
9088
+ (0, import_react21.useEffect)(() => {
8771
9089
  if (!state.editor) return;
8772
9090
  const editorEl = state.editor.element;
8773
9091
  const onPointerDown = (event) => {
@@ -8780,31 +9098,32 @@ function App() {
8780
9098
  }, [state.editor, dispatch]);
8781
9099
  if (!session) return null;
8782
9100
  if (state.hidden) {
8783
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DispatchContext.Provider, { value: dispatch, children: null });
8784
- }
8785
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DispatchContext.Provider, { value: dispatch, children: [
8786
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PickLayer, {}),
8787
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PeerCursors, {}),
8788
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Toolbar, {}),
8789
- state.editor?.kind === "copy" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(InlineEditor, { editor: state.editor }),
8790
- state.editor?.kind === "image" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ImageEditor, { editor: state.editor }),
8791
- state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NoteComposer, { editor: state.editor }),
8792
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Pins, {}),
8793
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(QuickComment, {}),
8794
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Panel, {}),
8795
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Badge, {}),
8796
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CoachMark, {}),
8797
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CheatSheet, {}),
8798
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(EmblemaConnect, {}),
8799
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ConfirmHost, {}),
8800
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ToastHost, {})
9101
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DispatchContext.Provider, { value: dispatch, children: null });
9102
+ }
9103
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DispatchContext.Provider, { value: dispatch, children: [
9104
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PickLayer, {}),
9105
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PeerCursors, {}),
9106
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Toolbar, {}),
9107
+ state.editor?.kind === "copy" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(InlineEditor, { editor: state.editor }),
9108
+ state.editor?.kind === "image" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ImageEditor, { editor: state.editor }),
9109
+ state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(NoteComposer, { editor: state.editor }),
9110
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pins, {}),
9111
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(QuickComment, {}),
9112
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Panel, {}),
9113
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Badge, {}),
9114
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CoachMark, {}),
9115
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CheatSheet, {}),
9116
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(EmblemaConnect, {}),
9117
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TerminalConnect, {}),
9118
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConfirmHost, {}),
9119
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ToastHost, {})
8801
9120
  ] });
8802
9121
  }
8803
- var import_react20, import_jsx_runtime18;
9122
+ var import_react21, import_jsx_runtime19;
8804
9123
  var init_App = __esm({
8805
9124
  "src/client/App.tsx"() {
8806
9125
  "use strict";
8807
- import_react20 = require("react");
9126
+ import_react21 = require("react");
8808
9127
  init_env();
8809
9128
  init_persist();
8810
9129
  init_reveal();
@@ -8820,6 +9139,7 @@ var init_App = __esm({
8820
9139
  init_Badge();
8821
9140
  init_CheatSheet();
8822
9141
  init_EmblemaConnect();
9142
+ init_TerminalConnect();
8823
9143
  init_CoachMark();
8824
9144
  init_Confirm();
8825
9145
  init_ImageEditor();
@@ -8832,14 +9152,14 @@ var init_App = __esm({
8832
9152
  init_Pins();
8833
9153
  init_Toolbar();
8834
9154
  init_Toast();
8835
- import_jsx_runtime18 = require("react/jsx-runtime");
9155
+ import_jsx_runtime19 = require("react/jsx-runtime");
8836
9156
  }
8837
9157
  });
8838
9158
 
8839
9159
  // src/client/ReviewKit.tsx
8840
9160
  function ShadowHost({ config }) {
8841
- const [mount, setMount] = (0, import_react21.useState)(null);
8842
- (0, import_react21.useEffect)(() => {
9161
+ const [mount, setMount] = (0, import_react22.useState)(null);
9162
+ (0, import_react22.useEffect)(() => {
8843
9163
  const host = document.createElement("div");
8844
9164
  host.setAttribute(HOST_ATTR, "");
8845
9165
  host.style.position = "fixed";
@@ -8875,15 +9195,15 @@ function ShadowHost({ config }) {
8875
9195
  }, []);
8876
9196
  if (!mount) return null;
8877
9197
  return (0, import_react_dom.createPortal)(
8878
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StoreProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(App, {}) }),
9198
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StoreProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(App, {}) }),
8879
9199
  mount
8880
9200
  );
8881
9201
  }
8882
9202
  function ReviewKit(props) {
8883
- const [active2, setActive] = (0, import_react21.useState)(false);
9203
+ const [active2, setActive] = (0, import_react22.useState)(false);
8884
9204
  const { enabled, token, allowedHosts, devAutoOn } = props;
8885
9205
  const hostsKey = allowedHosts?.join("\0") ?? "";
8886
- (0, import_react21.useEffect)(() => {
9206
+ (0, import_react22.useEffect)(() => {
8887
9207
  if (enabled === false) {
8888
9208
  setActive(false);
8889
9209
  return;
@@ -8894,13 +9214,13 @@ function ReviewKit(props) {
8894
9214
  return () => window.removeEventListener(DEACTIVATE_EVENT, onDeactivate);
8895
9215
  }, [enabled, token, devAutoOn, hostsKey]);
8896
9216
  if (enabled === false || !active2) return null;
8897
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ShadowHost, { config: props });
9217
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ShadowHost, { config: props });
8898
9218
  }
8899
- var import_react21, import_react_dom, import_jsx_runtime19;
9219
+ var import_react22, import_react_dom, import_jsx_runtime20;
8900
9220
  var init_ReviewKit = __esm({
8901
9221
  "src/client/ReviewKit.tsx"() {
8902
9222
  "use strict";
8903
- import_react21 = require("react");
9223
+ import_react22 = require("react");
8904
9224
  import_react_dom = require("react-dom");
8905
9225
  init_env();
8906
9226
  init_pointer();
@@ -8908,7 +9228,7 @@ var init_ReviewKit = __esm({
8908
9228
  init_target();
8909
9229
  init_store();
8910
9230
  init_App();
8911
- import_jsx_runtime19 = require("react/jsx-runtime");
9231
+ import_jsx_runtime20 = require("react/jsx-runtime");
8912
9232
  }
8913
9233
  });
8914
9234
 
@@ -8955,19 +9275,19 @@ __export(next_exports, {
8955
9275
  module.exports = __toCommonJS(next_exports);
8956
9276
 
8957
9277
  // src/LazyReviewKit.tsx
8958
- var import_react22 = require("react");
9278
+ var import_react23 = require("react");
8959
9279
  init_activate();
8960
- var import_jsx_runtime20 = require("react/jsx-runtime");
9280
+ var import_jsx_runtime21 = require("react/jsx-runtime");
8961
9281
  var coreModule = null;
8962
9282
  function loadCore() {
8963
9283
  if (!coreModule) coreModule = Promise.resolve().then(() => (init_client(), client_exports)).then((mod) => mod.ReviewKit);
8964
9284
  return coreModule;
8965
9285
  }
8966
9286
  function LazyReviewKit(props) {
8967
- const [Core, setCore] = (0, import_react22.useState)(null);
9287
+ const [Core, setCore] = (0, import_react23.useState)(null);
8968
9288
  const { enabled, token, allowedHosts, devAutoOn } = props;
8969
9289
  const hostsKey = allowedHosts?.join(" ") ?? "";
8970
- (0, import_react22.useEffect)(() => {
9290
+ (0, import_react23.useEffect)(() => {
8971
9291
  if (!shouldActivate({ enabled, token, allowedHosts, devAutoOn })) return;
8972
9292
  let cancelled = false;
8973
9293
  loadCore().then((component) => {
@@ -8980,7 +9300,7 @@ function LazyReviewKit(props) {
8980
9300
  };
8981
9301
  }, [enabled, token, devAutoOn, hostsKey]);
8982
9302
  if (!Core || enabled === false) return null;
8983
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Core, { ...props });
9303
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Core, { ...props });
8984
9304
  }
8985
9305
  // Annotate the CommonJS export names for ESM import in node:
8986
9306
  0 && (module.exports = {