8cb1f9f479
Publish SDK (PyPI) / publish (push) Has been cancelled
Publish SDK (npm) / publish (@aionui/officecli-sdk) (push) Has been cancelled
SDK smoke / smoke (windows-latest) (push) Has been cancelled
Publish SDK (npm) / publish (@officecli/officecli-sdk) (push) Has been cancelled
Publish SDK (npm) / publish (@officecli/sdk) (push) Has been cancelled
Publish SDK (npm) / publish (officecli-sdk) (push) Has been cancelled
SDK smoke / smoke (macos-latest) (push) Has been cancelled
SDK smoke / smoke (ubuntu-latest) (push) Has been cancelled
Skill parity / diff (push) Has been cancelled
483 lines
23 KiB
Bash
Executable File
483 lines
23 KiB
Bash
Executable File
#!/bin/bash
|
|
# Generate a Word tracked-revision showcase document covering every
|
|
# revision element the docx handler supports on this branch.
|
|
#
|
|
# Marker creation (covered):
|
|
# Run scope:
|
|
# * w:ins set + revision.type=ins
|
|
# * w:del set + revision.type=del
|
|
# * w:rPrChange (implicit) set + font.* / bold / ... + revision.author
|
|
# * w:rPrChange (explicit) set + revision.type=format + <prop change>
|
|
# * w:moveFrom / w:moveTo set + revision.type=moveFrom|moveTo + shared revision.id
|
|
# Paragraph scope:
|
|
# * w:ins (paragraph) add paragraph + revision.author
|
|
# * w:del (paragraph) remove paragraph + revision.author
|
|
# * w:pPrChange set paragraph prop (align/indent/...) + revision.author
|
|
# Table scope:
|
|
# * w:tblPrChange set tbl + style/... + revision.author
|
|
# * w:trPrChange set tr + height/header/... + revision.author
|
|
# * w:tcPrChange set tc + shd/borders/... + revision.author
|
|
# (implicitly cascades w:tblPrExChange + w:tblGridChange when grid mutates)
|
|
# * w:trPr/w:ins (rowInsertion) add row + revision.author
|
|
# * w:tcPr/w:cellIns add cell + revision.author
|
|
# * w:tcPr/w:cellDel remove cell + revision.author
|
|
# Section scope:
|
|
# * w:sectPrChange set /body/sectPr[N] + revision.author
|
|
# Bonus:
|
|
# * Default author (revision.author="" -> "OfficeCLI")
|
|
# * Auto-allocated revision.id (omit; comes from shared paraId pool)
|
|
# * Explicit revision.id (required for move pair; allowed everywhere)
|
|
#
|
|
# Action verb demo (on a temp copy at the bottom):
|
|
# * /revision[@author=NAME] accept/reject by author
|
|
# * /revision[@type=ins|del|...] accept/reject by type
|
|
# * /revision[@id=N] accept/reject by stable id
|
|
# * /revision[@id=N][@type=moveTo] single-end of a move pair
|
|
# * native path (/body/p[N]/ins[M] ...) accept/reject in DOM terms
|
|
# * /revision accept-all / reject-all (terminal sweep)
|
|
|
|
# NOTE: intentionally NO `set -e`. Like the SDK twin's doc.batch, this script
|
|
# tolerates forward-compat 'UNSUPPORTED props' warnings (officecli exit 2) and
|
|
# keeps building so the full document is produced.
|
|
DIR="$(dirname "$0")"
|
|
DOCX="$DIR/revisions.docx"
|
|
|
|
echo "=========================================="
|
|
echo "Generating tracked-revision showcase: $DOCX"
|
|
echo "=========================================="
|
|
|
|
rm -f "$DOCX"
|
|
officecli create "$DOCX"
|
|
officecli open "$DOCX"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Paragraph index map (1-based; counts EVERY body paragraph). Comments inline
|
|
# below show what each set targets so the indices stay correct as the doc
|
|
# grows. Tracked-delete keeps the paragraph element in place (wrapped in
|
|
# w:del), so subsequent indices do NOT shift after step 2b.
|
|
# --------------------------------------------------------------------------
|
|
|
|
# p[1] title, p[2] spacer
|
|
officecli add "$DOCX" /body --type paragraph --prop text="Revision API — Full Coverage" --prop style=Heading1 --prop align=center
|
|
officecli add "$DOCX" /body --type paragraph --prop text=""
|
|
|
|
# ==========================================================================
|
|
# Section 1 — Run-level edits.
|
|
# p[3] H2, p[4]=ins target, p[5]=del target,
|
|
# p[6]=implicit-format target, p[7]=explicit-format target
|
|
# ==========================================================================
|
|
echo " -> Section 1: run-level edits (ins / del / rPrChange implicit + explicit)"
|
|
officecli add "$DOCX" /body --type paragraph --prop text="1. Run-level edits" --prop style=Heading2
|
|
officecli add "$DOCX" /body --type paragraph --prop text="This run will be marked as an INSERTION."
|
|
officecli add "$DOCX" /body --type paragraph --prop text="This run will be marked as a DELETION."
|
|
officecli add "$DOCX" /body --type paragraph --prop text="This run keeps the text and gets an IMPLICIT format change (font.color)."
|
|
officecli add "$DOCX" /body --type paragraph --prop text="This run gets an EXPLICIT revision.type=format with italic toggle."
|
|
|
|
# 1a. w:ins around the run.
|
|
officecli set "$DOCX" '/body/p[4]/r[1]' \
|
|
--prop revision.type=ins \
|
|
--prop revision.author=Alice \
|
|
--prop revision.date=2026-05-25T10:00:00Z
|
|
|
|
# 1b. w:del around the run (text becomes w:delText).
|
|
officecli set "$DOCX" '/body/p[5]/r[1]' \
|
|
--prop revision.type=del \
|
|
--prop revision.author=Bob \
|
|
--prop revision.date=2026-05-25T10:05:00Z
|
|
|
|
# 1c. Implicit format change — any font.* prop + revision.author captures the
|
|
# previous rPr in w:rPrChange. Most natural form.
|
|
officecli set "$DOCX" '/body/p[6]/r[1]' \
|
|
--prop font.color=C00000 \
|
|
--prop bold=true \
|
|
--prop revision.author=Carol \
|
|
--prop revision.date=2026-05-25T10:10:00Z
|
|
|
|
# 1d. Explicit revision.type=format. Still needs a real property change
|
|
# alongside (empty rPrChange records nothing, so the handler errors out).
|
|
officecli set "$DOCX" '/body/p[7]/r[1]' \
|
|
--prop revision.type=format \
|
|
--prop italic=true \
|
|
--prop revision.author=Carol \
|
|
--prop revision.date=2026-05-25T10:11:00Z
|
|
|
|
# ==========================================================================
|
|
# Section 2 — Paragraph-level edits.
|
|
# p[8] H2
|
|
# p[9] whole-paragraph tracked insertion (add + revision.author)
|
|
# p[10] plain paragraph that becomes a tracked deletion (remove + ...)
|
|
# p[11] paragraph that gets a pPrChange (set align + revision.author)
|
|
# ==========================================================================
|
|
echo " -> Section 2: paragraph-level edits (ins / del / pPrChange)"
|
|
officecli add "$DOCX" /body --type paragraph --prop text="2. Paragraph-level edits" --prop style=Heading2
|
|
|
|
# 2a. w:ins around the entire paragraph (plus paragraphMarkInsertion on the ¶).
|
|
officecli add "$DOCX" /body --type paragraph \
|
|
--prop text="This whole paragraph was inserted by Alice as a tracked change." \
|
|
--prop revision.author=Alice \
|
|
--prop revision.date=2026-05-25T10:15:00Z
|
|
|
|
# 2b. w:del around the entire paragraph (plus paragraphMarkDeletion).
|
|
# remove + revision.author KEEPS the element (wraps it); it does not drop it.
|
|
officecli add "$DOCX" /body --type paragraph --prop text="This whole paragraph will be tracked-deleted by Bob."
|
|
officecli remove "$DOCX" '/body/p[10]' \
|
|
--prop revision.author=Bob \
|
|
--prop revision.date=2026-05-25T10:20:00Z
|
|
|
|
# 2c. pPrChange — set a paragraph-level property (alignment here) + revision.author.
|
|
# Surfaces in query as revision.type=paragraph.
|
|
officecli add "$DOCX" /body --type paragraph --prop text="This paragraph had alignment changed (pPrChange) by Carol."
|
|
officecli set "$DOCX" '/body/p[11]' \
|
|
--prop align=center \
|
|
--prop revision.author=Carol \
|
|
--prop revision.date=2026-05-25T10:21:00Z
|
|
|
|
# ==========================================================================
|
|
# Section 3 — Paired move (shared revision.id binds the two halves).
|
|
# p[12] H2, p[13]=moveFrom source, p[14]=moveTo destination
|
|
# ==========================================================================
|
|
echo " -> Section 3: paired move (moveFrom + moveTo, shared id)"
|
|
officecli add "$DOCX" /body --type paragraph --prop text="3. Moved content" --prop style=Heading2
|
|
officecli add "$DOCX" /body --type paragraph --prop text="Source: this sentence is being relocated."
|
|
officecli add "$DOCX" /body --type paragraph --prop text="Destination: it will land here in its new home."
|
|
|
|
# revision.id MUST be supplied (and equal) for the two halves to pair.
|
|
officecli set "$DOCX" '/body/p[13]/r[1]' \
|
|
--prop revision.type=moveFrom \
|
|
--prop revision.author=Alice \
|
|
--prop revision.date=2026-05-25T10:25:00Z \
|
|
--prop revision.id=500
|
|
officecli set "$DOCX" '/body/p[14]/r[1]' \
|
|
--prop revision.type=moveTo \
|
|
--prop revision.author=Alice \
|
|
--prop revision.date=2026-05-25T10:25:00Z \
|
|
--prop revision.id=500
|
|
|
|
# ==========================================================================
|
|
# Section 4 — Table-scope revisions (all five table elements).
|
|
# p[15] H2, tbl[1] = 3 rows x 3 cols seed.
|
|
# Order of operations is chosen so per-row/per-cell indices stay correct.
|
|
# ==========================================================================
|
|
echo " -> Section 4: table scope (tblPrChange + trPrChange + tcPrChange + row/cell ins/del)"
|
|
officecli add "$DOCX" /body --type paragraph --prop text="4. Table-scope revisions" --prop style=Heading2
|
|
officecli add "$DOCX" /body --type table --prop rows=3 --prop cols=3
|
|
|
|
# Seed content
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[1]/tc[1]' --prop text="Header A" --prop bold=true
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[1]/tc[2]' --prop text="Header B" --prop bold=true
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[1]/tc[3]' --prop text="Header C" --prop bold=true
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[2]/tc[1]' --prop text="row2 a"
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[2]/tc[2]' --prop text="row2 b (shading change)"
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[2]/tc[3]' --prop text="row2 c"
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[3]/tc[1]' --prop text="row3 a (cell delete)"
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[3]/tc[2]' --prop text="row3 b"
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[3]/tc[3]' --prop text="row3 c"
|
|
|
|
# 4a. tblPrChange — table-level property change.
|
|
officecli set "$DOCX" '/body/tbl[1]' \
|
|
--prop style=TableGrid \
|
|
--prop revision.author=Dan \
|
|
--prop revision.date=2026-05-25T10:30:00Z
|
|
|
|
# 4b. trPrChange — row-level property change (row height).
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[1]' \
|
|
--prop height=600 \
|
|
--prop revision.author=Dan \
|
|
--prop revision.date=2026-05-25T10:31:00Z
|
|
|
|
# 4c. tcPrChange — cell-level property change (shading).
|
|
# Cascades tblPrExChange / tblGridChange automatically when needed.
|
|
officecli set "$DOCX" '/body/tbl[1]/tr[2]/tc[2]' \
|
|
--prop shd=FFE699 \
|
|
--prop revision.author=Dan \
|
|
--prop revision.date=2026-05-25T10:32:00Z
|
|
|
|
# 4d. Cell insertion — add a 4th cell to row 2.
|
|
officecli add "$DOCX" '/body/tbl[1]/tr[2]' --type cell \
|
|
--prop text="row2 d (inserted)" \
|
|
--prop revision.author=Eve \
|
|
--prop revision.date=2026-05-25T10:33:00Z
|
|
|
|
# 4e. Cell deletion — drop cell 1 of row 3 (tracked, not destructive).
|
|
officecli remove "$DOCX" '/body/tbl[1]/tr[3]/tc[1]' \
|
|
--prop revision.author=Eve \
|
|
--prop revision.date=2026-05-25T10:34:00Z
|
|
|
|
# 4f. Row insertion — append a row at the table tail; whole row marked inserted.
|
|
officecli add "$DOCX" '/body/tbl[1]' --type row \
|
|
--prop revision.author=Eve \
|
|
--prop revision.date=2026-05-25T10:35:00Z
|
|
|
|
# ==========================================================================
|
|
# Section 5 — Section properties (sectPrChange).
|
|
# The body's section properties live at /body/sectPr[1] (NOT /body/sect[1]).
|
|
# ==========================================================================
|
|
echo " -> Section 5: section properties (sectPrChange)"
|
|
officecli add "$DOCX" /body --type paragraph --prop text="5. Section properties" --prop style=Heading2
|
|
officecli add "$DOCX" /body --type paragraph --prop text="The body sectPr below got a tracked pageWidth change."
|
|
|
|
officecli set "$DOCX" '/body/sectPr[1]' \
|
|
--prop pageWidth=11906 \
|
|
--prop revision.author=Frank \
|
|
--prop revision.date=2026-05-25T10:40:00Z
|
|
|
|
# ==========================================================================
|
|
# Section 6 — Defaults & explicit-id (bonus).
|
|
# ==========================================================================
|
|
echo " -> Section 6: default author + auto-allocated id"
|
|
officecli add "$DOCX" /body --type paragraph --prop text="6. Defaults" --prop style=Heading2
|
|
|
|
# 6a. Empty revision.author -> falls back to "OfficeCLI".
|
|
# `add + revision.author=""` silently produces an untracked paragraph
|
|
# (empty author = "no revision"); the default-author fallback fires on
|
|
# the `set` path. So we add the paragraph plain, then `set` it with an
|
|
# empty author to demonstrate the fallback.
|
|
officecli add "$DOCX" /body --type paragraph \
|
|
--prop text="This run was wrapped via set with revision.author=\"\" (defaults to OfficeCLI)."
|
|
officecli set "$DOCX" '/body/p[19]/r[1]' \
|
|
--prop revision.type=ins \
|
|
--prop revision.author="" \
|
|
--prop revision.date=2026-05-25T10:44:00Z
|
|
|
|
# 6b. Explicit revision.id outside of a move pair — accepted, you control the
|
|
# w:id attribute. Useful when post-processing needs a deterministic id.
|
|
officecli add "$DOCX" /body \
|
|
--type paragraph \
|
|
--prop text="This paragraph carries an explicit revision.id=9001." \
|
|
--prop revision.author=Grace \
|
|
--prop revision.date=2026-05-25T10:45:00Z \
|
|
--prop revision.id=9001
|
|
|
|
# ==========================================================================
|
|
# Section 7 — Find + Replace combined with revision tracking.
|
|
# Mirrors Word's Find&Replace dialog with Track Changes ON: every match is
|
|
# wrapped in the marker shape inferred from the props you pass alongside.
|
|
# The handler auto-allocates a fresh revision.id per marker (one w:del per
|
|
# matched run + one w:ins for the replacement, or one w:rPrChange per match,
|
|
# etc.), so `--prop revision.id=…` is rejected on find — would collide.
|
|
# ==========================================================================
|
|
echo " -> Section 7: find + revision (Word-style Find&Replace with Track Changes)"
|
|
officecli add "$DOCX" /body --type paragraph --prop text="7. Find + Replace + Revision" --prop style=Heading2
|
|
|
|
# Helper: add a paragraph and echo the path the handler assigned (e.g.
|
|
# /body/p[@paraId=00100012]) so subsequent `set --find …` calls don't
|
|
# need to hand-count positional indices. The handler-assigned paraId path
|
|
# is stable across content shifts in the body, unlike /body/p[N] which
|
|
# drifts every time the section count above changes.
|
|
add_para_capture() {
|
|
officecli add "$DOCX" /body --type paragraph --prop text="$1" 2>&1 \
|
|
| grep -oE '/body/p\[@paraId=[A-F0-9]+\]' | tail -1
|
|
}
|
|
|
|
# 7a. find + replace + revision via REGEX — track only the FIRST occurrence
|
|
# of "fox", leave subsequent ones alone. The bare `--find fox` would match
|
|
# every occurrence; controlling which match to track is a job for the
|
|
# regex pattern.
|
|
#
|
|
# Pattern: (?<!fox.*)fox
|
|
# Reading: match "fox" only when NOT preceded by another "fox" on the
|
|
# same line. The .NET regex engine supports variable-width
|
|
# negative lookbehind, so this works without contortions.
|
|
# Effect on this paragraph (two "fox" tokens):
|
|
# The fox jumped and another fox ran fast.
|
|
# ^^^ ^^^
|
|
# tracked: del+ins untouched
|
|
P7A=$(add_para_capture "7a. The fox jumped and another fox ran fast. (regex tracks only the 1st 'fox'→'cat')")
|
|
officecli set "$DOCX" "$P7A" \
|
|
--find '(?<!fox.*)fox' \
|
|
--prop regex=true \
|
|
--replace cat \
|
|
--prop revision.author=Iris \
|
|
--prop revision.date=2026-05-25T10:50:00Z
|
|
|
|
# 7b. find + format + revision — one w:rPrChange per matched run. The matched
|
|
# text keeps its content; each match becomes a tracked format change.
|
|
P7B=$(add_para_capture "7b. Color red apples and the red barn. (tracked bold on every 'red')")
|
|
officecli set "$DOCX" "$P7B" \
|
|
--find red \
|
|
--prop bold=true \
|
|
--prop revision.author=Jack \
|
|
--prop revision.date=2026-05-25T10:51:00Z
|
|
|
|
# 7c. find + replace + format + revision — the inserted run inherits the
|
|
# original rPr from the matched text AND has the new format layered on.
|
|
P7C=$(add_para_capture "7c. Replace bar with FOO. (find target → bold-green replacement)")
|
|
officecli set "$DOCX" "$P7C" \
|
|
--find bar \
|
|
--replace BAZ \
|
|
--prop bold=true \
|
|
--prop font.color=00B050 \
|
|
--prop revision.author=Kelly \
|
|
--prop revision.date=2026-05-25T10:52:00Z
|
|
|
|
# 7d. find + regex + revision — multiple matches each get their own marker.
|
|
P7D=$(add_para_capture "7d. Prices: \$100, \$250, \$999 (regex \\\$\\d+ → tracked bold)")
|
|
officecli set "$DOCX" "$P7D" \
|
|
--find '\$\d+' \
|
|
--prop regex=true \
|
|
--prop bold=true \
|
|
--prop revision.author=Liam \
|
|
--prop revision.date=2026-05-25T10:53:00Z
|
|
|
|
# ==========================================================================
|
|
# Section 8 — Less-common find + revision variants (rounds out Section 7).
|
|
# 8a covers `find + replace=""` — a pure tracked deletion via find (one
|
|
# w:del per matched run, no w:ins). 8b covers `find + paragraph property`
|
|
# — paragraph-scope mutation captured as w:pPrChange instead of run-scope
|
|
# w:rPrChange.
|
|
# ==========================================================================
|
|
echo " -> Section 8: find variants (replace='' delete-only + paragraph prop pPrChange)"
|
|
officecli add "$DOCX" /body --type paragraph --prop text="8. Find variants" --prop style=Heading2
|
|
|
|
# 8a. find + replace="" + revision — tracked DELETION of every match, no insertion.
|
|
# Useful for "scrub this token from the doc but keep an audit trail".
|
|
P8A=$(add_para_capture "8a. Remove the OBSOLETE token here. (delete-only via find — no insertion)")
|
|
officecli set "$DOCX" "$P8A" \
|
|
--find OBSOLETE \
|
|
--replace "" \
|
|
--prop revision.author=Mira \
|
|
--prop revision.date=2026-05-25T10:54:00Z
|
|
|
|
# 8b. find + paragraph prop + revision — one w:pPrChange per matched paragraph.
|
|
# Same code path as `set /body/p[N] --prop align=… --prop revision.author=…`,
|
|
# but filtered to ONLY the paragraphs whose text actually matched the find.
|
|
P8B=$(add_para_capture "8b. This paragraph contains MARK so its alignment gets tracked-centered.")
|
|
officecli set "$DOCX" "$P8B" \
|
|
--find MARK \
|
|
--prop align=center \
|
|
--prop revision.author=Nora \
|
|
--prop revision.date=2026-05-25T10:55:00Z
|
|
|
|
officecli close "$DOCX"
|
|
|
|
# ==========================================================================
|
|
# Inspection — list every revision marker in the shipped file.
|
|
# ==========================================================================
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "All revisions in $DOCX:"
|
|
echo "=========================================="
|
|
officecli query "$DOCX" revision
|
|
|
|
# ==========================================================================
|
|
# Action verbs — runs on a TEMP COPY so the shipped artifact keeps every
|
|
# marker intact for inspection in Word.
|
|
# ==========================================================================
|
|
DEMO="$(mktemp -t revisions-demo.XXXXXX).docx"
|
|
cp "$DOCX" "$DEMO"
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "Accept/reject demo on temp copy:"
|
|
echo " $DEMO"
|
|
echo "=========================================="
|
|
|
|
# A. Single-end addressing of a move pair: `/revision[@id=N][@type=…]`
|
|
# addresses ONE half of a shared-id pair. Section 3 created moveFrom +
|
|
# moveTo at id=500; reject only the moveTo half here (the moveFrom
|
|
# survives — useful when reviewing decides "keep the deletion at source,
|
|
# discard the insertion at destination"). Done BEFORE step B because B
|
|
# accepts everything Alice authored, including this move pair.
|
|
echo " A) single-end move reject: /revision[@id=500][@type=moveTo]"
|
|
officecli set "$DEMO" '/revision[@id=500][@type=moveTo]' --prop revision.action=reject 2>&1 | tail -1
|
|
SURVIVING_MOVE=$(officecli query "$DEMO" revision --json 2>/dev/null | python3 -c "
|
|
import sys, json
|
|
d = json.load(sys.stdin)
|
|
for r in d['data']['results']:
|
|
if r.get('format',{}).get('revision.id') == '500':
|
|
print(r['format'].get('revision.type'))
|
|
")
|
|
echo " surviving half of id=500: ${SURVIVING_MOVE:-none} (expected: moveFrom)"
|
|
|
|
# B. Accept everything Alice authored.
|
|
echo " B) accept by author: /revision[@author=Alice]"
|
|
officecli set "$DEMO" '/revision[@author=Alice]' --prop revision.action=accept
|
|
|
|
# C. Reject every w:del-typed revision still left.
|
|
echo " C) reject by type: /revision[@type=del]"
|
|
officecli set "$DEMO" '/revision[@type=del]' --prop revision.action=reject
|
|
|
|
# D. Accept Carol's explicit-format change by its stable id.
|
|
CAROL_FMT_ID=$(officecli query "$DEMO" revision --json 2>/dev/null | python3 -c "
|
|
import sys, json
|
|
d = json.load(sys.stdin)
|
|
for r in d['data']['results']:
|
|
f = r.get('format', {})
|
|
if f.get('revision.author') == 'Carol' and f.get('revision.type') == 'formatChange':
|
|
print(f['revision.id']); break
|
|
")
|
|
if [ -n "$CAROL_FMT_ID" ]; then
|
|
echo " D) accept by stable id: /revision[@id=$CAROL_FMT_ID]"
|
|
officecli set "$DEMO" "/revision[@id=$CAROL_FMT_ID]" --prop revision.action=accept
|
|
fi
|
|
|
|
# E. Accept a marker via its native DOM path. Pick the first surviving marker
|
|
# after steps A-D and feed its nativePath back to `set --prop revision.action=accept`.
|
|
NATIVE_PATH=$(officecli query "$DEMO" revision --json 2>/dev/null | python3 -c "
|
|
import sys, json
|
|
d = json.load(sys.stdin)
|
|
for r in d['data']['results']:
|
|
np = r.get('format', {}).get('revision.nativePath','')
|
|
if np:
|
|
print(np); break
|
|
")
|
|
if [ -n "$NATIVE_PATH" ]; then
|
|
echo " E) accept by native path: $NATIVE_PATH"
|
|
officecli set "$DEMO" "$NATIVE_PATH" --prop revision.action=accept
|
|
fi
|
|
|
|
# F. Sweep — reject everything still pending.
|
|
echo " F) terminal sweep: /revision (reject-all)"
|
|
officecli set "$DEMO" /revision --prop revision.action=reject
|
|
|
|
REMAINING=$(officecli query "$DEMO" revision 2>&1 | grep -c "^/revision" || true)
|
|
echo " remaining markers after sweep: $REMAINING (expected: 0)"
|
|
|
|
rm -f "$DEMO"
|
|
|
|
# ==========================================================================
|
|
# Read-side capabilities — agent-friendly JSON envelope, plain-text render,
|
|
# dump→batch round-trip (revision keys survive serialization, so a doc with
|
|
# tracked changes can be regenerated end-to-end via `batch --input`).
|
|
# ==========================================================================
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "Read-side capabilities on $DOCX:"
|
|
echo "=========================================="
|
|
|
|
echo " i) query revision --json (first 3 markers, agent-consumable):"
|
|
officecli query "$DOCX" revision --json 2>/dev/null | python3 -c "
|
|
import sys, json
|
|
d = json.load(sys.stdin)
|
|
print(f' matches={d[\"data\"][\"matches\"]}')
|
|
for r in d['data']['results'][:3]:
|
|
f = r['format']
|
|
print(f' path={r[\"path\"]} type={f.get(\"revision.type\")} author={f.get(\"revision.author\")} text={repr(r.get(\"text\",\"\"))[:40]}')
|
|
"
|
|
|
|
echo ""
|
|
echo " ii) view text — runs/paragraphs render with their current content"
|
|
echo " (inserted text shows, deleted text is suppressed; cf. Word's"
|
|
echo " 'All Markup' vs 'No Markup' view):"
|
|
officecli view "$DOCX" text 2>&1 | head -10 | sed 's/^/ /'
|
|
|
|
echo ""
|
|
echo " iii) dump --format batch round-trip — revision creation keys survive"
|
|
echo " serialization so a tracked-change doc regenerates end-to-end via"
|
|
echo " 'batch --input <dump.json>'. Sample of revision keys in dump:"
|
|
officecli dump "$DOCX" / --format batch 2>/dev/null \
|
|
| python3 -c "
|
|
import sys, json
|
|
batch = json.load(sys.stdin)
|
|
rev_steps = [s for s in batch
|
|
if any(k.startswith('revision.') for k in (s.get('props') or {}))]
|
|
print(f' total batch steps: {len(batch)}, steps carrying revision.* props: {len(rev_steps)}')
|
|
for s in rev_steps[:3]:
|
|
keys = [k for k in s['props'] if k.startswith('revision.')]
|
|
print(f' {s[\"command\"]:>6} {s.get(\"path\",s.get(\"parent\",\"\")):40} keys={keys}')
|
|
"
|
|
|
|
echo ""
|
|
echo "Done: $DOCX"
|
|
ls -lh "$DOCX"
|