overleaf-review 0.6.0 → 0.6.1

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/README.md CHANGED
@@ -212,6 +212,13 @@ Grouping applies to new pushes; it does not consolidate or accept suggestions al
212
212
  ### Holistic paragraph rewrites over reviewed suggestions and comments
213
213
 
214
214
  Use this when a paragraph rewrite intentionally incorporates or supersedes existing suggestions.
215
+
216
+ To regroup existing fragmented suggestions **without changing their proposed wording**, set
217
+ `before` and `after` to the same paragraph text and explicitly list the suggestion IDs in
218
+ `supersedes`. Keep the local file unchanged for that block. This is supported from v0.6.1;
219
+ the proposal must still differ from the underlying text obtained by rejecting those suggestions.
220
+ Affected comments still require explicit anchor mappings, and all stale-plan and verification
221
+ checks still apply. Ordinary replacements without selected suggestions cannot be no-ops.
215
222
  It differs from consolidation: the proposed text may change, and selected older suggestion cards
216
223
  are replaced by a new pending revision under the authenticated account.
217
224
 
@@ -924,11 +924,11 @@ function buildReviewEdits(source, target, options = {}) {
924
924
  }
925
925
  return groups;
926
926
  }
927
- function validateExplicitEdits(source, value) {
927
+ function validateExplicitEdits(source, value, validation = {}) {
928
928
  if (!Array.isArray(value) || !value.length) throw new Error("Explicit blocks must be a nonempty array.");
929
929
  let previousEnd = -1;
930
930
  return value.map((edit) => {
931
- if (!edit || !Number.isSafeInteger(edit.start) || !Number.isSafeInteger(edit.end) || edit.start < 0 || edit.end < edit.start || edit.end > source.length || typeof edit.text !== "string" || edit.start <= previousEnd || source.slice(edit.start, edit.end) === edit.text) {
931
+ if (!edit || !Number.isSafeInteger(edit.start) || !Number.isSafeInteger(edit.end) || edit.start < 0 || edit.end < edit.start || edit.end > source.length || typeof edit.text !== "string" || edit.start <= previousEnd || !validation.allowUnchanged && source.slice(edit.start, edit.end) === edit.text) {
932
932
  throw new Error("Explicit blocks must be valid, ordered, separated replacements with a text change.");
933
933
  }
934
934
  previousEnd = edit.end;
@@ -941,7 +941,7 @@ function applyTextEdits(source, edits) {
941
941
  }
942
942
  return source;
943
943
  }
944
- function parseReplacementManifest(base, value) {
944
+ function parseReplacementManifest(base, value, validation = {}) {
945
945
  const manifest = value;
946
946
  if (!Array.isArray(manifest?.replacements) || !manifest.replacements.length) {
947
947
  throw new Error("Replacement manifest requires a nonempty replacements array.");
@@ -962,10 +962,10 @@ function parseReplacementManifest(base, value) {
962
962
  if (start === void 0) throw new Error("Replacement occurrence is absent from the saved base.");
963
963
  return { start, end: start + block.before.length, text: block.after };
964
964
  }).sort((a, b) => a.start - b.start);
965
- return validateExplicitEdits(base, edits);
965
+ return validateExplicitEdits(base, edits, validation);
966
966
  }
967
- function bindExplicitEdits(base, local, live, edits, options) {
968
- const checked = validateExplicitEdits(base, edits);
967
+ function bindExplicitEdits(base, local, live, edits, options, validation = {}) {
968
+ const checked = validateExplicitEdits(base, edits, validation);
969
969
  if (applyTextEdits(base, checked) !== local) {
970
970
  throw new Error("Replacement manifest must describe every local change exactly.");
971
971
  }
@@ -2299,7 +2299,7 @@ ${doc.localPath} \u2192 ${doc.docPath} (v${doc.liveVersion}): ${doc.ops.length}
2299
2299
  }
2300
2300
  async function push(opts) {
2301
2301
  validatePushOptions(opts);
2302
- const { isReviewedManifest, createReviewedFilePlan, applyReviewedFilePlan, REVIEWED_FILE_KIND } = await import("./reviewed-replace-A7C4UNJS.js");
2302
+ const { isReviewedManifest, createReviewedFilePlan, applyReviewedFilePlan, REVIEWED_FILE_KIND } = await import("./reviewed-replace-4PCULETF.js");
2303
2303
  if (opts.edits && isReviewedManifest(JSON.parse(readFileSync6(workspaceReadPath(opts.edits), "utf8")))) {
2304
2304
  await createReviewedFilePlan(opts);
2305
2305
  return;
package/dist/cli.js CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  uploadFile,
40
40
  validateSession,
41
41
  workspaceWritePath
42
- } from "./chunk-TWIDV42A.js";
42
+ } from "./chunk-JRXROCS6.js";
43
43
 
44
44
  // src/commands/pull.ts
45
45
  import { writeFileSync, mkdirSync } from "fs";
@@ -34,7 +34,7 @@ import {
34
34
  workspaceRelativePath,
35
35
  workspaceWritePath,
36
36
  writeJsonAtomic
37
- } from "./chunk-TWIDV42A.js";
37
+ } from "./chunk-JRXROCS6.js";
38
38
 
39
39
  // src/commands/reviewed-replace.ts
40
40
  import { readFileSync } from "fs";
@@ -315,10 +315,13 @@ function resolveReviewedBlocks(base, local, live, value) {
315
315
  const manifest = value;
316
316
  if (!Array.isArray(manifest?.replacements) || !manifest.replacements.length) throw new Error("Reviewed manifest requires replacements.");
317
317
  const resolved = manifest.replacements.map((block) => {
318
- const [edit] = parseReplacementManifest(base, { replacements: [{ before: block.before, after: block.after, occurrence: block.occurrence }] });
318
+ const allowUnchanged = Array.isArray(block.supersedes) && block.supersedes.length > 0;
319
+ const [edit] = parseReplacementManifest(base, {
320
+ replacements: [{ before: block.before, after: block.after, occurrence: block.occurrence }]
321
+ }, { allowUnchanged });
319
322
  return { ...edit, supersedes: block.supersedes ?? [], reason: block.reason, comments: block.comments ?? [] };
320
323
  }).sort((a, b) => a.start - b.start);
321
- const mapped = bindExplicitEdits(base, local, live, resolved, {}).explicitEdits;
324
+ const mapped = bindExplicitEdits(base, local, live, resolved, {}, { allowUnchanged: true }).explicitEdits;
322
325
  return resolved.map((block, index) => ({ ...block, ...mapped[index] }));
323
326
  }
324
327
  async function createReviewedFilePlan(opts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overleaf-review",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "The missing review layer for Overleaf's Git bridge — sync comments and tracked changes between Overleaf and your local repo.",
5
5
  "type": "module",
6
6
  "license": "MIT",