Skip to content

Commit d1eaaf2

Browse files
committed
feat: upgrade meta.docx in yjs docs to real Y.Map
1 parent 09ebfcb commit d1eaaf2

127 files changed

Lines changed: 10208 additions & 2427 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/cli/src/lib/__tests__/bootstrap.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ describe('detectRoomState', () => {
4040
expect(detectRoomState(ydoc)).toBe('populated');
4141
});
4242

43+
test('returns "populated" when bootstrapDocxParts._version is 1', () => {
44+
const ydoc = new YDoc();
45+
ydoc.getMap('bootstrapDocxParts').set('_version', 1);
46+
expect(detectRoomState(ydoc)).toBe('populated');
47+
});
48+
49+
test('returns "empty" when bootstrapDocxParts exists but _version is absent', () => {
50+
const ydoc = new YDoc();
51+
// Just access the map without setting _version
52+
ydoc.getMap('bootstrapDocxParts');
53+
expect(detectRoomState(ydoc)).toBe('empty');
54+
});
55+
4356
test('returns "empty" when meta map only has a pending bootstrap marker (stale claim recovery)', () => {
4457
const ydoc = new YDoc();
4558
ydoc.getMap('meta').set('bootstrap', {

apps/cli/src/lib/bootstrap.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ export function detectRoomState(ydoc: YDoc): RoomState {
8080
const fragment = ydoc.getXmlFragment('supereditor');
8181
if (fragment.length > 0) return 'populated';
8282

83+
const bootstrapMap = ydoc.getMap('bootstrapDocxParts');
84+
if ((bootstrapMap.get('_version') as number | undefined) === 1) return 'populated';
85+
8386
const metaMap = ydoc.getMap('meta');
8487
// A pending-only bootstrap marker does NOT count as populated — the
8588
// claimer may have crashed before seeding actual content. Only

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export default [
7878
TextDecoder: 'readonly',
7979
FileReader: 'readonly',
8080
DOMRect: 'readonly',
81+
structuredClone: 'readonly',
8182

8283
// DOM APIs (text editing, clipboard, elements)
8384
HTMLElement: 'readonly',

packages/layout-engine/pm-adapter/src/converters/paragraph.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ let defaultConverterContext: ConverterContext = {
125125
runProperties: {},
126126
paragraphProperties: {},
127127
},
128-
styles: {},
128+
latentStyles: { lsdExceptions: [] },
129+
styles: [],
129130
},
130131
};
131132

@@ -759,7 +760,8 @@ describe('paragraph converters', () => {
759760
runProperties: {},
760761
paragraphProperties: {},
761762
},
762-
styles: {},
763+
latentStyles: { lsdExceptions: [] },
764+
styles: [],
763765
},
764766
};
765767
defaultConverterContext = converterContext;

packages/layout-engine/pm-adapter/src/converters/table.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ const DEFAULT_CONVERTER_CONTEXT: ConverterContext = {
2222
translatedNumbering: {},
2323
translatedLinkedStyles: {
2424
docDefaults: {},
25-
latentStyles: {},
26-
styles: {},
25+
latentStyles: { lsdExceptions: [] },
26+
styles: [],
2727
},
2828
};
2929

packages/layout-engine/pm-adapter/src/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ const createDefaultConverterContext = () => ({
1313
docx: {},
1414
translatedLinkedStyles: {
1515
docDefaults: {},
16-
latentStyles: {},
17-
styles: {},
16+
latentStyles: { lsdExceptions: [] },
17+
styles: [],
1818
},
1919
translatedNumbering: {
2020
abstracts: {},

packages/layout-engine/pm-adapter/src/integration.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ const DEFAULT_CONVERTER_CONTEXT = {
2424
docx: {},
2525
translatedLinkedStyles: {
2626
docDefaults: {},
27-
latentStyles: {},
28-
styles: {},
27+
latentStyles: { lsdExceptions: [] },
28+
styles: [],
2929
},
3030
translatedNumbering: {
3131
abstracts: {},

packages/layout-engine/pm-adapter/src/internal.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,8 @@ function normalizeConverterContext(
337337
translatedNumbering: {},
338338
translatedLinkedStyles: {
339339
docDefaults: {},
340-
latentStyles: {},
341-
styles: {},
340+
latentStyles: { lsdExceptions: [] },
341+
styles: [],
342342
},
343343
};
344344
}

packages/layout-engine/style-engine/src/ooxml/index.test.ts

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
type OoxmlResolverParams,
1111
} from './index.js';
1212

13-
const emptyStyles = { docDefaults: {}, latentStyles: {}, styles: {} };
13+
const emptyStyles = { docDefaults: {}, latentStyles: {}, styles: [] };
1414
const emptyNumbering = { abstracts: {}, definitions: {} };
1515

1616
const buildParams = (overrides?: Partial<OoxmlResolverParams>): OoxmlResolverParams => ({
@@ -30,9 +30,7 @@ describe('ooxml - resolveStyleChain', () => {
3030
const params = buildParams({
3131
translatedLinkedStyles: {
3232
...emptyStyles,
33-
styles: {
34-
Heading1: { runProperties: { fontSize: 32, bold: true } },
35-
},
33+
styles: [{ styleId: 'Heading1', runProperties: { fontSize: 32, bold: true } }],
3634
},
3735
});
3836
const result = resolveStyleChain('runProperties', params, 'Heading1');
@@ -43,16 +41,34 @@ describe('ooxml - resolveStyleChain', () => {
4341
const params = buildParams({
4442
translatedLinkedStyles: {
4543
...emptyStyles,
46-
styles: {
47-
BaseStyle: { runProperties: { fontSize: 22, italic: true } },
48-
DerivedStyle: { basedOn: 'BaseStyle', runProperties: { fontSize: 24, bold: true } },
49-
},
44+
styles: [
45+
{ styleId: 'BaseStyle', runProperties: { fontSize: 22, italic: true } },
46+
{ styleId: 'DerivedStyle', basedOn: 'BaseStyle', runProperties: { fontSize: 24, bold: true } },
47+
],
5048
},
5149
});
5250
const result = resolveStyleChain('runProperties', params, 'DerivedStyle');
5351
expect(result).toEqual({ fontSize: 24, bold: true, italic: true });
5452
});
5553

54+
it('refreshes cached lookups after in-place styles mutations', () => {
55+
const styles = [{ styleId: 'Heading1', runProperties: { fontSize: 32, bold: true } }];
56+
const params = buildParams({
57+
translatedLinkedStyles: {
58+
...emptyStyles,
59+
styles,
60+
},
61+
});
62+
63+
expect(resolveStyleChain('runProperties', params, 'Heading1')).toEqual({ fontSize: 32, bold: true });
64+
65+
styles.push({ styleId: 'Heading2', runProperties: { italic: true } });
66+
expect(resolveStyleChain('runProperties', params, 'Heading2')).toEqual({ italic: true });
67+
68+
styles[0].styleId = 'Heading1Renamed';
69+
expect(resolveStyleChain('runProperties', params, 'Heading1Renamed')).toEqual({ fontSize: 32, bold: true });
70+
});
71+
5672
it('returns empty object when styleId is missing from definitions', () => {
5773
const params = buildParams();
5874
const result = resolveStyleChain('runProperties', params, 'MissingStyle');
@@ -150,9 +166,7 @@ describe('ooxml - resolveRunProperties', () => {
150166
translatedLinkedStyles: {
151167
...emptyStyles,
152168
docDefaults: { runProperties: { fontSize: 20 } },
153-
styles: {
154-
Normal: { default: true, runProperties: { fontSize: 22 } },
155-
},
169+
styles: [{ styleId: 'Normal', default: true, runProperties: { fontSize: 22 } }],
156170
},
157171
});
158172
const result = resolveRunProperties(params, null, null);
@@ -164,9 +178,7 @@ describe('ooxml - resolveRunProperties', () => {
164178
translatedLinkedStyles: {
165179
...emptyStyles,
166180
docDefaults: { runProperties: { fontSize: 20, color: { val: 'AAAAAA' } } },
167-
styles: {
168-
Normal: { default: false, runProperties: { fontSize: 22, color: { val: 'BBBBBB' } } },
169-
},
181+
styles: [{ styleId: 'Normal', default: false, runProperties: { fontSize: 22, color: { val: 'BBBBBB' } } }],
170182
},
171183
});
172184
const result = resolveRunProperties(params, null, null);
@@ -177,10 +189,10 @@ describe('ooxml - resolveRunProperties', () => {
177189
const params = buildParams({
178190
translatedLinkedStyles: {
179191
...emptyStyles,
180-
styles: {
181-
TOC1: { runProperties: { bold: true } },
182-
Emphasis: { runProperties: { italic: true } },
183-
},
192+
styles: [
193+
{ styleId: 'TOC1', runProperties: { bold: true } },
194+
{ styleId: 'Emphasis', runProperties: { italic: true } },
195+
],
184196
},
185197
});
186198
const result = resolveRunProperties(params, { styleId: 'Emphasis', color: { val: 'FF0000' } }, { styleId: 'TOC1' });
@@ -219,8 +231,9 @@ describe('ooxml - resolveRunProperties', () => {
219231
const params = buildParams({
220232
translatedLinkedStyles: {
221233
...emptyStyles,
222-
styles: {
223-
TableStyle1: {
234+
styles: [
235+
{
236+
styleId: 'TableStyle1',
224237
type: 'table',
225238
runProperties: { color: { val: 'AAAAAA' } },
226239
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
@@ -233,7 +246,7 @@ describe('ooxml - resolveRunProperties', () => {
233246
nwCell: { runProperties: { fontSize: 15 } },
234247
},
235248
},
236-
},
249+
],
237250
},
238251
});
239252
const tableInfo = {
@@ -277,9 +290,7 @@ describe('ooxml - resolveParagraphProperties', () => {
277290
translatedLinkedStyles: {
278291
...emptyStyles,
279292
docDefaults: { paragraphProperties: { spacing: { before: 240 } } },
280-
styles: {
281-
Normal: { default: true, paragraphProperties: { spacing: { after: 120 } } },
282-
},
293+
styles: [{ styleId: 'Normal', default: true, paragraphProperties: { spacing: { after: 120 } } }],
283294
},
284295
});
285296
const inlineProps = { spacing: { before: 480 } };
@@ -291,9 +302,7 @@ describe('ooxml - resolveParagraphProperties', () => {
291302
const params = buildParams({
292303
translatedLinkedStyles: {
293304
...emptyStyles,
294-
styles: {
295-
ListStyle: { paragraphProperties: { indent: { left: 1200 } } },
296-
},
305+
styles: [{ styleId: 'ListStyle', paragraphProperties: { indent: { left: 1200 } } }],
297306
},
298307
translatedNumbering: {
299308
definitions: { '1': { abstractNumId: 10 } },
@@ -317,13 +326,14 @@ describe('ooxml - resolveParagraphProperties', () => {
317326
const params = buildParams({
318327
translatedLinkedStyles: {
319328
...emptyStyles,
320-
styles: {
321-
BaseStyle: { paragraphProperties: { indent: { left: 2000 } } },
322-
NumberedStyle: {
329+
styles: [
330+
{ styleId: 'BaseStyle', paragraphProperties: { indent: { left: 2000 } } },
331+
{
332+
styleId: 'NumberedStyle',
323333
basedOn: 'BaseStyle',
324334
paragraphProperties: { numberingProperties: { numId: 1, ilvl: 0 } },
325335
},
326-
},
336+
],
327337
},
328338
translatedNumbering: {
329339
definitions: { '1': { abstractNumId: 10 } },
@@ -346,9 +356,7 @@ describe('ooxml - resolveParagraphProperties', () => {
346356
translatedLinkedStyles: {
347357
...emptyStyles,
348358
docDefaults: { paragraphProperties: { tabStops: [{ pos: 720 }] } },
349-
styles: {
350-
Normal: { default: true, paragraphProperties: { tabStops: [{ pos: 1440 }] } },
351-
},
359+
styles: [{ styleId: 'Normal', default: true, paragraphProperties: { tabStops: [{ pos: 1440 }] } }],
352360
},
353361
});
354362
const result = resolveParagraphProperties(params, { tabStops: [{ pos: 2160 }] });
@@ -359,16 +367,17 @@ describe('ooxml - resolveParagraphProperties', () => {
359367
const params = buildParams({
360368
translatedLinkedStyles: {
361369
...emptyStyles,
362-
styles: {
363-
TableStyle1: {
370+
styles: [
371+
{
372+
styleId: 'TableStyle1',
364373
type: 'table',
365374
paragraphProperties: { spacing: { before: 120, after: 120 }, keepNext: true },
366375
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
367376
tableStyleProperties: {
368377
firstRow: { paragraphProperties: { spacing: { after: 240 } } },
369378
},
370379
},
371-
},
380+
],
372381
},
373382
});
374383
const tableInfo = {
@@ -389,8 +398,9 @@ describe('ooxml - resolveCellStyles', () => {
389398
const params = buildParams({
390399
translatedLinkedStyles: {
391400
...emptyStyles,
392-
styles: {
393-
TableStyleBand: {
401+
styles: [
402+
{
403+
styleId: 'TableStyleBand',
394404
type: 'table',
395405
tableProperties: { tableStyleRowBandSize: 2, tableStyleColBandSize: 3 },
396406
tableStyleProperties: {
@@ -401,7 +411,7 @@ describe('ooxml - resolveCellStyles', () => {
401411
band2Horz: { runProperties: { fontSize: 50 } },
402412
},
403413
},
404-
},
414+
],
405415
},
406416
});
407417
const tableInfo = {
@@ -419,9 +429,10 @@ describe('ooxml - resolveCellStyles', () => {
419429
describe('ooxml - resolveTableCellProperties', () => {
420430
const gridTable4Styles = {
421431
...emptyStyles,
422-
styles: {
423-
'GridTable4-Accent1': {
424-
type: 'table',
432+
styles: [
433+
{
434+
styleId: 'GridTable4-Accent1',
435+
type: 'table' as const,
425436
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
426437
tableStyleProperties: {
427438
firstRow: {
@@ -442,7 +453,7 @@ describe('ooxml - resolveTableCellProperties', () => {
442453
},
443454
},
444455
},
445-
},
456+
],
446457
};
447458

448459
it('resolves firstRow shading from table style', () => {
@@ -472,7 +483,6 @@ describe('ooxml - resolveTableCellProperties', () => {
472483
numCells: 4,
473484
};
474485
const result = resolveTableCellProperties(null, tableInfo, gridTable4Styles);
475-
// band1Horz overrides wholeTable
476486
expect(result.shading).toEqual({ val: 'clear', color: 'auto', fill: 'C1E4F5' });
477487
});
478488

@@ -538,7 +548,6 @@ describe('ooxml - resolveTableCellProperties', () => {
538548
};
539549
const inlineProps = { borders: { bottom: { val: 'double', color: '000000', size: 8 } } };
540550
const result = resolveTableCellProperties(inlineProps, tableInfo, gridTable4Styles);
541-
// firstRow style provides top border, inline provides bottom border - both should be present
542551
expect(result.borders?.top).toEqual({ val: 'single', color: '156082', size: 4 });
543552
expect(result.borders?.bottom).toEqual({ val: 'double', color: '000000', size: 8 });
544553
});

0 commit comments

Comments
 (0)