Skip to content

Commit d60b4f2

Browse files
committed
feat: upgrade meta.docx in yjs docs to real Y.Map
1 parent 74fca9c commit d60b4f2

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
@@ -11,7 +11,7 @@ import {
1111
type OoxmlResolverParams,
1212
} from './index.js';
1313

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

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

55+
it('refreshes cached lookups after in-place styles mutations', () => {
56+
const styles = [{ styleId: 'Heading1', runProperties: { fontSize: 32, bold: true } }];
57+
const params = buildParams({
58+
translatedLinkedStyles: {
59+
...emptyStyles,
60+
styles,
61+
},
62+
});
63+
64+
expect(resolveStyleChain('runProperties', params, 'Heading1')).toEqual({ fontSize: 32, bold: true });
65+
66+
styles.push({ styleId: 'Heading2', runProperties: { italic: true } });
67+
expect(resolveStyleChain('runProperties', params, 'Heading2')).toEqual({ italic: true });
68+
69+
styles[0].styleId = 'Heading1Renamed';
70+
expect(resolveStyleChain('runProperties', params, 'Heading1Renamed')).toEqual({ fontSize: 32, bold: true });
71+
});
72+
5773
it('returns empty object when styleId is missing from definitions', () => {
5874
const params = buildParams();
5975
const result = resolveStyleChain('runProperties', params, 'MissingStyle');
@@ -151,9 +167,7 @@ describe('ooxml - resolveRunProperties', () => {
151167
translatedLinkedStyles: {
152168
...emptyStyles,
153169
docDefaults: { runProperties: { fontSize: 20 } },
154-
styles: {
155-
Normal: { default: true, runProperties: { fontSize: 22 } },
156-
},
170+
styles: [{ styleId: 'Normal', default: true, runProperties: { fontSize: 22 } }],
157171
},
158172
});
159173
const result = resolveRunProperties(params, null, null);
@@ -165,9 +179,7 @@ describe('ooxml - resolveRunProperties', () => {
165179
translatedLinkedStyles: {
166180
...emptyStyles,
167181
docDefaults: { runProperties: { fontSize: 20, color: { val: 'AAAAAA' } } },
168-
styles: {
169-
Normal: { default: false, runProperties: { fontSize: 22, color: { val: 'BBBBBB' } } },
170-
},
182+
styles: [{ styleId: 'Normal', default: false, runProperties: { fontSize: 22, color: { val: 'BBBBBB' } } }],
171183
},
172184
});
173185
const result = resolveRunProperties(params, null, null);
@@ -178,10 +190,10 @@ describe('ooxml - resolveRunProperties', () => {
178190
const params = buildParams({
179191
translatedLinkedStyles: {
180192
...emptyStyles,
181-
styles: {
182-
TOC1: { runProperties: { bold: true } },
183-
Emphasis: { runProperties: { italic: true } },
184-
},
193+
styles: [
194+
{ styleId: 'TOC1', runProperties: { bold: true } },
195+
{ styleId: 'Emphasis', runProperties: { italic: true } },
196+
],
185197
},
186198
});
187199
const result = resolveRunProperties(params, { styleId: 'Emphasis', color: { val: 'FF0000' } }, { styleId: 'TOC1' });
@@ -220,8 +232,9 @@ describe('ooxml - resolveRunProperties', () => {
220232
const params = buildParams({
221233
translatedLinkedStyles: {
222234
...emptyStyles,
223-
styles: {
224-
TableStyle1: {
235+
styles: [
236+
{
237+
styleId: 'TableStyle1',
225238
type: 'table',
226239
runProperties: { color: { val: 'AAAAAA' } },
227240
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
@@ -234,7 +247,7 @@ describe('ooxml - resolveRunProperties', () => {
234247
nwCell: { runProperties: { fontSize: 15 } },
235248
},
236249
},
237-
},
250+
],
238251
},
239252
});
240253
const tableInfo = {
@@ -278,9 +291,7 @@ describe('ooxml - resolveParagraphProperties', () => {
278291
translatedLinkedStyles: {
279292
...emptyStyles,
280293
docDefaults: { paragraphProperties: { spacing: { before: 240 } } },
281-
styles: {
282-
Normal: { default: true, paragraphProperties: { spacing: { after: 120 } } },
283-
},
294+
styles: [{ styleId: 'Normal', default: true, paragraphProperties: { spacing: { after: 120 } } }],
284295
},
285296
});
286297
const inlineProps = { spacing: { before: 480 } };
@@ -292,9 +303,7 @@ describe('ooxml - resolveParagraphProperties', () => {
292303
const params = buildParams({
293304
translatedLinkedStyles: {
294305
...emptyStyles,
295-
styles: {
296-
ListStyle: { paragraphProperties: { indent: { left: 1200 } } },
297-
},
306+
styles: [{ styleId: 'ListStyle', paragraphProperties: { indent: { left: 1200 } } }],
298307
},
299308
translatedNumbering: {
300309
definitions: { '1': { abstractNumId: 10 } },
@@ -318,13 +327,14 @@ describe('ooxml - resolveParagraphProperties', () => {
318327
const params = buildParams({
319328
translatedLinkedStyles: {
320329
...emptyStyles,
321-
styles: {
322-
BaseStyle: { paragraphProperties: { indent: { left: 2000 } } },
323-
NumberedStyle: {
330+
styles: [
331+
{ styleId: 'BaseStyle', paragraphProperties: { indent: { left: 2000 } } },
332+
{
333+
styleId: 'NumberedStyle',
324334
basedOn: 'BaseStyle',
325335
paragraphProperties: { numberingProperties: { numId: 1, ilvl: 0 } },
326336
},
327-
},
337+
],
328338
},
329339
translatedNumbering: {
330340
definitions: { '1': { abstractNumId: 10 } },
@@ -347,9 +357,7 @@ describe('ooxml - resolveParagraphProperties', () => {
347357
translatedLinkedStyles: {
348358
...emptyStyles,
349359
docDefaults: { paragraphProperties: { tabStops: [{ pos: 720 }] } },
350-
styles: {
351-
Normal: { default: true, paragraphProperties: { tabStops: [{ pos: 1440 }] } },
352-
},
360+
styles: [{ styleId: 'Normal', default: true, paragraphProperties: { tabStops: [{ pos: 1440 }] } }],
353361
},
354362
});
355363
const result = resolveParagraphProperties(params, { tabStops: [{ pos: 2160 }] });
@@ -360,16 +368,17 @@ describe('ooxml - resolveParagraphProperties', () => {
360368
const params = buildParams({
361369
translatedLinkedStyles: {
362370
...emptyStyles,
363-
styles: {
364-
TableStyle1: {
371+
styles: [
372+
{
373+
styleId: 'TableStyle1',
365374
type: 'table',
366375
paragraphProperties: { spacing: { before: 120, after: 120 }, keepNext: true },
367376
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
368377
tableStyleProperties: {
369378
firstRow: { paragraphProperties: { spacing: { after: 240 } } },
370379
},
371380
},
372-
},
381+
],
373382
},
374383
});
375384
const tableInfo = {
@@ -390,8 +399,9 @@ describe('ooxml - resolveCellStyles', () => {
390399
const params = buildParams({
391400
translatedLinkedStyles: {
392401
...emptyStyles,
393-
styles: {
394-
TableStyleBand: {
402+
styles: [
403+
{
404+
styleId: 'TableStyleBand',
395405
type: 'table',
396406
tableProperties: { tableStyleRowBandSize: 2, tableStyleColBandSize: 3 },
397407
tableStyleProperties: {
@@ -402,7 +412,7 @@ describe('ooxml - resolveCellStyles', () => {
402412
band2Horz: { runProperties: { fontSize: 50 } },
403413
},
404414
},
405-
},
415+
],
406416
},
407417
});
408418
const tableInfo = {
@@ -420,9 +430,10 @@ describe('ooxml - resolveCellStyles', () => {
420430
describe('ooxml - resolveTableCellProperties', () => {
421431
const gridTable4Styles = {
422432
...emptyStyles,
423-
styles: {
424-
'GridTable4-Accent1': {
425-
type: 'table',
433+
styles: [
434+
{
435+
styleId: 'GridTable4-Accent1',
436+
type: 'table' as const,
426437
tableProperties: { tableStyleRowBandSize: 1, tableStyleColBandSize: 1 },
427438
tableStyleProperties: {
428439
firstRow: {
@@ -443,7 +454,7 @@ describe('ooxml - resolveTableCellProperties', () => {
443454
},
444455
},
445456
},
446-
},
457+
],
447458
};
448459

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

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

0 commit comments

Comments
 (0)