@a3s-lab/office 0.4.0 → 0.6.0
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/COLLABORATION_ROADMAP.md +51 -15
- package/README.md +33 -4
- package/dist/4560.js +167 -14
- package/dist/internal/collaboration/office-presentation-collaboration-claims.d.ts +5 -0
- package/dist/internal/collaboration/office-presentation-collaboration-model.d.ts +2 -0
- package/dist/internal/collaboration/office-presentation-collaboration-records.d.ts +3 -2
- package/dist/office-kernel.wasm +0 -0
- package/package.json +7 -3
package/COLLABORATION_ROADMAP.md
CHANGED
|
@@ -150,12 +150,17 @@ round trip preserves supported and untouched unsupported OOXML.
|
|
|
150
150
|
|
|
151
151
|
### Phase 3: Presentation
|
|
152
152
|
|
|
153
|
-
Status: browser collaboration foundation
|
|
153
|
+
Status: browser collaboration foundation and native conflict-local scene-element
|
|
154
|
+
mutations implemented; rich-text CRDT, structural native operations, and PPTX
|
|
154
155
|
round-trip concurrency coverage are pending.
|
|
155
156
|
|
|
156
157
|
- Stable slide/master/layout order arrays and ID-keyed record maps avoid a
|
|
157
158
|
serialized whole-deck root. Scene elements and slide comments are ID-keyed
|
|
158
159
|
child records.
|
|
160
|
+
- Append-only element claims make identical same-ID creation idempotent and
|
|
161
|
+
reject different records assigned to one ID. Deletion writes a durable
|
|
162
|
+
tombstone, removes the element from visible order, and permanently reserves
|
|
163
|
+
the identity; element ID and type cannot drift.
|
|
159
164
|
- Snapshot writes are translated into `previous -> next` record patches, so an
|
|
160
165
|
unrelated stale snapshot does not remove remotely added slides or objects.
|
|
161
166
|
- Typed validation rejects duplicate identities and invalid references before
|
|
@@ -166,13 +171,21 @@ round-trip concurrency coverage are pending.
|
|
|
166
171
|
- Tests cover bootstrap, identity validation, separate-record convergence,
|
|
167
172
|
stale snapshots, concurrent comments, local-only undo, permissions, mounted
|
|
168
173
|
projection, and framework adapters.
|
|
174
|
+
- Rust, CLI, MCP, and A3S Code can create, optimistically update, or tombstone
|
|
175
|
+
one element in a slide, master, or layout. Creation can insert after a stable
|
|
176
|
+
active element. Update compares complete expected/current/next records and
|
|
177
|
+
writes only changed top-level fields, so unrelated concurrent changes merge
|
|
178
|
+
while a stale same-field edit fails atomically. Delete requires an exact
|
|
179
|
+
complete-element match. Browser/Yrs fixtures, native restart, duplicate and
|
|
180
|
+
reordered delivery, real CLI/MCP subprocesses, and the Playground cover the
|
|
181
|
+
same lifecycle.
|
|
169
182
|
|
|
170
183
|
Remaining:
|
|
171
184
|
|
|
172
185
|
- Bind editable scene text to collaborative XML fragments instead of scalar
|
|
173
186
|
text/run replacement.
|
|
174
|
-
- Add
|
|
175
|
-
|
|
187
|
+
- Add structural native operations and explicit conflict handling for slide
|
|
188
|
+
order, z-order, grouping, and theme changes.
|
|
176
189
|
- Keep derived thumbnails and layout measurements local and prove this in UI
|
|
177
190
|
tests.
|
|
178
191
|
- Add offline/reordered-update property tests plus merged PPTX export/reopen.
|
|
@@ -182,8 +195,9 @@ comments, and delete-vs-edit cases converge and survive PPTX round trips.
|
|
|
182
195
|
|
|
183
196
|
### Phase 4: Spreadsheet
|
|
184
197
|
|
|
185
|
-
Status: browser collaboration foundation
|
|
186
|
-
native recalculation parity, and
|
|
198
|
+
Status: browser collaboration foundation and native conflict-local cell
|
|
199
|
+
mutations implemented; structural operations, native recalculation parity, and
|
|
200
|
+
XLSX concurrency coverage are pending.
|
|
187
201
|
|
|
188
202
|
- Stable sheet/named-range order arrays, ID-keyed records, and append-only
|
|
189
203
|
creation claims avoid a serialized workbook or dense worksheet root.
|
|
@@ -208,6 +222,14 @@ native recalculation parity, and XLSX concurrency coverage are pending.
|
|
|
208
222
|
route canonical edits plus local-only undo/redo through the same binding.
|
|
209
223
|
Active-sheet status, selections, zoom, and Fortune Sheet focus remain local
|
|
210
224
|
view state and are reapplied after remote canonical updates.
|
|
225
|
+
- Rust, CLI, MCP, and A3S Code can create or recursively patch one cell with
|
|
226
|
+
`spreadsheet-set-cell`, or delete one exact complete cell with
|
|
227
|
+
`spreadsheet-delete-cell`. Recursive optimistic guards merge unrelated JSON
|
|
228
|
+
leaves and reject stale same-leaf edits before writing. Dense projections
|
|
229
|
+
retain and safely extend row lengths, sparse projections remain `celldata`,
|
|
230
|
+
and an empty sheet's first write stays sparse. Native restart, real CLI/MCP
|
|
231
|
+
subprocesses, and browser Yjs duplicate/reordered delivery cover the same
|
|
232
|
+
update contract.
|
|
211
233
|
|
|
212
234
|
Remaining:
|
|
213
235
|
|
|
@@ -313,9 +335,10 @@ destructive actions remain attributable, reviewable, and non-retryable.
|
|
|
313
335
|
|
|
314
336
|
Status: native Yrs replica store, resumable CLI/MCP event streams, `a3s code`
|
|
315
337
|
projection, a host-injected live CLI transport session, and typed Markdown,
|
|
316
|
-
Document,
|
|
317
|
-
|
|
318
|
-
|
|
338
|
+
Document, Spreadsheet cell, Presentation scene-element, PDF
|
|
339
|
+
annotation/form-value, and PDF redaction/page-operation review mutation
|
|
340
|
+
surfaces are implemented; the remaining format mutations and native presence
|
|
341
|
+
projection are pending.
|
|
319
342
|
|
|
320
343
|
- Yrs `0.27.3` now uses 53-bit Yjs-compatible client IDs and exchanges standard
|
|
321
344
|
Yjs v1 updates, state vectors, and y-sync `SyncStep1`/`Update` messages with
|
|
@@ -361,6 +384,16 @@ presence projection are pending.
|
|
|
361
384
|
incremental updates through the same durable
|
|
362
385
|
receipt/checkpoint path. Canonical typed mutations require `edit`; raw remote
|
|
363
386
|
updates remain receivable in every mode so read-only peers still converge.
|
|
387
|
+
- Spreadsheet cell mutations create or recursively patch one zero-based
|
|
388
|
+
coordinate after matching the caller's observed cell, or delete it only after
|
|
389
|
+
an exact complete-cell match. They preserve the browser's field-addressed
|
|
390
|
+
representation, dense/sparse projection mode, and atomic fail-closed
|
|
391
|
+
semantics without replacing a worksheet or workbook.
|
|
392
|
+
- Presentation scene-element mutations create, update, or tombstone one stable
|
|
393
|
+
object inside a slide, master, or layout. Canonical creation claims prevent
|
|
394
|
+
conflicting same-ID reuse, optimistic top-level field guards merge unrelated
|
|
395
|
+
concurrent edits, and exact deletion guards prevent a stale destructive
|
|
396
|
+
write without replacing the deck or container.
|
|
364
397
|
- PDF form-value mutations write the browser-compatible conflict-local record
|
|
365
398
|
roots by fully-qualified field name, retain idempotent receipts and optional
|
|
366
399
|
state-vector preconditions, and never synchronize source bytes.
|
|
@@ -388,19 +421,22 @@ presence projection are pending.
|
|
|
388
421
|
source browser operation, and source attribution is audit data rather than
|
|
389
422
|
an authorization token.
|
|
390
423
|
- Cross-language tests apply deterministic native UTF-16 Markdown, ProseMirror
|
|
391
|
-
Document text/options/paragraph,
|
|
392
|
-
|
|
393
|
-
|
|
424
|
+
Document text/options/paragraph, Spreadsheet cell, Presentation
|
|
425
|
+
scene-element, and PDF annotation/form-value updates in browser Yjs,
|
|
426
|
+
including concurrent browser/native paragraph, cell-leaf, element-field,
|
|
427
|
+
annotation-leaf, and PDF form edits, in addition to importing browser Yjs
|
|
428
|
+
fixtures in Yrs.
|
|
394
429
|
- Standard MCP exposes the durable replica lifecycle and bounded event stream;
|
|
395
430
|
the same seven collaboration tools are explicitly available to the dedicated
|
|
396
431
|
`a3s code` Use worker.
|
|
397
432
|
|
|
398
433
|
Remaining:
|
|
399
434
|
|
|
400
|
-
- Extend typed format-model mutations to Spreadsheet and Presentation
|
|
401
|
-
signatures; deepen Document
|
|
402
|
-
|
|
403
|
-
those modes for local
|
|
435
|
+
- Extend typed format-model mutations to Spreadsheet and Presentation
|
|
436
|
+
structural/rich-text operations plus PDF signatures; deepen Document
|
|
437
|
+
mutations to additional nested structures and review operations, and add
|
|
438
|
+
durable comment/suggest operations before enabling those modes for local
|
|
439
|
+
mutation.
|
|
404
440
|
- Project editor-visible presence and selection state while keeping Awareness
|
|
405
441
|
ephemeral and outside native replica persistence.
|
|
406
442
|
|
package/README.md
CHANGED
|
@@ -138,9 +138,10 @@ The images below are committed visual-regression baselines from the real
|
|
|
138
138
|
- **Automation outside the browser** — The native Rust CLI, standard MCP
|
|
139
139
|
server, and Office Skill share bounded mutation contracts. Coding agents can
|
|
140
140
|
also keep a durable Yrs replica, exchange standard Yjs v1 updates and state
|
|
141
|
-
vectors, perform authorized typed Markdown, Document,
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
vectors, perform authorized typed Markdown, Document, Spreadsheet cell,
|
|
142
|
+
Presentation scene-element, and PDF annotation, form-value, and review
|
|
143
|
+
changes, retain browser/native actor attribution, and checkpoint without
|
|
144
|
+
replacing a whole Office file.
|
|
144
145
|
|
|
145
146
|
## Quick start
|
|
146
147
|
|
|
@@ -1023,6 +1024,20 @@ cargo run -p a3s-office-cli -- collab mutate .a3s/report.replica \
|
|
|
1023
1024
|
--operation-id edit-44 \
|
|
1024
1025
|
--mutation '{"type":"document-insert-paragraph","anchorParagraphId":"00000001","position":"after","paragraphId":"00000012","textId":"00000013","text":"Native paragraph"}' --json
|
|
1025
1026
|
|
|
1027
|
+
# Recursively patch one Spreadsheet cell after matching the observed value.
|
|
1028
|
+
# Zero-based row/column coordinates follow the browser collaboration model.
|
|
1029
|
+
cargo run -p a3s-office-cli -- collab mutate .a3s/plan.replica \
|
|
1030
|
+
--artifact-id plan --kind spreadsheet --actor-id agent-7 --mode edit \
|
|
1031
|
+
--operation-id edit-45 \
|
|
1032
|
+
--mutation '{"type":"spreadsheet-set-cell","sheetId":"sheet-data","row":1,"column":0,"expectedCell":{"v":10,"m":"10"},"nextCell":{"v":12,"m":"12","f":"=6*2"}}' --json
|
|
1033
|
+
|
|
1034
|
+
# Update one Presentation scene element after matching the complete observed
|
|
1035
|
+
# record. Create/delete use presentation-create/delete-element.
|
|
1036
|
+
cargo run -p a3s-office-cli -- collab mutate .a3s/deck.replica \
|
|
1037
|
+
--artifact-id deck --kind presentation --actor-id agent-7 --mode edit \
|
|
1038
|
+
--operation-id edit-46 \
|
|
1039
|
+
--mutation '{"type":"presentation-update-element","containerKind":"slide","containerId":"slide-1","elementId":"title-1","expectedElement":{"id":"title-1","type":"text","x":10,"y":10,"width":80,"height":20,"text":"Draft"},"nextElement":{"id":"title-1","type":"text","x":16,"y":10,"width":80,"height":20,"text":"Final"}}' --json
|
|
1040
|
+
|
|
1026
1041
|
# Set one PDF form value through its stable fully-qualified field name.
|
|
1027
1042
|
cargo run -p a3s-office-cli -- collab mutate .a3s/application.replica \
|
|
1028
1043
|
--artifact-id application --kind pdf --actor-id agent-7 --mode edit \
|
|
@@ -1080,7 +1095,19 @@ table rotate every identified ancestor row's `rowTextId`; incomplete row
|
|
|
1080
1095
|
identities fail before any write. Paragraphs containing inline atoms or review
|
|
1081
1096
|
marks remain guarded.
|
|
1082
1097
|
Page-color/track-changes sidecars remain independent conflict-local fields. All
|
|
1083
|
-
mutations use the same durable event path.
|
|
1098
|
+
mutations use the same durable event path. Spreadsheet set-cell mutations
|
|
1099
|
+
recursively compare `expectedCell` with the current browser-compatible cell and
|
|
1100
|
+
write only changed leaves, so unrelated concurrent value, formula, style,
|
|
1101
|
+
hyperlink, note, and metadata edits merge. Use `expectedCell: null` only when
|
|
1102
|
+
creating an observed blank coordinate. Spreadsheet deletion requires the exact
|
|
1103
|
+
complete current cell, dense projections keep their dimensions, and sparse or
|
|
1104
|
+
empty sheets remain sparse. Presentation creation writes one complete element
|
|
1105
|
+
plus a canonical immutable claim inside a slide, master, or layout and can
|
|
1106
|
+
place it after a stable active element. Presentation update compares complete
|
|
1107
|
+
expected/current/next elements and writes only changed top-level fields, so
|
|
1108
|
+
unrelated concurrent geometry, text, or style changes merge while a stale
|
|
1109
|
+
same-field edit fails. Exact deletion writes a durable tombstone, and element
|
|
1110
|
+
IDs and types never drift or become reusable. PDF annotation creation accepts the
|
|
1084
1111
|
portable browser record for FreeText, Highlight, Underline, StrikeOut, or Ink,
|
|
1085
1112
|
writes `source: created`, and commits its immutable claim atomically. An update
|
|
1086
1113
|
supplies complete `expectedAnnotation` and `nextAnnotation` values; recursive
|
|
@@ -1158,6 +1185,8 @@ without hard-coded return URLs.
|
|
|
1158
1185
|
|
|
1159
1186
|
- [Live Playground](https://a3s-lab.github.io/Office/)
|
|
1160
1187
|
- [Documentation center](https://a3s-lab.github.io/Office/docs/)
|
|
1188
|
+
- [A3S Office 0.6.0 documentation](https://a3s-lab.github.io/Office/docs/0.6.0/)
|
|
1189
|
+
- [A3S Office 0.5.0 documentation](https://a3s-lab.github.io/Office/docs/0.5.0/)
|
|
1161
1190
|
- [A3S Office 0.4.0 documentation](https://a3s-lab.github.io/Office/docs/0.4.0/)
|
|
1162
1191
|
- [A3S Office 0.3.0 documentation](https://a3s-lab.github.io/Office/docs/0.3.0/)
|
|
1163
1192
|
- [A3S Office 0.2.0 documentation](https://a3s-lab.github.io/Office/docs/0.2.0/)
|
package/dist/4560.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { initializeWorkOfficeCollaborationMetadata, assertWorkOfficeCollaborationEditable, OfficeCollaborationError as WorkOfficeCollaborationError, readOfficeCollaborationMetadata as readWorkOfficeCollaborationMetadata, registerWorkOfficeCollaborationInitializer, markWorkOfficeCollaborationInitialized, assertWorkOfficeCollaborationOrigin } from "./9787.js";
|
|
2
|
-
import { workOfficeCollaborationJsonEqual, isWorkOfficeCollaborationRecord, cloneWorkOfficeCollaborationJson } from "./4650.js";
|
|
2
|
+
import { workOfficeCollaborationJsonEqual, canonicalWorkOfficeCollaborationJson, isWorkOfficeCollaborationRecord, cloneWorkOfficeCollaborationJson } from "./4650.js";
|
|
3
3
|
import * as __rspack_external_yjs from "yjs";
|
|
4
4
|
function validateWorkOfficePresentationContent(content) {
|
|
5
5
|
if (!content || 'presentation' !== content.type) invalidWorkOfficePresentationInput('a Presentation content value');
|
|
@@ -66,6 +66,7 @@ function validateLayout(value) {
|
|
|
66
66
|
}
|
|
67
67
|
function validateElement(value) {
|
|
68
68
|
const record = requiredInputRecord(value, 'presentation element');
|
|
69
|
+
if (Object.hasOwn(record, 'tombstone')) invalidWorkOfficePresentationInput("a presentation element without the reserved 'tombstone' field");
|
|
69
70
|
const element = validateJsonRecord(record, 'presentation element');
|
|
70
71
|
element.id = requiredIdentifier(record.id, 'presentation element');
|
|
71
72
|
element.type = requiredString(record.type, 'presentation element type');
|
|
@@ -131,11 +132,115 @@ function requiredFiniteNumber(value, label) {
|
|
|
131
132
|
function invalidWorkOfficePresentationInput(expected) {
|
|
132
133
|
throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `Presentation collaboration requires ${expected}.`);
|
|
133
134
|
}
|
|
135
|
+
function appendWorkOfficePresentationRecordClaims(claims, previous, next) {
|
|
136
|
+
appendContainerClaims(claims, 'slide', previous?.slides ?? [], next.slides);
|
|
137
|
+
appendContainerClaims(claims, 'master', previous?.masters ?? [], next.masters ?? []);
|
|
138
|
+
appendContainerClaims(claims, 'layout', previous?.layouts ?? [], next.layouts ?? []);
|
|
139
|
+
}
|
|
140
|
+
function assertWorkOfficePresentationRecordClaims(claims, content) {
|
|
141
|
+
const fingerprints = new Map();
|
|
142
|
+
for (const raw of claims.toArray()){
|
|
143
|
+
const claim = parsedClaim(raw);
|
|
144
|
+
const identity = claimIdentity(claim.containerKind, claim.containerId, claim.id);
|
|
145
|
+
const existing = fingerprints.get(identity);
|
|
146
|
+
if (void 0 !== existing && existing !== claim.fingerprint) throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The scene element in ${claim.containerKind} '${claim.containerId}' ID '${claim.id}' was concurrently assigned to different records.`);
|
|
147
|
+
fingerprints.set(identity, claim.fingerprint);
|
|
148
|
+
}
|
|
149
|
+
assertClaimedElementIdentities(fingerprints, 'slide', content.slides);
|
|
150
|
+
assertClaimedElementIdentities(fingerprints, 'master', content.masters ?? []);
|
|
151
|
+
assertClaimedElementIdentities(fingerprints, 'layout', content.layouts ?? []);
|
|
152
|
+
}
|
|
153
|
+
function appendContainerClaims(claims, containerKind, previous, next) {
|
|
154
|
+
const previousById = new Map(previous.map((container)=>[
|
|
155
|
+
container.id,
|
|
156
|
+
container
|
|
157
|
+
]));
|
|
158
|
+
for (const container of next){
|
|
159
|
+
const previousElementIds = new Set((previousById.get(container.id)?.elements ?? []).map(({ id })=>id));
|
|
160
|
+
for (const element of container.elements)if (!previousElementIds.has(element.id)) appendClaim(claims, containerKind, container.id, element);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function appendClaim(claims, containerKind, containerId, element) {
|
|
164
|
+
const serialized = canonicalWorkOfficeCollaborationJson({
|
|
165
|
+
containerId,
|
|
166
|
+
containerKind,
|
|
167
|
+
fingerprint: canonicalWorkOfficeCollaborationJson(element),
|
|
168
|
+
id: element.id,
|
|
169
|
+
kind: 'element'
|
|
170
|
+
});
|
|
171
|
+
if (!claims.toArray().includes(serialized)) claims.push([
|
|
172
|
+
serialized
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
function parsedClaim(raw) {
|
|
176
|
+
if ('string' != typeof raw) invalidWorkOfficePresentationShared('record claim');
|
|
177
|
+
let value;
|
|
178
|
+
try {
|
|
179
|
+
value = JSON.parse(raw);
|
|
180
|
+
} catch {
|
|
181
|
+
invalidWorkOfficePresentationShared('record claim');
|
|
182
|
+
}
|
|
183
|
+
if (!isWorkOfficeCollaborationRecord(value) || canonicalWorkOfficeCollaborationJson(value) !== raw) invalidWorkOfficePresentationShared('record claim');
|
|
184
|
+
if (Object.keys(value).some((key)=>'containerId' !== key && 'containerKind' !== key && 'fingerprint' !== key && 'id' !== key && 'kind' !== key)) invalidWorkOfficePresentationShared('record claim');
|
|
185
|
+
if ('element' !== value.kind) invalidWorkOfficePresentationShared('record claim kind');
|
|
186
|
+
const containerKind = requiredContainerKind(value.containerKind);
|
|
187
|
+
const containerId = office_presentation_collaboration_claims_requiredIdentifier(value.containerId, 'record claim container');
|
|
188
|
+
const id = office_presentation_collaboration_claims_requiredIdentifier(value.id, 'record claim');
|
|
189
|
+
const fingerprint = office_presentation_collaboration_claims_requiredString(value.fingerprint, 'record claim fingerprint');
|
|
190
|
+
let claimed;
|
|
191
|
+
try {
|
|
192
|
+
claimed = JSON.parse(fingerprint);
|
|
193
|
+
} catch {
|
|
194
|
+
invalidWorkOfficePresentationShared('record claim fingerprint');
|
|
195
|
+
}
|
|
196
|
+
if (!isWorkOfficeCollaborationRecord(claimed) || canonicalWorkOfficeCollaborationJson(claimed) !== fingerprint || claimed.id !== id || 'string' != typeof claimed.type) invalidWorkOfficePresentationShared('record claim fingerprint');
|
|
197
|
+
return {
|
|
198
|
+
containerId,
|
|
199
|
+
containerKind,
|
|
200
|
+
fingerprint,
|
|
201
|
+
id,
|
|
202
|
+
kind: 'element'
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function assertClaimedElementIdentities(claims, containerKind, containers) {
|
|
206
|
+
for (const container of containers)for (const element of container.elements){
|
|
207
|
+
const fingerprint = claims.get(claimIdentity(containerKind, container.id, element.id));
|
|
208
|
+
if (void 0 === fingerprint) continue;
|
|
209
|
+
let claimed;
|
|
210
|
+
try {
|
|
211
|
+
claimed = JSON.parse(fingerprint);
|
|
212
|
+
} catch {
|
|
213
|
+
invalidWorkOfficePresentationShared('record claim fingerprint');
|
|
214
|
+
}
|
|
215
|
+
if (!isWorkOfficeCollaborationRecord(claimed) || claimed.id !== element.id || claimed.type !== element.type) invalidWorkOfficePresentationShared('scene element immutable identity claim');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function requiredContainerKind(value) {
|
|
219
|
+
if ('layout' === value || 'master' === value || 'slide' === value) return value;
|
|
220
|
+
invalidWorkOfficePresentationShared('record claim container kind');
|
|
221
|
+
}
|
|
222
|
+
function office_presentation_collaboration_claims_requiredIdentifier(value, label) {
|
|
223
|
+
const result = office_presentation_collaboration_claims_requiredString(value, label);
|
|
224
|
+
if (!result || result !== result.trim() || result.length > 256) invalidWorkOfficePresentationShared(label);
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
function office_presentation_collaboration_claims_requiredString(value, label) {
|
|
228
|
+
if ('string' != typeof value) invalidWorkOfficePresentationShared(label);
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
function claimIdentity(containerKind, containerId, id) {
|
|
232
|
+
return canonicalWorkOfficeCollaborationJson([
|
|
233
|
+
containerKind,
|
|
234
|
+
containerId,
|
|
235
|
+
id
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
134
238
|
const PRESENTATION_RECORD_ELEMENTS = 'elements';
|
|
135
239
|
const PRESENTATION_RECORD_ELEMENT_ORDER = 'elementOrder';
|
|
136
240
|
const PRESENTATION_RECORD_COMMENTS = 'comments';
|
|
137
241
|
const PRESENTATION_RECORD_COMMENT_ORDER = 'commentOrder';
|
|
138
242
|
const PRESENTATION_RECORD_COMMENTS_PRESENT = 'commentsPresent';
|
|
243
|
+
const PRESENTATION_RECORD_TOMBSTONE = 'tombstone';
|
|
139
244
|
function nestedPresentationMap(parent, key, label) {
|
|
140
245
|
const existing = parent.get(key);
|
|
141
246
|
if (existing instanceof __rspack_external_yjs.Map) return existing;
|
|
@@ -162,7 +267,7 @@ function requiredPresentationArray(parent, key, label) {
|
|
|
162
267
|
if (!(value instanceof __rspack_external_yjs.Array)) invalidWorkOfficePresentationShared(label);
|
|
163
268
|
return value;
|
|
164
269
|
}
|
|
165
|
-
function validatedPresentationOrder(order, records, label) {
|
|
270
|
+
function validatedPresentationOrder(order, records, label, allowTombstones = false) {
|
|
166
271
|
const seen = new Set();
|
|
167
272
|
const values = [];
|
|
168
273
|
for (const value of order.toArray()){
|
|
@@ -172,7 +277,20 @@ function validatedPresentationOrder(order, records, label) {
|
|
|
172
277
|
values.push(value);
|
|
173
278
|
}
|
|
174
279
|
}
|
|
175
|
-
|
|
280
|
+
for (const id of seen){
|
|
281
|
+
const record = records.get(id);
|
|
282
|
+
if (!(record instanceof __rspack_external_yjs.Map)) invalidWorkOfficePresentationShared(`${label} order and record set`);
|
|
283
|
+
if (void 0 !== record.get(PRESENTATION_RECORD_TOMBSTONE)) invalidWorkOfficePresentationShared(`${label} order and record set`);
|
|
284
|
+
}
|
|
285
|
+
for (const [id, value] of records.entries()){
|
|
286
|
+
if (!(value instanceof __rspack_external_yjs.Map)) invalidWorkOfficePresentationShared(`${label} order and record set`);
|
|
287
|
+
const tombstone = value.get(PRESENTATION_RECORD_TOMBSTONE);
|
|
288
|
+
if (void 0 === tombstone) {
|
|
289
|
+
if (!seen.has(id)) invalidWorkOfficePresentationShared(`${label} order and record set`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (!allowTombstones || true !== tombstone || seen.has(id)) invalidWorkOfficePresentationShared(`${label} order and record set`);
|
|
293
|
+
}
|
|
176
294
|
return values;
|
|
177
295
|
}
|
|
178
296
|
function readPresentationCollection(records, order, label) {
|
|
@@ -191,15 +309,15 @@ function readPresentationCollection(records, order, label) {
|
|
|
191
309
|
}
|
|
192
310
|
}
|
|
193
311
|
if (value.id !== id) invalidWorkOfficePresentationShared(`${label} identity`);
|
|
194
|
-
value.elements = readIdRecords(requiredPresentationMap(record, PRESENTATION_RECORD_ELEMENTS, `${label} elements`), requiredPresentationArray(record, PRESENTATION_RECORD_ELEMENT_ORDER, `${label} element order`), `${label} element
|
|
312
|
+
value.elements = readIdRecords(requiredPresentationMap(record, PRESENTATION_RECORD_ELEMENTS, `${label} elements`), requiredPresentationArray(record, PRESENTATION_RECORD_ELEMENT_ORDER, `${label} element order`), `${label} element`, true);
|
|
195
313
|
if ('slide' === label) readSlideComments(record, value);
|
|
196
314
|
return value;
|
|
197
315
|
});
|
|
198
316
|
}
|
|
199
317
|
function patchPresentationElements(records, order, previous, next, parentLabel) {
|
|
200
|
-
patchPresentationIdRecords(records, order, previous, next, `${parentLabel} element
|
|
318
|
+
patchPresentationIdRecords(records, order, previous, next, `${parentLabel} element`, true);
|
|
201
319
|
}
|
|
202
|
-
function patchPresentationIdRecords(records, order, previous, next, label) {
|
|
320
|
+
function patchPresentationIdRecords(records, order, previous, next, label, durableTombstones = false) {
|
|
203
321
|
const previousById = new Map(previous.map((value)=>[
|
|
204
322
|
value.id,
|
|
205
323
|
value
|
|
@@ -208,7 +326,19 @@ function patchPresentationIdRecords(records, order, previous, next, label) {
|
|
|
208
326
|
value.id,
|
|
209
327
|
value
|
|
210
328
|
]));
|
|
211
|
-
for (const value of previous)
|
|
329
|
+
for (const value of previous){
|
|
330
|
+
if (nextById.has(value.id)) continue;
|
|
331
|
+
if (!durableTombstones) {
|
|
332
|
+
records.delete(value.id);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const record = records.get(value.id);
|
|
336
|
+
if (void 0 === record) continue;
|
|
337
|
+
if (!(record instanceof __rspack_external_yjs.Map)) invalidWorkOfficePresentationShared(label);
|
|
338
|
+
const tombstone = record.get(PRESENTATION_RECORD_TOMBSTONE);
|
|
339
|
+
if (void 0 !== tombstone && true !== tombstone) invalidWorkOfficePresentationShared(`${label} tombstone`);
|
|
340
|
+
record.set(PRESENTATION_RECORD_TOMBSTONE, true);
|
|
341
|
+
}
|
|
212
342
|
for (const value of next){
|
|
213
343
|
const before = previousById.get(value.id);
|
|
214
344
|
if (before && workOfficeCollaborationJsonEqual(before, value)) continue;
|
|
@@ -218,6 +348,13 @@ function patchPresentationIdRecords(records, order, previous, next, label) {
|
|
|
218
348
|
records.set(value.id, record);
|
|
219
349
|
}
|
|
220
350
|
if (!(record instanceof __rspack_external_yjs.Map)) invalidWorkOfficePresentationShared(label);
|
|
351
|
+
const tombstone = record.get(PRESENTATION_RECORD_TOMBSTONE);
|
|
352
|
+
if (void 0 !== tombstone) {
|
|
353
|
+
if (true !== tombstone) invalidWorkOfficePresentationShared(`${label} tombstone`);
|
|
354
|
+
if (!before) throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The deleted scene element ID '${value.id}' cannot be reused.`);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (durableTombstones && before && before.type !== value.type) throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The scene element ID '${value.id}' cannot change type.`);
|
|
221
358
|
patchPresentationJsonMap(record, before ?? {}, value);
|
|
222
359
|
}
|
|
223
360
|
patchPresentationOrder(order, previous.map((value)=>value.id), next.map((value)=>value.id));
|
|
@@ -246,12 +383,21 @@ function readSlideComments(record, value) {
|
|
|
246
383
|
if (commentRecords.size > 0 || commentOrder.length > 0) value.comments = readIdRecords(commentRecords, commentOrder, 'slide comment');
|
|
247
384
|
else if (true === record.get(PRESENTATION_RECORD_COMMENTS_PRESENT)) value.comments = [];
|
|
248
385
|
}
|
|
249
|
-
function readIdRecords(records, order, label) {
|
|
250
|
-
|
|
386
|
+
function readIdRecords(records, order, label, allowTombstones = false) {
|
|
387
|
+
const activeIds = validatedPresentationOrder(order, records, label, allowTombstones);
|
|
388
|
+
if (allowTombstones) for (const [id, value] of records.entries()){
|
|
389
|
+
if (!(value instanceof __rspack_external_yjs.Map)) invalidWorkOfficePresentationShared(label);
|
|
390
|
+
if (true === value.get(PRESENTATION_RECORD_TOMBSTONE)) readJsonMap(value, id, label, true);
|
|
391
|
+
}
|
|
392
|
+
return activeIds.map((id)=>readJsonMap(requiredPresentationMap(records, id, label), id, label));
|
|
251
393
|
}
|
|
252
|
-
function readJsonMap(record, expectedId, label) {
|
|
394
|
+
function readJsonMap(record, expectedId, label, tombstoned = false) {
|
|
253
395
|
const value = {};
|
|
254
396
|
for (const [key, item] of record.entries()){
|
|
397
|
+
if (key === PRESENTATION_RECORD_TOMBSTONE) {
|
|
398
|
+
if (!tombstoned || true !== item) invalidWorkOfficePresentationShared(`${label} tombstone`);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
255
401
|
if (item instanceof __rspack_external_yjs.AbstractType) invalidWorkOfficePresentationShared(`${label} field '${key}'`);
|
|
256
402
|
value[key] = cloneWorkOfficeCollaborationJson(item);
|
|
257
403
|
}
|
|
@@ -299,6 +445,7 @@ const PRESENTATION_MASTERS_ROOT = 'presentation.masters';
|
|
|
299
445
|
const PRESENTATION_MASTER_ORDER_ROOT = 'presentation.master-order';
|
|
300
446
|
const PRESENTATION_LAYOUTS_ROOT = 'presentation.layouts';
|
|
301
447
|
const PRESENTATION_LAYOUT_ORDER_ROOT = 'presentation.layout-order';
|
|
448
|
+
const PRESENTATION_RECORD_CLAIMS_ROOT = 'presentation.record-claims';
|
|
302
449
|
function workOfficePresentationRoots(document, rootName) {
|
|
303
450
|
return {
|
|
304
451
|
options: document.getMap(rootName(PRESENTATION_OPTIONS_ROOT)),
|
|
@@ -307,10 +454,12 @@ function workOfficePresentationRoots(document, rootName) {
|
|
|
307
454
|
masters: document.getMap(rootName(PRESENTATION_MASTERS_ROOT)),
|
|
308
455
|
masterOrder: document.getArray(rootName(PRESENTATION_MASTER_ORDER_ROOT)),
|
|
309
456
|
layouts: document.getMap(rootName(PRESENTATION_LAYOUTS_ROOT)),
|
|
310
|
-
layoutOrder: document.getArray(rootName(PRESENTATION_LAYOUT_ORDER_ROOT))
|
|
457
|
+
layoutOrder: document.getArray(rootName(PRESENTATION_LAYOUT_ORDER_ROOT)),
|
|
458
|
+
recordClaims: document.getArray(rootName(PRESENTATION_RECORD_CLAIMS_ROOT))
|
|
311
459
|
};
|
|
312
460
|
}
|
|
313
461
|
function initializeWorkOfficePresentationRoots(roots, content) {
|
|
462
|
+
appendWorkOfficePresentationRecordClaims(roots.recordClaims, void 0, content);
|
|
314
463
|
patchOptionalJson(roots.options, 'width', void 0, content.width);
|
|
315
464
|
patchOptionalJson(roots.options, 'height', void 0, content.height);
|
|
316
465
|
patchCollection({
|
|
@@ -348,11 +497,14 @@ function readWorkOfficePresentationRoots(roots) {
|
|
|
348
497
|
const layouts = readPresentationCollection(roots.layouts, roots.layoutOrder, 'presentation layout');
|
|
349
498
|
if (masters.length > 0) result.masters = masters;
|
|
350
499
|
if (layouts.length > 0) result.layouts = layouts;
|
|
351
|
-
|
|
500
|
+
const validated = validateSharedWorkOfficePresentationContent(result);
|
|
501
|
+
assertWorkOfficePresentationRecordClaims(roots.recordClaims, validated);
|
|
502
|
+
return validated;
|
|
352
503
|
}
|
|
353
504
|
function patchWorkOfficePresentationRoots(roots, previous, next) {
|
|
354
505
|
const shared = readWorkOfficePresentationRoots(roots);
|
|
355
506
|
assertChangedRecordsStillExist(previous, next, shared);
|
|
507
|
+
appendWorkOfficePresentationRecordClaims(roots.recordClaims, previous, next);
|
|
356
508
|
patchOptionalJson(roots.options, 'width', previous.width, next.width);
|
|
357
509
|
patchOptionalJson(roots.options, 'height', previous.height, next.height);
|
|
358
510
|
patchCollection({
|
|
@@ -413,7 +565,7 @@ function assertChangedChildRecordsExist(previous, next, shared, label) {
|
|
|
413
565
|
}
|
|
414
566
|
}
|
|
415
567
|
function assertWorkOfficePresentationRootsEmpty(roots) {
|
|
416
|
-
if (roots.options.size > 0 || roots.slides.size > 0 || roots.slideOrder.length > 0 || roots.masters.size > 0 || roots.masterOrder.length > 0 || roots.layouts.size > 0 || roots.layoutOrder.length > 0) throw new WorkOfficeCollaborationError('office.collaboration.bootstrap_ambiguous', 'The Presentation collaboration roots contain data without initialized metadata.');
|
|
568
|
+
if (roots.options.size > 0 || roots.slides.size > 0 || roots.slideOrder.length > 0 || roots.masters.size > 0 || roots.masterOrder.length > 0 || roots.layouts.size > 0 || roots.layoutOrder.length > 0 || roots.recordClaims.length > 0) throw new WorkOfficeCollaborationError('office.collaboration.bootstrap_ambiguous', 'The Presentation collaboration roots contain data without initialized metadata.');
|
|
417
569
|
}
|
|
418
570
|
function workOfficePresentationUndoScope(roots) {
|
|
419
571
|
return [
|
|
@@ -423,7 +575,8 @@ function workOfficePresentationUndoScope(roots) {
|
|
|
423
575
|
roots.masters,
|
|
424
576
|
roots.masterOrder,
|
|
425
577
|
roots.layouts,
|
|
426
|
-
roots.layoutOrder
|
|
578
|
+
roots.layoutOrder,
|
|
579
|
+
roots.recordClaims
|
|
427
580
|
];
|
|
428
581
|
}
|
|
429
582
|
function patchCollection({ label, order, previous, records, next }) {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type * as Y from 'yjs';
|
|
2
|
+
import type { WorkPresentationContent } from '../features/work/work-types';
|
|
3
|
+
export type WorkOfficePresentationContainerKind = 'layout' | 'master' | 'slide';
|
|
4
|
+
export declare function appendWorkOfficePresentationRecordClaims(claims: Y.Array<string>, previous: WorkPresentationContent | undefined, next: WorkPresentationContent): void;
|
|
5
|
+
export declare function assertWorkOfficePresentationRecordClaims(claims: Y.Array<string>, content: WorkPresentationContent): void;
|
|
@@ -8,6 +8,7 @@ export declare const PRESENTATION_MASTERS_ROOT = "presentation.masters";
|
|
|
8
8
|
export declare const PRESENTATION_MASTER_ORDER_ROOT = "presentation.master-order";
|
|
9
9
|
export declare const PRESENTATION_LAYOUTS_ROOT = "presentation.layouts";
|
|
10
10
|
export declare const PRESENTATION_LAYOUT_ORDER_ROOT = "presentation.layout-order";
|
|
11
|
+
export declare const PRESENTATION_RECORD_CLAIMS_ROOT = "presentation.record-claims";
|
|
11
12
|
export interface WorkOfficePresentationRoots {
|
|
12
13
|
options: Y.Map<unknown>;
|
|
13
14
|
slides: Y.Map<unknown>;
|
|
@@ -16,6 +17,7 @@ export interface WorkOfficePresentationRoots {
|
|
|
16
17
|
masterOrder: Y.Array<string>;
|
|
17
18
|
layouts: Y.Map<unknown>;
|
|
18
19
|
layoutOrder: Y.Array<string>;
|
|
20
|
+
recordClaims: Y.Array<string>;
|
|
19
21
|
}
|
|
20
22
|
export declare function workOfficePresentationRoots(document: Y.Doc, rootName: (suffix: string) => string): WorkOfficePresentationRoots;
|
|
21
23
|
export declare function initializeWorkOfficePresentationRoots(roots: WorkOfficePresentationRoots, content: WorkPresentationContent): void;
|
|
@@ -5,6 +5,7 @@ export declare const PRESENTATION_RECORD_ELEMENT_ORDER = "elementOrder";
|
|
|
5
5
|
export declare const PRESENTATION_RECORD_COMMENTS = "comments";
|
|
6
6
|
export declare const PRESENTATION_RECORD_COMMENT_ORDER = "commentOrder";
|
|
7
7
|
export declare const PRESENTATION_RECORD_COMMENTS_PRESENT = "commentsPresent";
|
|
8
|
+
export declare const PRESENTATION_RECORD_TOMBSTONE = "tombstone";
|
|
8
9
|
export type WorkOfficePresentationRecord = {
|
|
9
10
|
id: string;
|
|
10
11
|
elements: WorkSlideElement[];
|
|
@@ -13,10 +14,10 @@ export declare function nestedPresentationMap(parent: Y.Map<unknown>, key: strin
|
|
|
13
14
|
export declare function nestedPresentationArray(parent: Y.Map<unknown>, key: string, label: string): Y.Array<string>;
|
|
14
15
|
export declare function requiredPresentationMap(parent: Y.Map<unknown>, key: string, label: string): Y.Map<unknown>;
|
|
15
16
|
export declare function requiredPresentationArray(parent: Y.Map<unknown>, key: string, label: string): Y.Array<string>;
|
|
16
|
-
export declare function validatedPresentationOrder(order: Y.Array<string>, records: Y.Map<unknown>, label: string): string[];
|
|
17
|
+
export declare function validatedPresentationOrder(order: Y.Array<string>, records: Y.Map<unknown>, label: string, allowTombstones?: boolean): string[];
|
|
17
18
|
export declare function readPresentationCollection(records: Y.Map<unknown>, order: Y.Array<string>, label: string): WorkOfficePresentationRecord[];
|
|
18
19
|
export declare function patchPresentationElements(records: Y.Map<unknown>, order: Y.Array<string>, previous: WorkSlideElement[], next: WorkSlideElement[], parentLabel: string): void;
|
|
19
20
|
export declare function patchPresentationIdRecords<T extends {
|
|
20
21
|
id: string;
|
|
21
|
-
}>(records: Y.Map<unknown>, order: Y.Array<string>, previous: T[], next: T[], label: string): void;
|
|
22
|
+
}>(records: Y.Map<unknown>, order: Y.Array<string>, previous: T[], next: T[], label: string, durableTombstones?: boolean): void;
|
|
22
23
|
export declare function patchPresentationOrder(order: Y.Array<string>, previous: string[], next: string[]): void;
|
package/dist/office-kernel.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a3s-lab/office",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Open-source browser editors for documents, Markdown, spreadsheets, presentations, and PDFs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"office",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"playground:visual:spreadsheet-ribbon": "playwright test visual-tests/spreadsheet-ribbon.functional.spec.ts --config playwright.config.ts",
|
|
88
88
|
"playground:visual:update": "playwright test --config playwright.config.ts --update-snapshots",
|
|
89
89
|
"test": "rstest",
|
|
90
|
-
"test:e2e": "bun run test:e2e:fixtures && a3s-test run tests/e2e/word-page-color.acl --json && a3s-test run tests/e2e/word-page-setup-phone.acl --json && a3s-test run tests/e2e/word-paragraph-layout-phone.acl --json && a3s-test run tests/e2e/word-list-formatting-phone.acl --json && a3s-test run tests/e2e/word-font-picker-phone.acl --json && a3s-test run tests/e2e/word-comments-drawer.acl --json && a3s-test run tests/e2e/word-comment-windowing.acl --json && a3s-test run tests/e2e/word-citations-phone.acl --json && a3s-test run tests/e2e/word-navigation-search.acl --json && a3s-test run tests/e2e/word-navigation-windowing.acl --json && a3s-test run tests/e2e/word-find-replace-phone.acl --json && a3s-test run tests/e2e/word-page-navigation.acl --json && a3s-test run tests/e2e/word-page-windowing.acl --json && a3s-test run tests/e2e/word-ai-question.acl --json && a3s-test run tests/e2e/word-picture-insert.acl --json && a3s-test run tests/e2e/word-picture-alt-text-phone.acl --json && a3s-test run tests/e2e/word-track-changes-phone.acl --json && a3s-test run tests/e2e/word-review-conflict-phone.acl --json && a3s-test run tests/e2e/word-revision-windowing.acl --json && a3s-test run tests/e2e/word-table-phone.acl --json && a3s-test run tests/e2e/word-table-borders.acl --json && a3s-test run tests/e2e/word-theme-table.acl --json && a3s-test run tests/e2e/word-styled-table.acl --json && a3s-test run tests/e2e/word-multi-page-table.acl --json && a3s-test run tests/e2e/word-cross-reference-phone.acl --json && a3s-test run tests/e2e/word-bookmark-links-phone.acl --json && a3s-test run tests/e2e/word-notes-phone.acl --json && a3s-test run tests/e2e/word-fields-phone.acl --json && a3s-test run tests/e2e/spreadsheet-phone-rename.acl --json && a3s-test run tests/e2e/spreadsheet-phone-find.acl --json && a3s-test run tests/e2e/spreadsheet-phone-context-menu.acl --json && a3s-test run tests/e2e/spreadsheet-phone-task-pane.acl --json && a3s-test run tests/e2e/presentation-cut-paste-focus.acl --json && a3s-test run tests/e2e/presentation-presenter-view.acl --json && a3s-test run tests/e2e/presentation-phone-chart-pane.acl --json && a3s-test run tests/e2e/presentation-phone-comments.acl --json && a3s-test run tests/e2e/pdf-thumbnail-keyboard.acl --json && a3s-test run tests/e2e/pdf-page-drawer-phone.acl --json && a3s-test run tests/e2e/markdown-phone-modes.acl --json && a3s-test run tests/e2e/office-sidebar-breakpoint.acl --json && a3s-test run tests/e2e/office-docs-navigation.acl --json && a3s-test run tests/e2e/collaboration-participants.acl --json && a3s-test run tests/e2e/collaboration-pdf-annotations.acl --json",
|
|
91
|
-
"test:e2e:check": "bun run test:e2e:fixtures && a3s-test check tests/e2e/word-page-color.acl --json && a3s-test check tests/e2e/word-page-setup-phone.acl --json && a3s-test check tests/e2e/word-paragraph-layout-phone.acl --json && a3s-test check tests/e2e/word-list-formatting-phone.acl --json && a3s-test check tests/e2e/word-font-picker-phone.acl --json && a3s-test check tests/e2e/word-comments-drawer.acl --json && a3s-test check tests/e2e/word-comment-windowing.acl --json && a3s-test check tests/e2e/word-citations-phone.acl --json && a3s-test check tests/e2e/word-navigation-search.acl --json && a3s-test check tests/e2e/word-navigation-windowing.acl --json && a3s-test check tests/e2e/word-find-replace-phone.acl --json && a3s-test check tests/e2e/word-page-navigation.acl --json && a3s-test check tests/e2e/word-page-windowing.acl --json && a3s-test check tests/e2e/word-ai-question.acl --json && a3s-test check tests/e2e/word-picture-insert.acl --json && a3s-test check tests/e2e/word-picture-alt-text-phone.acl --json && a3s-test check tests/e2e/word-track-changes-phone.acl --json && a3s-test check tests/e2e/word-review-conflict-phone.acl --json && a3s-test check tests/e2e/word-revision-windowing.acl --json && a3s-test check tests/e2e/word-table-phone.acl --json && a3s-test check tests/e2e/word-table-borders.acl --json && a3s-test check tests/e2e/word-theme-table.acl --json && a3s-test check tests/e2e/word-styled-table.acl --json && a3s-test check tests/e2e/word-multi-page-table.acl --json && a3s-test check tests/e2e/word-cross-reference-phone.acl --json && a3s-test check tests/e2e/word-bookmark-links-phone.acl --json && a3s-test check tests/e2e/word-notes-phone.acl --json && a3s-test check tests/e2e/word-fields-phone.acl --json && a3s-test check tests/e2e/spreadsheet-phone-rename.acl --json && a3s-test check tests/e2e/spreadsheet-phone-find.acl --json && a3s-test check tests/e2e/spreadsheet-phone-context-menu.acl --json && a3s-test check tests/e2e/spreadsheet-phone-task-pane.acl --json && a3s-test check tests/e2e/presentation-cut-paste-focus.acl --json && a3s-test check tests/e2e/presentation-presenter-view.acl --json && a3s-test check tests/e2e/presentation-phone-chart-pane.acl --json && a3s-test check tests/e2e/presentation-phone-comments.acl --json && a3s-test check tests/e2e/pdf-thumbnail-keyboard.acl --json && a3s-test check tests/e2e/pdf-page-drawer-phone.acl --json && a3s-test check tests/e2e/markdown-phone-modes.acl --json && a3s-test check tests/e2e/office-sidebar-breakpoint.acl --json && a3s-test check tests/e2e/office-docs-navigation.acl --json && a3s-test check tests/e2e/collaboration-participants.acl --json && a3s-test check tests/e2e/collaboration-pdf-annotations.acl --json",
|
|
90
|
+
"test:e2e": "bun run test:e2e:fixtures && a3s-test run tests/e2e/word-page-color.acl --json && a3s-test run tests/e2e/word-page-setup-phone.acl --json && a3s-test run tests/e2e/word-paragraph-layout-phone.acl --json && a3s-test run tests/e2e/word-list-formatting-phone.acl --json && a3s-test run tests/e2e/word-font-picker-phone.acl --json && a3s-test run tests/e2e/word-comments-drawer.acl --json && a3s-test run tests/e2e/word-comment-windowing.acl --json && a3s-test run tests/e2e/word-citations-phone.acl --json && a3s-test run tests/e2e/word-navigation-search.acl --json && a3s-test run tests/e2e/word-navigation-windowing.acl --json && a3s-test run tests/e2e/word-find-replace-phone.acl --json && a3s-test run tests/e2e/word-page-navigation.acl --json && a3s-test run tests/e2e/word-page-windowing.acl --json && a3s-test run tests/e2e/word-ai-question.acl --json && a3s-test run tests/e2e/word-picture-insert.acl --json && a3s-test run tests/e2e/word-picture-alt-text-phone.acl --json && a3s-test run tests/e2e/word-track-changes-phone.acl --json && a3s-test run tests/e2e/word-review-conflict-phone.acl --json && a3s-test run tests/e2e/word-revision-windowing.acl --json && a3s-test run tests/e2e/word-table-phone.acl --json && a3s-test run tests/e2e/word-table-borders.acl --json && a3s-test run tests/e2e/word-theme-table.acl --json && a3s-test run tests/e2e/word-styled-table.acl --json && a3s-test run tests/e2e/word-multi-page-table.acl --json && a3s-test run tests/e2e/word-cross-reference-phone.acl --json && a3s-test run tests/e2e/word-bookmark-links-phone.acl --json && a3s-test run tests/e2e/word-notes-phone.acl --json && a3s-test run tests/e2e/word-fields-phone.acl --json && a3s-test run tests/e2e/spreadsheet-phone-rename.acl --json && a3s-test run tests/e2e/spreadsheet-phone-find.acl --json && a3s-test run tests/e2e/spreadsheet-phone-context-menu.acl --json && a3s-test run tests/e2e/spreadsheet-phone-task-pane.acl --json && a3s-test run tests/e2e/presentation-cut-paste-focus.acl --json && a3s-test run tests/e2e/presentation-presenter-view.acl --json && a3s-test run tests/e2e/presentation-phone-chart-pane.acl --json && a3s-test run tests/e2e/presentation-phone-comments.acl --json && a3s-test run tests/e2e/pdf-thumbnail-keyboard.acl --json && a3s-test run tests/e2e/pdf-page-drawer-phone.acl --json && a3s-test run tests/e2e/markdown-phone-modes.acl --json && a3s-test run tests/e2e/office-sidebar-breakpoint.acl --json && a3s-test run tests/e2e/office-docs-navigation.acl --json && a3s-test run tests/e2e/collaboration-participants.acl --json && a3s-test run tests/e2e/collaboration-pdf-annotations.acl --json && a3s-test run tests/e2e/collaboration-spreadsheet-cells.acl --json && a3s-test run tests/e2e/collaboration-presentation-elements.acl --json",
|
|
91
|
+
"test:e2e:check": "bun run test:e2e:fixtures && a3s-test check tests/e2e/word-page-color.acl --json && a3s-test check tests/e2e/word-page-setup-phone.acl --json && a3s-test check tests/e2e/word-paragraph-layout-phone.acl --json && a3s-test check tests/e2e/word-list-formatting-phone.acl --json && a3s-test check tests/e2e/word-font-picker-phone.acl --json && a3s-test check tests/e2e/word-comments-drawer.acl --json && a3s-test check tests/e2e/word-comment-windowing.acl --json && a3s-test check tests/e2e/word-citations-phone.acl --json && a3s-test check tests/e2e/word-navigation-search.acl --json && a3s-test check tests/e2e/word-navigation-windowing.acl --json && a3s-test check tests/e2e/word-find-replace-phone.acl --json && a3s-test check tests/e2e/word-page-navigation.acl --json && a3s-test check tests/e2e/word-page-windowing.acl --json && a3s-test check tests/e2e/word-ai-question.acl --json && a3s-test check tests/e2e/word-picture-insert.acl --json && a3s-test check tests/e2e/word-picture-alt-text-phone.acl --json && a3s-test check tests/e2e/word-track-changes-phone.acl --json && a3s-test check tests/e2e/word-review-conflict-phone.acl --json && a3s-test check tests/e2e/word-revision-windowing.acl --json && a3s-test check tests/e2e/word-table-phone.acl --json && a3s-test check tests/e2e/word-table-borders.acl --json && a3s-test check tests/e2e/word-theme-table.acl --json && a3s-test check tests/e2e/word-styled-table.acl --json && a3s-test check tests/e2e/word-multi-page-table.acl --json && a3s-test check tests/e2e/word-cross-reference-phone.acl --json && a3s-test check tests/e2e/word-bookmark-links-phone.acl --json && a3s-test check tests/e2e/word-notes-phone.acl --json && a3s-test check tests/e2e/word-fields-phone.acl --json && a3s-test check tests/e2e/spreadsheet-phone-rename.acl --json && a3s-test check tests/e2e/spreadsheet-phone-find.acl --json && a3s-test check tests/e2e/spreadsheet-phone-context-menu.acl --json && a3s-test check tests/e2e/spreadsheet-phone-task-pane.acl --json && a3s-test check tests/e2e/presentation-cut-paste-focus.acl --json && a3s-test check tests/e2e/presentation-presenter-view.acl --json && a3s-test check tests/e2e/presentation-phone-chart-pane.acl --json && a3s-test check tests/e2e/presentation-phone-comments.acl --json && a3s-test check tests/e2e/pdf-thumbnail-keyboard.acl --json && a3s-test check tests/e2e/pdf-page-drawer-phone.acl --json && a3s-test check tests/e2e/markdown-phone-modes.acl --json && a3s-test check tests/e2e/office-sidebar-breakpoint.acl --json && a3s-test check tests/e2e/office-docs-navigation.acl --json && a3s-test check tests/e2e/collaboration-participants.acl --json && a3s-test check tests/e2e/collaboration-pdf-annotations.acl --json && a3s-test check tests/e2e/collaboration-spreadsheet-cells.acl --json && a3s-test check tests/e2e/collaboration-presentation-elements.acl --json",
|
|
92
92
|
"test:e2e:fixtures": "bun scripts/create-e2e-fixtures.ts",
|
|
93
93
|
"test:e2e:initial-focus": "a3s-test run tests/e2e/office-editor-initial-focus.acl --json",
|
|
94
94
|
"test:e2e:initial-focus:check": "a3s-test check tests/e2e/office-editor-initial-focus.acl --json",
|
|
@@ -96,6 +96,10 @@
|
|
|
96
96
|
"test:e2e:collaboration-participants:check": "a3s-test check tests/e2e/collaboration-participants.acl --json",
|
|
97
97
|
"test:e2e:collaboration-pdf-annotations": "a3s-test run tests/e2e/collaboration-pdf-annotations.acl --json",
|
|
98
98
|
"test:e2e:collaboration-pdf-annotations:check": "a3s-test check tests/e2e/collaboration-pdf-annotations.acl --json",
|
|
99
|
+
"test:e2e:collaboration-spreadsheet-cells": "a3s-test run tests/e2e/collaboration-spreadsheet-cells.acl --json",
|
|
100
|
+
"test:e2e:collaboration-spreadsheet-cells:check": "a3s-test check tests/e2e/collaboration-spreadsheet-cells.acl --json",
|
|
101
|
+
"test:e2e:collaboration-presentation-elements": "a3s-test run tests/e2e/collaboration-presentation-elements.acl --json",
|
|
102
|
+
"test:e2e:collaboration-presentation-elements:check": "a3s-test check tests/e2e/collaboration-presentation-elements.acl --json",
|
|
99
103
|
"test:e2e:wps-layout": "bun run test:e2e:fixtures && a3s-test run tests/e2e/word-wps-layout-parity.acl --json && a3s-test run tests/e2e/word-wps-font-grid-parity.acl --json",
|
|
100
104
|
"test:e2e:wps-layout:check": "bun run test:e2e:fixtures && a3s-test check tests/e2e/word-wps-layout-parity.acl --json && a3s-test check tests/e2e/word-wps-font-grid-parity.acl --json",
|
|
101
105
|
"test:e2e:writer-ribbon": "a3s-test run tests/e2e/word-ribbon-collapse.acl --json",
|