Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
109 changes: 108 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/ai/src/editor-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class EditorAdapter {
* @param from - Start position to scroll to
* @param to - End position to scroll to
*/
scrollToPosition(from: number, to: number): void {
scrollToPosition(from: number, _to: number): void {
const { view } = this.editor;
if (!view?.domAtPos) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ export function computeSectionMetadataHash(sections: SectionMetadata[]): string
const parts: string[] = [];

for (const section of sections) {
parts.push(`section:${section.sectionIndex}`);
const sectionId = (section as { id?: string | null }).id;
parts.push(`section:${sectionId ?? section.sectionIndex}`);

// Include section break type if present
const sectionType = (section as { type?: string | null }).type;
if (sectionType) {
parts.push(`type:${sectionType}`);
}

// Include numbering properties that affect display
if (section.numbering) {
Expand Down
78 changes: 0 additions & 78 deletions packages/layout-engine/layout-engine/src/layout-paragraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,83 +147,6 @@ const asSafeNumber = (value: unknown): number => {
return value;
};

/**
* Calculates the first line indent for list markers when remeasuring paragraphs.
*
* In Word layout, there are two distinct list marker layout patterns:
*
* 1. **firstLineIndentMode** (marker inline with text):
* - The marker is positioned at `left + firstLine` and consumes horizontal space on the first line
* - Text begins after the marker (at `textStartPx`)
* - The first line's available width must account for the marker's width
* - This pattern is indicated by `firstLineIndentMode === true`
*
* 2. **Standard hanging indent** (marker in hanging area):
* - The marker is positioned absolutely in the hanging region at `left - hanging`
* - The marker does NOT consume horizontal space from the text flow
* - Text begins at `left` on ALL lines (first and subsequent)
* - The first line's available width is the same as subsequent lines
* - This is the default pattern when `firstLineIndentMode` is not set
*
* This function determines which pattern is in use and calculates the appropriate
* first line indent for the remeasurement operation.
*
* @param block - The paragraph block being remeasured
* @param measure - The current paragraph measurement (may contain marker measurements)
* @returns The first line indent in pixels. Returns 0 for standard hanging indent,
* or the marker width + gutter width for firstLineIndentMode.
*
* @example
* ```typescript
* // Standard hanging indent - marker doesn't consume first line space
* const block1 = {
* attrs: {
* wordLayout: {
* marker: { markerBoxWidthPx: 20 },
* // firstLineIndentMode is NOT set
* }
* }
* };
* const indent1 = calculateFirstLineIndent(block1, measure);
* // Returns: 0 (marker is in hanging area)
*
* // firstLineIndentMode - marker consumes first line space
* const block2 = {
* attrs: {
* wordLayout: {
* marker: { markerBoxWidthPx: 20 },
* firstLineIndentMode: true
* }
* }
* };
* const indent2 = calculateFirstLineIndent(block2, measure);
* // Returns: markerWidth + gutterWidth (marker is inline)
* ```
*/
function calculateFirstLineIndent(block: ParagraphBlock, measure: ParagraphMeasure): number {
const wordLayout = block.attrs?.wordLayout as WordLayoutAttrs | undefined;

// Only apply first line indent in firstLineIndentMode
if (!wordLayout?.firstLineIndentMode) {
return 0;
}

// Ensure marker exists in both wordLayout and measure
if (!wordLayout.marker || !measure.marker) {
return 0;
}

// Extract marker width with fallback chain and validation
const markerWidthRaw = measure.marker.markerWidth ?? wordLayout.marker.markerBoxWidthPx ?? 0;
const markerWidth = Number.isFinite(markerWidthRaw) && markerWidthRaw >= 0 ? markerWidthRaw : 0;

// Extract gutter width with validation
const gutterWidthRaw = measure.marker.gutterWidth ?? 0;
const gutterWidth = Number.isFinite(gutterWidthRaw) && gutterWidthRaw >= 0 ? gutterWidthRaw : 0;

return markerWidth + gutterWidth;
}

export type ParagraphLayoutContext = {
block: ParagraphBlock;
measure: ParagraphMeasure;
Expand Down Expand Up @@ -408,7 +331,6 @@ export function layoutParagraphBlock(ctx: ParagraphLayoutContext, anchors?: Para
const negativeRightIndent = indentRight < 0 ? indentRight : 0;
// Paragraph content width should honor paragraph indents (including negative values).
const remeasureWidth = Math.max(1, columnWidth - indentLeft - indentRight);
const hasNegativeIndent = indentLeft < 0 || indentRight < 0;
let didRemeasureForColumnWidth = false;
if (
typeof remeasureParagraph === 'function' &&
Expand Down
11 changes: 2 additions & 9 deletions packages/layout-engine/painters/dom/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import type {
ImageDrawing,
ParagraphAttrs,
ParagraphBorder,
ParagraphBorders,
ListItemFragment,
ListBlock,
ListMeasure,
Expand Down Expand Up @@ -67,12 +66,7 @@ import { sanitizeHref, encodeTooltip } from '@superdoc/url-validation';
import { renderTableFragment as renderTableFragmentElement } from './table/renderTableFragment.js';
import { assertPmPositions, assertFragmentPmPositions } from './pm-position-validation.js';
import { applySdtContainerStyling } from './utils/sdt-helpers.js';
import {
generateRulerDefinitionFromPx,
createRulerElement,
ensureRulerStyles,
RULER_CLASS_NAMES,
} from './ruler/index.js';
import { generateRulerDefinitionFromPx, createRulerElement, ensureRulerStyles } from './ruler/index.js';
import { toCssFontFamily } from '../../../../../shared/font-utils/index.js';
import {
hashParagraphBorders,
Expand Down Expand Up @@ -1887,7 +1881,6 @@ export class DomPainter {
// When using explicit segment positioning, segments are absolutely positioned and textIndent
// has no effect, so we skip it to avoid confusion.
// Also skip when left indent is negative - fragment positioning already handles that case.
const hasNegativeLeftIndent = paraIndentLeft != null && paraIndentLeft < 0;
if (!fragment.continuesFromPrev && index === 0 && firstLineOffset && !isListFirstLine) {
if (!hasExplicitSegmentPositioning) {
lineEl.style.textIndent = `${firstLineOffset}px`;
Expand Down Expand Up @@ -3767,7 +3760,7 @@ export class DomPainter {
context: FragmentRenderContext,
availableWidthOverride?: number,
lineIndex?: number,
skipJustify?: boolean,
_skipJustify?: boolean,
): HTMLElement {
if (!this.doc) {
throw new Error('DomPainter: document is not available');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export const hydrateParagraphStyleAttrs = (
}

const hydrated: ParagraphStyleHydration = {
resolved,
resolved: resolved as ResolvedParagraphProperties,
spacing: resolvedSpacing,
indent: resolvedIndent,
borders: cloneIfObject(resolvedExtended.borders) as ParagraphAttrs['borders'],
Expand Down
2 changes: 1 addition & 1 deletion packages/super-editor/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
</head>
<body>
<div id="app" role="application"></div>
<script type="module" src="/src/main.js"></script>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
20 changes: 14 additions & 6 deletions packages/super-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
"build": "vite build",
"build:watch": "vite build --watch",
"types:check": "tsc --noEmit",
"types:check:strict": "tsc -p tsconfig.strict.json",
"types:check:extensions": "tsc -p tsconfig.extensions.json",
"types:check:vue": "vue-tsc --noEmit",
"types:check:test": "tsc -p tsconfig.test.json",
"types:check:all": "npm run types:check:strict && npm run types:check:vue && npm run types:check:test",
"types:build": "tsc -p tsconfig.build.json",
"test": "vitest",
"test:debug": "vitest --inspect-brk --pool threads --poolOptions.threads.singleThread",
Expand All @@ -53,13 +58,13 @@
"pack": "rm *.tgz 2>/dev/null || true && npm run build && npm pack && mv harbour-enterprises-super-editor-0.0.1-alpha.0.tgz ./super-editor.tgz"
},
"dependencies": {
"@superdoc/word-layout": "*",
"@superdoc/pm-adapter": "*",
"@superdoc/layout-bridge": "*",
"@superdoc/url-validation": "*",
"@superdoc/painter-dom": "*",
"@superdoc/contracts": "*",
"@superdoc/layout-bridge": "*",
"@superdoc/measuring-dom": "*",
"@superdoc/painter-dom": "*",
"@superdoc/pm-adapter": "*",
"@superdoc/url-validation": "*",
"@superdoc/word-layout": "*",
"buffer-crc32": "^1.0.0",
"color2k": "^2.0.3",
"eventemitter3": "^5.0.1",
Expand Down Expand Up @@ -98,6 +103,8 @@
},
"devDependencies": {
"@floating-ui/dom": "^1.7.0",
"@typescript-eslint/eslint-plugin": "^8.48.0",
"@typescript-eslint/parser": "^8.48.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/test-utils": "^2.4.6",
"postcss-nested": "^6.0.1",
Expand All @@ -107,6 +114,7 @@
"typescript": "^5.7.3",
"vite": "^6.3.5",
"vite-plugin-node-polyfills": "^0.24.0",
"vitest": "^3.2.4"
"vitest": "^3.2.4",
"vue-tsc": "^3.1.5"
}
}
Loading
Loading