Skip to content

Commit 938bf61

Browse files
committed
feat: upgrade meta.docx in yjs docs to real Y.Map
1 parent c554678 commit 938bf61

127 files changed

Lines changed: 10209 additions & 2428 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
@@ -12,7 +12,7 @@ import {
1212
type OoxmlResolverParams,
1313
} from './index.js';
1414

15-
const emptyStyles = { docDefaults: {}, latentStyles: {}, styles: {} };
15+
const emptyStyles = { docDefaults: {}, latentStyles: {}, styles: [] };
1616
const emptyNumbering = { abstracts: {}, definitions: {} };
1717

1818
const buildParams = (overrides?: Partial<OoxmlResolverParams>): OoxmlResolverParams => ({
@@ -32,9 +32,7 @@ describe('ooxml - resolveStyleChain', () => {
3232
const params = buildParams({
3333
translatedLinkedStyles: {
3434
...emptyStyles,
35-
styles: {
36-
Heading1: { runProperties: { fontSize: 32, bold: true } },
37-
},
35+
styles: [{ styleId: 'Heading1', runProperties: { fontSize: 32, bold: true } }],
3836
},
3937
});
4038
const result = resolveStyleChain('runProperties', params, 'Heading1');
@@ -45,16 +43,34 @@ describe('ooxml - resolveStyleChain', () => {
4543
const params = buildParams({
4644
translatedLinkedStyles: {
4745
...emptyStyles,
48-
styles: {
49-
BaseStyle: { runProperties: { fontSize: 22, italic: true } },
50-
DerivedStyle: { basedOn: 'BaseStyle', runProperties: { fontSize: 24, bold: true } },
51-
},
46+
styles: [
47+
{ styleId: 'BaseStyle', runProperties: { fontSize: 22, italic: true } },
48+
{ styleId: 'DerivedStyle', basedOn: 'BaseStyle', runProperties: { fontSize: 24, bold: true } },
49+
],
5250
},
5351
});
5452
const result = resolveStyleChain('runProperties', params, 'DerivedStyle');
5553
expect(result).toEqual({ fontSize: 24, bold: true, italic: true });
5654
});
5755

56+
it('refreshes cached lookups after in-place styles mutations', () => {
57+
const styles = [{ styleId: 'Heading1', runProperties: { fontSize: 32, bold: true } }];
58+
const params = buildParams({
59+
translatedLinkedStyles: {
60+
...emptyStyles,
61+
styles,
62+
},
63+
});
64+
65+
expect(resolveStyleChain('runProperties', params, 'Heading1')).toEqual({ fontSize: 32, bold: true });
66+
67+
styles.push({ styleId: 'Heading2', runProperties: { italic: true } });
68+
expect(resolveStyleChain('runProperties', params, 'Heading2')).toEqual({ italic: true });
69+
70+
styles[0].styleId = 'Heading1Renamed';
71+
expect(resolveStyleChain('runProperties', params, 'Heading1Renamed')).toEqual({ fontSize: 32, bold: true });
72+
});
73+
5874
it('returns empty object when styleId is missing from definitions', () => {
5975
const params = buildParams();
6076
const result = resolveStyleChain('runProperties', params, 'MissingStyle');
@@ -152,9 +168,7 @@ describe('ooxml - resolveRunProperties', () => {
152168
translatedLinkedStyles: {
153169
...emptyStyles,
154170
docDefaults: { runProperties: { fontSize: 20 } },
155-
styles: {
156-
Normal: { default: true, runProperties: { fontSize: 22 } },
157-
},
171+
styles: [{ styleId: 'Normal', default: true, runProperties: { fontSize: 22 } }],
158172
},
159173
});
160174
const result = resolveRunProperties(params, null, null);
@@ -166,9 +180,7 @@ describe('ooxml - resolveRunProperties', () => {
166180
translatedLinkedStyles: {
167181
...emptyStyles,
168182
docDefaults: { runProperties: { fontSize: 20, color: { val: 'AAAAAA' } } },
169-
styles: {
170-
Normal: { default: false, runProperties: { fontSize: 22, color: { val: 'BBBBBB' } } },
171-
},
183+
styles: [{ styleId: 'Normal', default: false, runProperties: { fontSize: 22, color: { val: 'BBBBBB' } } }],
172184
},
173185
});
174186
const result = resolveRunProperties(params, null, null);
@@ -179,10 +191,10 @@ describe('ooxml - resolveRunProperties', () => {
179191
const params = buildParams({
180192
translatedLinkedStyles: {
181193
...emptyStyles,
182-
styles: {
183-
TOC1: { runProperties: { bold: true } },
184-
Emphasis: { runProperties: { italic: true } },
185-
},
194+
styles: [
195+
{ styleId: 'TOC1', runProperties: { bold: true } },
196+
{ styleId: 'Emphasis', runProperties: { italic: true } },
197+
],
186198
},
187199
});
188200
const result = resolveRunProperties(params, { styleId: 'Emphasis', color: { val: 'FF0000' } }, { styleId: 'TOC1' });
@@ -221,8 +233,9 @@ describe('ooxml - resolveRunProperties', () => {
221233
const params = buildParams({
222234
translatedLinkedStyles: {
223235
...emptyStyles,
224-
styles: {
225-
TableStyle1: {
236+
styles: [
237+
{
238+
styleId: 'TableStyle1',
226239
type: 'table',
227240
runProperties: { color: { val: 'AAAAAA' } },
228241
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
@@ -235,7 +248,7 @@ describe('ooxml - resolveRunProperties', () => {
235248
nwCell: { runProperties: { fontSize: 15 } },
236249
},
237250
},
238-
},
251+
],
239252
},
240253
});
241254
const tableInfo = {
@@ -279,9 +292,7 @@ describe('ooxml - resolveParagraphProperties', () => {
279292
translatedLinkedStyles: {
280293
...emptyStyles,
281294
docDefaults: { paragraphProperties: { spacing: { before: 240 } } },
282-
styles: {
283-
Normal: { default: true, paragraphProperties: { spacing: { after: 120 } } },
284-
},
295+
styles: [{ styleId: 'Normal', default: true, paragraphProperties: { spacing: { after: 120 } } }],
285296
},
286297
});
287298
const inlineProps = { spacing: { before: 480 } };
@@ -293,9 +304,7 @@ describe('ooxml - resolveParagraphProperties', () => {
293304
const params = buildParams({
294305
translatedLinkedStyles: {
295306
...emptyStyles,
296-
styles: {
297-
ListStyle: { paragraphProperties: { indent: { left: 1200 } } },
298-
},
307+
styles: [{ styleId: 'ListStyle', paragraphProperties: { indent: { left: 1200 } } }],
299308
},
300309
translatedNumbering: {
301310
definitions: { '1': { abstractNumId: 10 } },
@@ -319,13 +328,14 @@ describe('ooxml - resolveParagraphProperties', () => {
319328
const params = buildParams({
320329
translatedLinkedStyles: {
321330
...emptyStyles,
322-
styles: {
323-
BaseStyle: { paragraphProperties: { indent: { left: 2000 } } },
324-
NumberedStyle: {
331+
styles: [
332+
{ styleId: 'BaseStyle', paragraphProperties: { indent: { left: 2000 } } },
333+
{
334+
styleId: 'NumberedStyle',
325335
basedOn: 'BaseStyle',
326336
paragraphProperties: { numberingProperties: { numId: 1, ilvl: 0 } },
327337
},
328-
},
338+
],
329339
},
330340
translatedNumbering: {
331341
definitions: { '1': { abstractNumId: 10 } },
@@ -348,9 +358,7 @@ describe('ooxml - resolveParagraphProperties', () => {
348358
translatedLinkedStyles: {
349359
...emptyStyles,
350360
docDefaults: { paragraphProperties: { tabStops: [{ pos: 720 }] } },
351-
styles: {
352-
Normal: { default: true, paragraphProperties: { tabStops: [{ pos: 1440 }] } },
353-
},
361+
styles: [{ styleId: 'Normal', default: true, paragraphProperties: { tabStops: [{ pos: 1440 }] } }],
354362
},
355363
});
356364
const result = resolveParagraphProperties(params, { tabStops: [{ pos: 2160 }] });
@@ -361,16 +369,17 @@ describe('ooxml - resolveParagraphProperties', () => {
361369
const params = buildParams({
362370
translatedLinkedStyles: {
363371
...emptyStyles,
364-
styles: {
365-
TableStyle1: {
372+
styles: [
373+
{
374+
styleId: 'TableStyle1',
366375
type: 'table',
367376
paragraphProperties: { spacing: { before: 120, after: 120 }, keepNext: true },
368377
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
369378
tableStyleProperties: {
370379
firstRow: { paragraphProperties: { spacing: { after: 240 } } },
371380
},
372381
},
373-
},
382+
],
374383
},
375384
});
376385
const tableInfo = {
@@ -391,8 +400,9 @@ describe('ooxml - resolveCellStyles', () => {
391400
const params = buildParams({
392401
translatedLinkedStyles: {
393402
...emptyStyles,
394-
styles: {
395-
TableStyleBand: {
403+
styles: [
404+
{
405+
styleId: 'TableStyleBand',
396406
type: 'table',
397407
tableProperties: { tableStyleRowBandSize: 2, tableStyleColBandSize: 3 },
398408
tableStyleProperties: {
@@ -403,7 +413,7 @@ describe('ooxml - resolveCellStyles', () => {
403413
band2Horz: { runProperties: { fontSize: 50 } },
404414
},
405415
},
406-
},
416+
],
407417
},
408418
});
409419
const tableInfo = {
@@ -421,9 +431,10 @@ describe('ooxml - resolveCellStyles', () => {
421431
describe('ooxml - resolveTableCellProperties', () => {
422432
const gridTable4Styles = {
423433
...emptyStyles,
424-
styles: {
425-
'GridTable4-Accent1': {
426-
type: 'table',
434+
styles: [
435+
{
436+
styleId: 'GridTable4-Accent1',
437+
type: 'table' as const,
427438
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
428439
tableStyleProperties: {
429440
firstRow: {
@@ -444,7 +455,7 @@ describe('ooxml - resolveTableCellProperties', () => {
444455
},
445456
},
446457
},
447-
},
458+
],
448459
};
449460

450461
it('resolves firstRow shading from table style', () => {
@@ -474,7 +485,6 @@ describe('ooxml - resolveTableCellProperties', () => {
474485
numCells: 4,
475486
};
476487
const result = resolveTableCellProperties(null, tableInfo, gridTable4Styles);
477-
// band1Horz overrides wholeTable
478488
expect(result.shading).toEqual({ val: 'clear', color: 'auto', fill: 'C1E4F5' });
479489
});
480490

@@ -540,7 +550,6 @@ describe('ooxml - resolveTableCellProperties', () => {
540550
};
541551
const inlineProps = { borders: { bottom: { val: 'double', color: '000000', size: 8 } } };
542552
const result = resolveTableCellProperties(inlineProps, tableInfo, gridTable4Styles);
543-
// firstRow style provides top border, inline provides bottom border - both should be present
544553
expect(result.borders?.top).toEqual({ val: 'single', color: '156082', size: 4 });
545554
expect(result.borders?.bottom).toEqual({ val: 'double', color: '000000', size: 8 });
546555
});

0 commit comments

Comments
 (0)