diff --git a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md index 4289f15ca5..38deab1766 100644 --- a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md +++ b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md @@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- We added new configuration to allow users to use class names instead of inline styling in generated HTML to support strict CSP. + +### Fixed + +- We fixed an issue where the editor pasting back the whole sentence instead of the single copied word + +### Changed + +- We removed codemirror from code dialog viewer due to unsupported strict CSP policy. A simple internally built code editor using highlightjs is now replacing it. + ## [4.12.0] - 2026-04-22 ### Added diff --git a/packages/pluggableWidgets/rich-text-web/e2e/RichText.spec.js b/packages/pluggableWidgets/rich-text-web/e2e/RichText.spec.js index c05860d869..e203f40cee 100644 --- a/packages/pluggableWidgets/rich-text-web/e2e/RichText.spec.js +++ b/packages/pluggableWidgets/rich-text-web/e2e/RichText.spec.js @@ -2,6 +2,7 @@ import { expect, test } from "@mendix/run-e2e/fixtures"; import { waitForMendixApp } from "@mendix/run-e2e/mendix-helpers"; test.describe("RichText", () => { + test.describe.configure({ mode: "serial" }); test("compares with a screenshot baseline and checks if inline basic mode are rendered as expected", async ({ page }) => { @@ -115,6 +116,37 @@ test.describe("RichText", () => { await expect(page.locator(".mx-name-richText6")).toHaveScreenshot(`readOnlyModeReadPanel.png`); }); + test("compares with a screenshot baseline and checks if class mode editor is rendered as expected", async ({ + page + }) => { + await page.goto("/p/classmode"); + await page.waitForLoadState("networkidle"); + await expect(page.locator(".mx-name-richText1")).toBeVisible(); + await expect(page.locator(".mx-name-richText1")).toHaveScreenshot(`classModeEditor.png`, { threshold: 0.4 }); + }); + + test("checks that class mode editor output uses CSS classes instead of inline styles", async ({ page }) => { + await page.goto("/p/classmode"); + await page.waitForLoadState("networkidle"); + const html = await page.locator(".mx-name-richText1 .ql-editor").innerHTML(); + expect(html).toMatch(/class="ql-color-/); + expect(html).toMatch(/class="ql-bg-/); + expect(html).toMatch(/class="ql-indent-/); + expect(html).toMatch(/data-style-format="class"/); + expect(html).not.toMatch(/style="color:/); + expect(html).not.toMatch(/style="background-color:/); + expect(html).not.toMatch(/style="padding-left:/); + }); + + test("compares with a screenshot baseline of the View/Edit Code dialog in class mode", async ({ page }) => { + await page.goto("/p/classmode"); + await page.waitForLoadState("networkidle"); + await page.click(".mx-name-richText1 .ql-toolbar button.ql-view-code"); + await expect(page.locator(".widget-rich-text .widget-rich-text-modal-body").first()).toHaveScreenshot( + `classModeViewCodeDialog.png` + ); + }); + test("compares with a screenshot for rich text inside modal popup layout", async ({ page }) => { await page.goto("/"); await waitForMendixApp(page); diff --git a/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/.openspec.yaml b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/.openspec.yaml new file mode 100644 index 0000000000..c53ef21aaa --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-05 diff --git a/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/design.md b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/design.md new file mode 100644 index 0000000000..b1909f37d3 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/design.md @@ -0,0 +1,186 @@ +## Context + +The rich text editor uses TipTap, which already supports column widths via the `colwidth` attribute on table cells. TipTap's base `TableCell` extension includes this attribute, and the `TableBackgroundColor` extension reads it to generate `` elements for column sizing. + +Currently: + +- `colwidth` is an array of pixel values (e.g., `[150]` for single column, `[100, 150, 200]` for colspan) +- TipTap has a `resizable: true` option that should enable drag-to-resize, but the custom `TableBackgroundColorNodeView` doesn't implement resize handles +- Users can click column borders, which sets a width, but cannot drag to adjust +- No UI control exists to set exact pixel widths + +The existing cell configuration dropdown already has sections for background color, border color/style/width using a `ConfigurationSection` pattern. + +## Goals / Non-Goals + +**Goals:** + +- Add a number input control to the cell configuration dropdown for setting column width +- Support exact pixel values (25-1000px range) +- Provide clear button to reset to auto width +- Handle colspan cells by setting only the first column width +- Follow existing configuration dropdown patterns and styling +- Validate input and clamp to acceptable ranges + +**Non-Goals:** + +- Implementing drag-to-resize handles (would require significant NodeView work) +- Supporting percentage or other CSS units (only pixels for now) +- Setting widths for all columns in a colspan cell (only first column) +- Syncing widths across all rows (only first row widths apply per TipTap's design) +- Adding column width presets dropdown (keep it simple with number input only) + +## Decisions + +### Decision 1: Use number input type (not dropdown with presets) + +**Rationale:** User requested "exact pixel values" in requirements. A number input provides precision without limiting users to preset sizes. + +**Alternatives considered:** + +- Dropdown with presets (Small/Medium/Large): Too restrictive, doesn't meet "exact values" requirement +- Hybrid (input + preset buttons): Over-engineered for initial implementation +- Text input: Number input provides better UX with built-in increment/decrement and validation + +**Implementation:** Add new `"numberInput"` type to `ConfigurationSection` interface alongside existing `"colorPicker"` and `"dropdown"` types. + +--- + +### Decision 2: Set only first column width for colspan cells + +**Rationale:** + +- User explicitly requested "set only first column" in requirements +- Simplifies implementation and UX (no need for multi-column width editor) +- Matches TipTap's array-based `colwidth` structure where `colwidth[0]` is the first column + +**Alternatives considered:** + +- Set all spanned columns to same width: Could surprise users if they had different widths set +- Show per-column width array editor: Complex UI for edge case (most cells don't have colspan) +- Disable control for colspan cells: Too restrictive + +**Implementation:** `onChange` handler creates `colwidth` array with single value: `[width]` + +--- + +### Decision 3: Use setCellAttribute command (not custom command) + +**Rationale:** + +- `setCellAttribute("colwidth", value)` should work because `colwidth` is in TipTap's base TableCell +- Other cell properties (backgroundColor, borderColor, etc.) use `setCellAttribute` successfully +- No need to define custom commands in extensions + +**Alternatives considered:** + +- Custom `setCellWidth` command in `TableCellBackgroundColor`: Unnecessary abstraction +- Modify `TableCellBackgroundColor.addAttributes()` to re-declare colwidth: Already inherits via `...this.parent?.()` + +**Implementation:** Direct call to `editor.chain().focus().setCellAttribute("colwidth", [width]).run()` + +--- + +### Decision 4: Render clear button conditionally (when value exists) + +**Rationale:** + +- Provides explicit way to reset to auto width (null) +- Visual indicator that a custom width is set +- Follows common UI pattern (seen in search inputs, etc.) + +**Alternatives considered:** + +- Always show clear button: Clutters UI when empty +- Use empty string to mean auto: Less explicit, could be confusing +- Add separate "Auto" button: Redundant with clear functionality + +**Implementation:** Render `×` button only when `currentValue !== null` + +--- + +### Decision 5: Store null for auto width (not empty array or zero) + +**Rationale:** + +- TipTap treats `colwidth: null` as auto-sizing behavior +- Consistent with how TipTap's base extensions work +- `colwidth: []` or `colwidth: [0]` could cause layout issues + +**Implementation:** When user clears input, call `setCellAttribute("colwidth", null)` + +## Risks / Trade-offs + +### Risk: Parent attribute inheritance might not work + +If `TableCellBackgroundColor` doesn't properly inherit `colwidth` from base `TableCell`, `setCellAttribute` calls will fail silently. + +**Mitigation:** The extension already uses `...this.parent?.()` in `addAttributes()`, which should preserve `colwidth`. If issues arise, explicitly re-declare `colwidth` in the extension's attributes. + +--- + +### Risk: First-row-only behavior might confuse users + +TipTap only uses `colwidth` from the first row of the table to generate ``. Setting widths on other rows has no effect. + +**Mitigation:** Could add tooltip or helper text: "Note: Only first row column widths apply." For initial implementation, leave as-is since it matches TipTap's inherent behavior. + +--- + +### Risk: Custom NodeView might interfere with colwidth updates + +The `TableBackgroundColorNodeView` manually generates `` in `updateColgroup()`. If it's not reactive to `colwidth` changes, widths might not update visually. + +**Mitigation:** The NodeView's `update()` method already calls `updateColgroup()` on node changes, so it should react to `colwidth` updates. Test thoroughly during implementation. + +--- + +### Trade-off: No drag-to-resize (only manual input) + +Users lose the convenience of dragging column borders to resize. + +**Mitigation:** The number input provides precision that drag handles lack. If drag-to-resize is needed later, it would require integrating ProseMirror's `columnResizing` plugin into the custom NodeView (~200 lines of code). For now, manual input meets the stated requirements. + +--- + +### Trade-off: Pixel-only units (no percentages) + +Users cannot set responsive column widths using percentages. + +**Mitigation:** Percentage support would require custom rendering logic (TipTap's `colwidth` is pixel-only). Tables in rich text editors typically use fixed layouts. Can be added later if users request it. + +## Migration Plan + +No migration needed. This is a purely additive feature: + +- Existing tables without `colwidth` continue to work (auto width) +- Existing tables with `colwidth` set (via TipTap's base functionality) display correctly +- No data model changes or breaking API changes + +Deployment: + +1. Merge code changes +2. Run build +3. Deploy widget to Mendix project +4. Feature is immediately available in cell configuration dropdown + +Rollback: + +- If issues arise, remove the new configuration section from `createCellConfigurationSections()` +- No data corruption risk (colwidth attribute is standard TipTap) + +## Open Questions + +1. **Should we show the actual rendered column width vs. the set value?** + - The set `colwidth` might differ from actual rendered width due to table layout constraints + - For initial implementation: Show the set value only (simpler) + - Can add "measured width" tooltip later if needed + +2. **Should we prevent setting widths on non-first-row cells?** + - Pro: Avoids confusion about why widths don't apply + - Con: Restricts user control, might not match mental model + - Decision: Allow setting on any cell (simpler), rely on TipTap's first-row behavior + +3. **Should we add keyboard shortcuts for common widths?** + - Out of scope for initial implementation + - Can be added later if users request it diff --git a/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/proposal.md b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/proposal.md new file mode 100644 index 0000000000..282fd1302a --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/proposal.md @@ -0,0 +1,41 @@ +## Why + +Users need precise control over table column widths in the rich text editor. Currently, while TipTap supports column widths via the `colwidth` attribute, there's no UI control to set them. Users can only click column borders which sets widths without drag-to-resize capability, making it difficult to achieve desired layouts. + +## What Changes + +- Add a "Column Width" number input control to the cell configuration dropdown +- Support setting exact pixel widths (25-1000px range) +- Provide "Clear" button to reset to auto width +- Handle colspan cells by setting only the first column width +- Integrate with existing `setCellAttribute` command for `colwidth` attribute + +## Capabilities + +### New Capabilities + +- `table-column-width-control`: UI control in cell configuration dropdown allowing users to set precise column widths in pixels, with validation, auto width support, and colspan handling + +### Modified Capabilities + + + +## Impact + +**Affected Files:** + +- `src/components/toolbars/components/ConfigurationDropdown.tsx` - Add number input type support +- `src/components/toolbars/components/ConfigurationDropdown.scss` - Add number input styling +- `src/components/toolbars/ToolbarConfig.ts` - Update ConfigurationSection type definition +- `src/components/toolbars/helpers/configurationHelpers.ts` - Add column width section + +**User Impact:** + +- Positive: Users gain precise control over table column widths via familiar number input UI +- No breaking changes: Feature is additive, existing tables continue to work + +**Technical Impact:** + +- Uses existing TipTap `colwidth` attribute mechanism (no changes to data model) +- No impact on existing table resize behavior (custom NodeView remains unchanged) +- Follows established configuration dropdown patterns diff --git a/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/specs/table-column-width-control/spec.md b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/specs/table-column-width-control/spec.md new file mode 100644 index 0000000000..1bafe2051b --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/specs/table-column-width-control/spec.md @@ -0,0 +1,102 @@ +## ADDED Requirements + +### Requirement: Column width input control + +The cell configuration dropdown SHALL include a number input control labeled "Column Width" that allows users to set the column width in pixels. + +#### Scenario: Opening cell configuration shows current width + +- **WHEN** user selects a table cell and opens the cell configuration dropdown +- **THEN** the "Column Width" input displays the current column width value if set, or shows empty with "Auto" placeholder if width is auto + +#### Scenario: Setting a valid column width + +- **WHEN** user enters a numeric value between 25 and 1000 in the "Column Width" input +- **THEN** the column width is set to that exact pixel value and the column resizes accordingly + +#### Scenario: Clearing column width for auto sizing + +- **WHEN** user clicks the clear button or deletes the value from the "Column Width" input +- **THEN** the column width is reset to auto (null) and the column resizes to fit content + +### Requirement: Column width validation + +The system SHALL validate column width input to ensure values are within acceptable ranges. + +#### Scenario: Entering value below minimum + +- **WHEN** user enters a value less than 25 pixels +- **THEN** the system clamps the value to 25 pixels + +#### Scenario: Entering value above maximum + +- **WHEN** user enters a value greater than 1000 pixels +- **THEN** the system clamps the value to 1000 pixels + +#### Scenario: Entering invalid input + +- **WHEN** user enters non-numeric text +- **THEN** the system ignores the input and retains the previous value + +### Requirement: Colspan cell width handling + +The system SHALL set only the first column width when the selected cell has a colspan greater than 1. + +#### Scenario: Setting width on colspan cell + +- **WHEN** user sets column width on a cell with colspan=3 that currently has colwidth=[100, 150, 200] +- **THEN** the system sets colwidth=[new_value] affecting only the first spanned column + +#### Scenario: Reading width from colspan cell + +- **WHEN** user opens cell configuration for a cell with colspan=3 and colwidth=[100, 150, 200] +- **THEN** the "Column Width" input displays 100 (the first column's width) + +### Requirement: Number input UI component + +The configuration dropdown SHALL support a "numberInput" type with number-specific controls. + +#### Scenario: Number input with unit label + +- **WHEN** a configuration section has type "numberInput" with unit "px" +- **THEN** the UI renders a number input field with "px" unit label beside it + +#### Scenario: Number input with clear button + +- **WHEN** a configuration section has type "numberInput" and the field has a value +- **THEN** the UI displays a clear button (×) that resets the field to empty when clicked + +#### Scenario: Number input with placeholder + +- **WHEN** a configuration section has type "numberInput" with placeholder "Auto" +- **THEN** the empty input field shows "Auto" as placeholder text + +### Requirement: Column width persistence + +The system SHALL persist column width values in the document's colwidth attribute. + +#### Scenario: Width persists after save and reload + +- **WHEN** user sets a column width to 150 pixels, saves the document, and reloads the page +- **THEN** the column width remains 150 pixels and is displayed correctly in the cell configuration + +#### Scenario: Width persists across editing sessions + +- **WHEN** user sets column widths on multiple columns and closes the editor +- **THEN** reopening the document displays all columns at their configured widths + +### Requirement: Visual feedback + +The system SHALL provide clear visual feedback when column width changes. + +#### Scenario: Immediate column resize on input + +- **WHEN** user changes the column width value in the input field +- **THEN** the table column resizes immediately without requiring additional confirmation + +#### Scenario: Clear button visibility + +- **WHEN** the column width input has a value +- **THEN** the clear button is visible +- **WHEN** the column width input is empty +- **THEN** the clear button is hidden diff --git a/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/tasks.md b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/tasks.md new file mode 100644 index 0000000000..dbd8a58ee0 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/openspec/changes/add-cell-column-width/tasks.md @@ -0,0 +1,48 @@ +## 1. Type Definition Updates + +- [x] 1.1 Update `ConfigurationSection` interface in `src/components/toolbars/ToolbarConfig.ts` to add `"numberInput"` to type union +- [x] 1.2 Add optional fields to `ConfigurationSection` interface: `min?: number`, `max?: number`, `step?: number`, `placeholder?: string`, `unit?: string` +- [x] 1.3 Update `ConfigurationSection` interface in `src/components/toolbars/components/ConfigurationDropdown.tsx` to match the updated type definition +- [x] 1.4 Update `getCurrentValue` return type to allow `string | number | null` instead of just `string | null` + +## 2. UI Component Implementation + +- [x] 2.1 Add number input rendering case in `ConfigurationDropdown.tsx` after the dropdown case (around line 73) +- [x] 2.2 Create wrapper div with className `configuration-number-input` containing the input, unit label, and clear button +- [x] 2.3 Implement number input with type="number", min/max/step/placeholder props from section config +- [x] 2.4 Implement conditional unit label display using `configuration-unit` className when `section.unit` exists +- [x] 2.5 Implement conditional clear button (×) with `configuration-clear-button` className, shown only when `currentValue !== null` +- [x] 2.6 Wire up onChange handler to call `section.onChange(e.target.value)` on input change +- [x] 2.7 Wire up clear button onClick handler to call `section.onChange("")` to reset to auto width + +## 3. Styling + +- [x] 3.1 Add `.configuration-number-input` styles in `ConfigurationDropdown.scss` with flex layout and 4px gap +- [x] 3.2 Add `.configuration-input` styles matching `.configuration-select` with padding, borders, transitions, and focus states +- [x] 3.3 Add number input specific styles to hide spinner arrows (`::-webkit-inner-spin-button`, `::-webkit-outer-spin-button`, `-moz-appearance: textfield`) +- [x] 3.4 Add `.configuration-input::placeholder` styles with gray color and italic font +- [x] 3.5 Add `.configuration-unit` styles with 12px font size, gray color, and nowrap +- [x] 3.6 Add `.configuration-clear-button` styles with padding, borders, hover/active states matching the design + +## 4. Cell Configuration Logic + +- [x] 4.1 Add new configuration section object in `createCellConfigurationSections()` function in `src/components/toolbars/helpers/configurationHelpers.ts` after `cellBorderWidth` +- [x] 4.2 Set section id to `"cellWidth"`, label to `"Column Width"`, type to `"numberInput"` +- [x] 4.3 Set min to 25, max to 1000, step to 1, placeholder to "Auto", unit to "px" +- [x] 4.4 Implement `getCurrentValue` function that calls `getCellAttributes(editor)`, extracts `colwidth` array, and returns `colwidth[0]` or null +- [x] 4.5 Implement `onChange` function that parses input value, validates it, clamps to min/max range, and calls `setCellAttribute("colwidth", [value])` or `setCellAttribute("colwidth", null)` for empty input +- [x] 4.6 Handle edge case where input is empty string by calling `setCellAttribute("colwidth", null)` to reset to auto width +- [x] 4.7 Handle colspan cells by creating colwidth array with single value `[clampedValue]` regardless of colspan + +## 5. Testing & Verification + +- [x] 5.1 Test setting column width to valid value (e.g., 150px) and verify column resizes +- [x] 5.2 Test clearing column width via clear button and verify column auto-sizes +- [x] 5.3 Test entering value below minimum (e.g., 10) and verify it clamps to 25 +- [x] 5.4 Test entering value above maximum (e.g., 2000) and verify it clamps to 1000 +- [x] 5.5 Test entering invalid input (e.g., "abc") and verify it's ignored +- [x] 5.6 Test with colspan cell (colspan=3) and verify only first column width is set +- [x] 5.7 Test that width persists after save/reload of document +- [x] 5.8 Test that clear button visibility toggles correctly (hidden when empty, shown when value exists) +- [x] 5.9 Test that placeholder "Auto" displays when input is empty +- [x] 5.10 Test keyboard navigation and accessibility of number input control diff --git a/packages/pluggableWidgets/rich-text-web/openspec/changes/code-view-highlighting/.openspec.yaml b/packages/pluggableWidgets/rich-text-web/openspec/changes/code-view-highlighting/.openspec.yaml new file mode 100644 index 0000000000..e0c0898ffd --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/openspec/changes/code-view-highlighting/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-11 diff --git a/packages/pluggableWidgets/rich-text-web/openspec/changes/code-view-highlighting/README.md b/packages/pluggableWidgets/rich-text-web/openspec/changes/code-view-highlighting/README.md new file mode 100644 index 0000000000..cecd8401d8 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/openspec/changes/code-view-highlighting/README.md @@ -0,0 +1,3 @@ +# code-view-highlighting + +Add syntax highlighting to code view mode using react-simple-code-editor and highlight.js diff --git a/packages/pluggableWidgets/rich-text-web/package.json b/packages/pluggableWidgets/rich-text-web/package.json index 5fb882b303..bdc1a819ba 100644 --- a/packages/pluggableWidgets/rich-text-web/package.json +++ b/packages/pluggableWidgets/rich-text-web/package.json @@ -1,7 +1,7 @@ { "name": "@mendix/rich-text-web", "widgetName": "RichText", - "version": "4.12.0", + "version": "4.13.0", "description": "Rich inline or toolbar text editing", "copyright": "© Mendix Technology BV 2025. All rights reserved.", "license": "Apache-2.0", @@ -24,15 +24,15 @@ }, "testProject": { "githubUrl": "https://github.com/mendix/testProjects", - "branchName": "rich-text-v4-web" + "branchName": "rich-text-v4-web-v2" }, "scripts": { "build": "cross-env MPKOUTPUT=RichText.mpk pluggable-widgets-tools build:web", "create-gh-release": "rui-create-gh-release", "create-translation": "rui-create-translation", "dev": "cross-env MPKOUTPUT=RichText.mpk pluggable-widgets-tools start:web", - "e2e": "run-e2e ci", - "e2edev": "run-e2e dev --with-preps", + "e2e": "MENDIX_VERSION=11.9.1 run-e2e ci", + "e2edev": "MENDIX_VERSION=11.9.1 run-e2e dev --with-preps", "format": "prettier --ignore-path ./node_modules/@mendix/prettier-config-web-widgets/global-prettierignore --write .", "lint": "eslint src/ package.json", "publish-marketplace": "rui-publish-marketplace", @@ -43,21 +43,43 @@ "verify": "rui-verify-package-format" }, "dependencies": { - "@codemirror/lang-html": "^6.4.9", - "@codemirror/state": "^6.5.2", "@floating-ui/dom": "^1.7.4", "@floating-ui/react": "^0.26.27", "@melloware/coloris": "^0.25.0", - "@uiw/codemirror-theme-github": "^4.23.13", - "@uiw/react-codemirror": "^4.23.13", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@tiptap/core": "^3.23.4", + "@tiptap/extension-color": "^3.23.4", + "@tiptap/extension-font-family": "^3.23.4", + "@tiptap/extension-highlight": "^3.23.5", + "@tiptap/extension-image": "^3.23.4", + "@tiptap/extension-link": "^3.23.4", + "@tiptap/extension-list": "^3.23.5", + "@tiptap/extension-list-item": "^3.23.5", + "@tiptap/extension-subscript": "^3.23.4", + "@tiptap/extension-superscript": "^3.23.4", + "@tiptap/extension-table": "^3.23.4", + "@tiptap/extension-table-cell": "^3.23.4", + "@tiptap/extension-table-header": "^3.23.4", + "@tiptap/extension-table-row": "^3.23.4", + "@tiptap/extension-task-item": "^3.23.5", + "@tiptap/extension-task-list": "^3.23.5", + "@tiptap/extension-text-align": "^3.23.4", + "@tiptap/extension-text-style": "^3.23.4", + "@tiptap/extension-underline": "^3.23.4", + "@tiptap/extension-youtube": "^3.23.4", + "@tiptap/pm": "^3.23.4", + "@tiptap/react": "^3.23.4", + "@tiptap/starter-kit": "^3.23.4", + "@uiw/react-color-compact": "^2.10.1", "classnames": "^2.5.1", + "highlight.js": "^11.11.1", "js-beautify": "^1.15.4", "katex": "^0.16.22", "linkifyjs": "^4.3.2", "lodash.merge": "^4.6.2", - "parchment": "^3.0.0", - "quill": "^2.0.3", - "quill-resize-module": "^2.0.4" + "react-dropzone": "^14.3.8", + "react-scroll-sync": "^1.0.2", + "react-simple-code-editor": "^0.14.1" }, "devDependencies": { "@mendix/automation-utils": "workspace:*", diff --git a/packages/pluggableWidgets/rich-text-web/src/RichText.editorConfig.ts b/packages/pluggableWidgets/rich-text-web/src/RichText.editorConfig.ts index 24a8972d85..abdc183d28 100644 --- a/packages/pluggableWidgets/rich-text-web/src/RichText.editorConfig.ts +++ b/packages/pluggableWidgets/rich-text-web/src/RichText.editorConfig.ts @@ -6,8 +6,6 @@ import { StructurePreviewProps } from "@mendix/widget-plugin-platform/preview/structure-preview-api"; import { RichTextPreviewProps } from "typings/RichTextProps"; -import RichTextPreviewSVGDark from "./assets/rich-text-preview-dark.svg"; -import RichTextPreviewSVGLight from "./assets/rich-text-preview-light.svg"; const toolbarGroupKeys: Array = [ "history", @@ -74,17 +72,16 @@ export function getProperties(values: RichTextPreviewProps, defaultProperties: P return defaultProperties; } -export function getPreview(props: RichTextPreviewProps, isDarkMode: boolean): StructurePreviewProps { - const variant = isDarkMode ? RichTextPreviewSVGDark : RichTextPreviewSVGLight; - const doc = decodeURIComponent(variant.replace("data:image/svg+xml,", "")); - +export function getPreview(props: RichTextPreviewProps, _isDarkMode: boolean): StructurePreviewProps { const richTextPreview = container()( rowLayout({ columnSize: "grow", borders: false })({ - type: "Image", - document: props.stringAttribute - ? doc.replace("[No attribute selected]", `[${props.stringAttribute}]`) - : doc, - height: 150 + type: "Container", + children: [ + { + type: "Text", + content: props.stringAttribute ? `Rich Text: ${props.stringAttribute}` : "Rich Text Editor" + } + ] }) ); diff --git a/packages/pluggableWidgets/rich-text-web/src/RichText.editorPreview.tsx b/packages/pluggableWidgets/rich-text-web/src/RichText.editorPreview.tsx index 8b7f935fa8..1cb5211223 100644 --- a/packages/pluggableWidgets/rich-text-web/src/RichText.editorPreview.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/RichText.editorPreview.tsx @@ -1,19 +1,10 @@ -import { ReactElement } from "react"; -import RichTextPreviewSVG from "./assets/rich-text-preview-light.svg"; +import { ReactElement, createElement } from "react"; import { RichTextPreviewProps } from "../typings/RichTextProps"; -export function preview(props: RichTextPreviewProps): ReactElement { - let doc = decodeURI(RichTextPreviewSVG); - doc = props.stringAttribute ? doc.replace("[No attribute selected]", `[${props.stringAttribute}]`) : doc; +export function preview(_props: RichTextPreviewProps): ReactElement { + return createElement("div", { className: "widget-rich-text-preview" }, "Rich Text Editor"); +} - return ( -
- - {props.imageSource && ( - -
- - )} -
- ); +export function getPreviewCss(): string { + return require("./ui/RichText.scss"); } diff --git a/packages/pluggableWidgets/rich-text-web/src/RichText.tsx b/packages/pluggableWidgets/rich-text-web/src/RichText.tsx index 283829650c..9eb127cb07 100644 --- a/packages/pluggableWidgets/rich-text-web/src/RichText.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/RichText.tsx @@ -1,59 +1,24 @@ import { ValidationAlert } from "@mendix/widget-plugin-component-kit/Alert"; import classNames from "classnames"; -import { Fragment, ReactElement, useEffect, useState } from "react"; +import { Fragment, ReactElement } from "react"; import { RichTextContainerProps } from "../typings/RichTextProps"; import EditorWrapper from "./components/EditorWrapper"; import "./ui/RichText.scss"; -import { constructWrapperStyle } from "./utils/helpers"; export default function RichText(props: RichTextContainerProps): ReactElement { - const { stringAttribute, readOnlyStyle } = props; - - const wrapperStyle = constructWrapperStyle(props); - const [isIncubator, setIsIncubator] = useState(true); - - useEffect(() => { - // this is a fix for dojo runtime rendering - // in dojo runtime, DOM is rendered inside
at the inital stage - // and moved to content once it fully loads, which cause rich text editor looses reference to it's iframe - // this fix waits for it to be fully out of incubator div, then only fully renders rich text afterwards. - const observedIncubator = document.querySelector(`.mx-incubator.mx-offscreen`); - const observer = new MutationObserver((_mutationList, _observer) => { - if (!observedIncubator?.childElementCount || observedIncubator?.childElementCount <= 0) { - setIsIncubator(false); - } - }); - - if (observedIncubator && observedIncubator.childElementCount) { - observer.observe(observedIncubator, { - childList: true - }); - } else { - // eslint-disable-next-line react-hooks/set-state-in-effect - setIsIncubator(false); - } - - return () => { - observer.disconnect(); - }; - }, []); + const { stringAttribute } = props; return ( - {stringAttribute.status === "loading" || isIncubator ? ( + {stringAttribute.status === "loading" ? (
) : ( )} {stringAttribute.validation} diff --git a/packages/pluggableWidgets/rich-text-web/src/RichText.xml b/packages/pluggableWidgets/rich-text-web/src/RichText.xml index 8fb841b775..ce242e9f53 100644 --- a/packages/pluggableWidgets/rich-text-web/src/RichText.xml +++ b/packages/pluggableWidgets/rich-text-web/src/RichText.xml @@ -17,9 +17,9 @@ - + Enable status bar - + Show a status bar at the bottom of the editor displaying document metrics @@ -220,6 +220,14 @@ Character count (including HTML) + + Style data format + Choose how to render styling attribute in HTML + + inline + class + + @@ -317,9 +325,10 @@ Code Code Block View Code - Left Align + Left Align Center Align Right Align + Justify Align Font Type Font Size Color diff --git a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx index 1ab4c89bad..870d49894d 100644 --- a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx @@ -47,7 +47,8 @@ describe("Rich Text", () => { customFonts: [], enableDefaultUpload: true, formOrientation: "vertical", - linkValidation: true + linkValidation: true, + styleDataFormat: "inline" }; }); diff --git a/packages/pluggableWidgets/rich-text-web/src/__tests__/customList.spec.ts b/packages/pluggableWidgets/rich-text-web/src/__tests__/customList.spec.ts new file mode 100644 index 0000000000..0fec09d663 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/__tests__/customList.spec.ts @@ -0,0 +1,59 @@ +import { CustomListItem, CustomListItemClass, STANDARD_LIST_TYPES } from "../utils/formats/customList"; + +// CustomListItem and CustomListItemClass extend Quill's ListItem blot. +// We test only the static helpers and the constructor-level DOM mutation, +// which do not require a live Quill / Scroll instance. + +function makeListNode(listType = "ordered"): HTMLElement { + const li = document.createElement("li"); + li.dataset.list = listType; + return li; +} + +describe("STANDARD_LIST_TYPES", () => { + it("contains exactly the four standard types", () => { + expect(STANDARD_LIST_TYPES).toEqual(["ordered", "checked", "unchecked", "bullet"]); + }); +}); + +describe("CustomListItem.formats", () => { + it("returns data-list value for standard list types", () => { + const node = makeListNode("ordered"); + expect(CustomListItem.formats(node)).toBe("ordered"); + }); + + it("prefers data-custom-list over data-list when both are present", () => { + const node = makeListNode("ordered"); + node.dataset.customList = "lower-alpha"; + expect(CustomListItem.formats(node)).toBe("lower-alpha"); + }); + + it("returns undefined when neither attribute is present", () => { + const node = document.createElement("li"); + expect(CustomListItem.formats(node)).toBeUndefined(); + }); +}); + +describe("CustomListItemClass — styleFormat marker contract", () => { + // CustomListItemClass constructor assigns domNode.dataset.styleFormat = "class". + // Instantiating it requires a live Quill Scroll instance (a Quill integration concern), + // so here we verify the contract at the class-definition level and the DOM-mutation logic + // in isolation. + + it("is a subclass of CustomListItem", () => { + expect(Object.getPrototypeOf(CustomListItemClass)).toBe(CustomListItem); + }); + + it("the styleFormat marker 'class' round-trips correctly on a DOM node (logic under test)", () => { + // This mirrors exactly what the constructor body does: + // domNode.dataset.styleFormat = "class"; + const node = makeListNode("ordered"); + node.dataset.styleFormat = "class"; + expect(node.dataset.styleFormat).toBe("class"); + }); + + it("inline-mode list nodes do NOT have a styleFormat marker by default", () => { + const node = makeListNode("ordered"); + expect(node.dataset.styleFormat).toBeUndefined(); + }); +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/__tests__/fonts.spec.ts b/packages/pluggableWidgets/rich-text-web/src/__tests__/fonts.spec.ts new file mode 100644 index 0000000000..780fc6dd16 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/__tests__/fonts.spec.ts @@ -0,0 +1,148 @@ +import { FONT_LIST, FontClassAttributor, FontStyleAttributor, formatCustomFonts } from "../utils/formats/fonts"; + +// parchment ClassAttributor and StyleAttributor operate directly on HTMLElement nodes — +// no Quill instance is needed for unit-level attribute tests. + +function makeSpan(): HTMLElement { + return document.createElement("span"); +} + +// FontStyleAttributor -------------------------------------------------------- + +describe("FontStyleAttributor", () => { + let attr: FontStyleAttributor; + + beforeEach(() => { + attr = new FontStyleAttributor([]); + }); + + it("adds font-family style for a known font value", () => { + const node = makeSpan(); + const result = attr.add(node, "arial"); + expect(result).toBe(true); + expect(node.style.fontFamily).toMatch(/arial/i); + expect(node.dataset.value).toBe("arial"); + }); + + it("returns false for an unknown font value", () => { + const node = makeSpan(); + const result = attr.add(node, "not-a-real-font"); + expect(result).toBe(false); + expect(node.style.fontFamily).toBe(""); + }); + + it("reads back the value via dataset.value", () => { + const node = makeSpan(); + attr.add(node, "courier-new"); + expect(attr.value(node)).toBe("courier-new"); + }); + + it("returns empty string for a node with no dataset.value", () => { + const node = makeSpan(); + expect(attr.value(node)).toBe(""); + }); + + it("applies custom fonts passed to the constructor", () => { + const custom = new FontStyleAttributor([ + { value: "my-font", description: "My Font", style: "MyFont, sans-serif" } + ]); + const node = makeSpan(); + expect(custom.add(node, "my-font")).toBe(true); + expect(node.style.fontFamily).toMatch(/MyFont/i); + }); + + it("FONT_LIST contains all 13 fonts including serif", () => { + const values = FONT_LIST.map(f => f.value); + expect(values).toContain("serif"); + expect(values).toHaveLength(13); + }); +}); + +// FontClassAttributor -------------------------------------------------------- + +describe("FontClassAttributor", () => { + let attr: FontClassAttributor; + + beforeEach(() => { + attr = new FontClassAttributor([]); + }); + + it("adds font-family- class for a known font value", () => { + const node = makeSpan(); + const result = attr.add(node, "arial"); + expect(result).toBe(true); + expect(node.classList.contains("font-family-arial")).toBe(true); + expect(node.dataset.value).toBe("arial"); + }); + + it("returns false for an unknown font value and adds no class", () => { + const node = makeSpan(); + const result = attr.add(node, "not-a-real-font"); + expect(result).toBe(false); + const hasClass = Array.from(node.classList).some(c => c.startsWith("font-family-")); + expect(hasClass).toBe(false); + }); + + it("reads back the value via dataset.value", () => { + const node = makeSpan(); + attr.add(node, "impact"); + expect(attr.value(node)).toBe("impact"); + }); + + it("returns empty string for a node with no dataset.value", () => { + const node = makeSpan(); + expect(attr.value(node)).toBe(""); + }); + + it("adds font-family-serif class for the serif font (Critical #3 regression guard)", () => { + const node = makeSpan(); + const result = attr.add(node, "serif"); + expect(result).toBe(true); + expect(node.classList.contains("font-family-serif")).toBe(true); + }); + + it("applies custom fonts passed to the constructor", () => { + const custom = new FontClassAttributor([ + { value: "my-font", description: "My Font", style: "MyFont, sans-serif" } + ]); + const node = makeSpan(); + expect(custom.add(node, "my-font")).toBe(true); + expect(node.classList.contains("font-family-my-font")).toBe(true); + }); + + it("emits class-based name, not inline style", () => { + const node = makeSpan(); + attr.add(node, "helvetica"); + expect(node.style.fontFamily).toBe(""); + expect(node.classList.contains("font-family-helvetica")).toBe(true); + }); +}); + +// formatCustomFonts ---------------------------------------------------------- + +describe("formatCustomFonts", () => { + it("maps custom font objects to FONT_LIST shape", () => { + const result = formatCustomFonts([{ fontName: "My Brand Font", fontStyle: "MyBrandFont, sans-serif" }]); + expect(result).toEqual([ + { value: "my-brand-font", description: "My Brand Font", style: "MyBrandFont, sans-serif" } + ]); + }); + + it("lowercases and hyphenates multi-word font names", () => { + const result = formatCustomFonts([{ fontName: "Open Sans", fontStyle: "Open Sans, sans-serif" }]); + expect(result[0].value).toBe("open-sans"); + }); + + it("returns an empty array when called with no arguments", () => { + expect(formatCustomFonts()).toEqual([]); + }); + + it("returns an empty array for an empty input", () => { + expect(formatCustomFonts([])).toEqual([]); + }); + + it("handles undefined fontName gracefully", () => { + const result = formatCustomFonts([{ fontName: undefined as any, fontStyle: "serif" }]); + expect(result[0].value).toBe(""); + }); +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/__tests__/helpers.spec.ts b/packages/pluggableWidgets/rich-text-web/src/__tests__/helpers.spec.ts new file mode 100644 index 0000000000..85f9649a37 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/__tests__/helpers.spec.ts @@ -0,0 +1,143 @@ +import { INDENT_MAGIC_NUMBER, normalizeStyleAndClassAttribute } from "../utils/helpers"; + +function makeDoc(html: string): Document { + const doc = document.implementation.createHTMLDocument(); + doc.body.innerHTML = html; + return doc; +} + +describe("INDENT_MAGIC_NUMBER", () => { + it("equals 3", () => { + expect(INDENT_MAGIC_NUMBER).toBe(3); + }); +}); + +describe("normalizeStyleAndClassAttribute — class mode (inline → class)", () => { + it("converts padding-left:3em to ql-indent-1 and removes the style", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "class"); + const p = doc.querySelector("p")!; + expect(p.classList.contains("ql-indent-1")).toBe(true); + expect(p.style.paddingLeft).toBe(""); + }); + + it("converts padding-left:6em to ql-indent-2", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "class"); + expect(doc.querySelector("p")!.classList.contains("ql-indent-2")).toBe(true); + }); + + it("converts padding-left:9em to ql-indent-3", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "class"); + expect(doc.querySelector("p")!.classList.contains("ql-indent-3")).toBe(true); + }); + + it("rounds non-multiples of 3 using Math.round (5em → ql-indent-2)", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "class"); + expect(doc.querySelector("p")!.classList.contains("ql-indent-2")).toBe(true); + }); + + it("ignores elements with padding-left:0em (zero is falsy — no class added)", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "class"); + const p = doc.querySelector("p")!; + const hasIndentClass = Array.from(p.classList).some(c => c.startsWith("ql-indent-")); + expect(hasIndentClass).toBe(false); + }); + + it("converts RTL padding-right:3em to ql-indent-1 and removes the style", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "class"); + const p = doc.querySelector("p")!; + expect(p.classList.contains("ql-indent-1")).toBe(true); + expect(p.style.paddingRight).toBe(""); + }); + + it("converts multiple elements independently", () => { + const doc = makeDoc(` +

a

+

b

+ `); + normalizeStyleAndClassAttribute(doc, "class"); + const [a, b] = Array.from(doc.querySelectorAll("p")); + expect(a.classList.contains("ql-indent-1")).toBe(true); + expect(b.classList.contains("ql-indent-2")).toBe(true); + }); + + it("leaves elements without padding-left unchanged", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "class"); + const p = doc.querySelector("p")!; + const hasIndentClass = Array.from(p.classList).some(c => c.startsWith("ql-indent-")); + expect(hasIndentClass).toBe(false); + }); +}); + +describe("normalizeStyleAndClassAttribute — inline mode (class → inline)", () => { + it("converts ql-indent-1 to padding-left:3em and removes the class", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "inline"); + const p = doc.querySelector("p")!; + expect(p.style.paddingLeft).toBe("3em"); + expect(p.classList.contains("ql-indent-1")).toBe(false); + }); + + it("converts ql-indent-2 to padding-left:6em", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "inline"); + expect(doc.querySelector("p")!.style.paddingLeft).toBe("6em"); + }); + + it("converts ql-indent-3 to padding-left:9em", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "inline"); + expect(doc.querySelector("p")!.style.paddingLeft).toBe("9em"); + }); + + it("uses padding-right for RTL elements (ql-direction-rtl)", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "inline"); + const p = doc.querySelector("p")!; + expect(p.style.paddingRight).toBe("3em"); + expect(p.style.paddingLeft).toBe(""); + }); + + it("removes ql-indent-* class after conversion", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "inline"); + expect(doc.querySelector("p")!.classList.contains("ql-indent-2")).toBe(false); + }); + + it("skips ql-indent-0 (zero — no padding added, class still removed)", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "inline"); + const p = doc.querySelector("p")!; + expect(p.style.paddingLeft).toBe(""); + expect(p.classList.contains("ql-indent-0")).toBe(false); + }); + + it("preserves other classes on the element", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "inline"); + expect(doc.querySelector("p")!.classList.contains("some-other-class")).toBe(true); + }); + + it("converts multiple elements independently", () => { + const doc = makeDoc(` +

a

+

b

+ `); + normalizeStyleAndClassAttribute(doc, "inline"); + const [a, b] = Array.from(doc.querySelectorAll("p")); + expect(a.style.paddingLeft).toBe("3em"); + expect(b.style.paddingLeft).toBe("9em"); + }); + + it("leaves elements without ql-indent-* unchanged", () => { + const doc = makeDoc(`

text

`); + normalizeStyleAndClassAttribute(doc, "inline"); + expect(doc.querySelector("p")!.style.paddingLeft).toBe(""); + }); +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/assets/.gitkeep b/packages/pluggableWidgets/rich-text-web/src/assets/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/pluggableWidgets/rich-text-web/src/assets/Icons.tsx b/packages/pluggableWidgets/rich-text-web/src/assets/Icons.tsx deleted file mode 100644 index e6f84d8528..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/assets/Icons.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { ReactElement } from "react"; - -export function IconLowerAlpha(): ReactElement { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/Fullscreen.tsx b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/Fullscreen.tsx deleted file mode 100644 index bc464f3264..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/Fullscreen.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ReactElement, useCallback, useContext } from "react"; -import { ToolbarButton } from "./ToolbarWrapper"; -import { CustomToolbarProps } from "./customToolbars"; -import { ACTION_DISPATCHER } from "../../utils/helpers"; -import { SET_FULLSCREEN_ACTION } from "../../store/store"; -import { EditorContext } from "../../store/EditorProvider"; -import classNames from "classnames"; - -export function FullscreenButton({ quill }: CustomToolbarProps): ReactElement { - const handleClick = useCallback(() => { - quill?.emitter.emit(ACTION_DISPATCHER, { type: SET_FULLSCREEN_ACTION }); - }, [quill]); - - const { isFullscreen } = useContext(EditorContext); - - return ( - - ); -} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/ToolbarWrapper.tsx b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/ToolbarWrapper.tsx deleted file mode 100644 index a1d08a66ff..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/ToolbarWrapper.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { If } from "@mendix/widget-plugin-component-kit/If"; -import { createContext, PropsWithChildren, ReactElement, useContext } from "react"; -import { PresetEnum } from "typings/RichTextProps"; -import type { ToolbarButtonProps, ToolbarConsumerContext, ToolbarContextType } from "./customToolbars"; - -export function presetToNumberConverter(preset: PresetEnum): number { - switch (preset) { - case "basic": - return 1; - case "standard": - return 2; - case "full": - return 3; - case "custom": - return 4; - default: - return 1; - } -} - -export const ToolbarContext = createContext({ - presetValue: 0 -}); - -export function FormatsContainer({ presetValue, children }: ToolbarConsumerContext & PropsWithChildren): ReactElement { - const toolbarContextValue = useContext(ToolbarContext); - return ( - = presetValue}> - - {children} - - - ); -} - -export function ToolbarButton({ - presetValue, - children, - className, - value, - onClick, - title -}: ToolbarButtonProps): ReactElement { - const toolbarContextValue = useContext(ToolbarContext); - return ( - = presetValue}> - - - ); -} - -export function ToolbarDropdown({ presetValue, className, value, title }: ToolbarButtonProps): ReactElement { - const toolbarContextValue = useContext(ToolbarContext); - return ( - = presetValue}> - - - ); -} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/UndoRedo.tsx b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/UndoRedo.tsx deleted file mode 100644 index 6f84717003..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/UndoRedo.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { ReactElement, useCallback } from "react"; -import { ToolbarButton } from "./ToolbarWrapper"; -import type { CustomToolbarProps } from "./customToolbars"; - -export function UndoToolbar({ quill }: CustomToolbarProps): ReactElement { - const onUndo = useCallback(() => { - quill?.history.undo(); - }, [quill]); - - return ; -} - -export function RedoToolbar({ quill }: CustomToolbarProps): ReactElement { - const onRedo = useCallback(() => { - quill?.history.redo(); - }, [quill]); - - return ; -} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/constants.ts b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/constants.ts deleted file mode 100644 index dfcda2a3c4..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/constants.ts +++ /dev/null @@ -1,249 +0,0 @@ -import type Quill from "quill"; -import { FunctionComponent } from "react"; -import { IconLowerAlpha } from "../../assets/Icons"; -import { FONT_LIST } from "../../utils/formats/fonts"; -import { FONT_SIZE_LIST } from "../../utils/formats/fontsize"; -import { ToolbarButton, ToolbarDropdown } from "./ToolbarWrapper"; -import { RedoToolbar, UndoToolbar } from "./UndoRedo"; -import { FullscreenButton } from "./Fullscreen"; -type DefaultComponentProps = { - className?: string; - value?: string | any[]; - presetValue?: number; - title: string; - children?: FunctionComponent; -}; - -type CustomComponentProps = - | { - quill?: Quill | null; - } - | DefaultComponentProps; - -type toolbarMappingType = { - [k: string]: { - component: FunctionComponent; - custom?: boolean; - } & DefaultComponentProps; -}; - -export const TOOLBAR_MAPPING: toolbarMappingType = { - undo: { component: UndoToolbar, custom: true, title: "Undo" }, - redo: { component: RedoToolbar, custom: true, title: "Redo" }, - bold: { component: ToolbarButton, className: "ql-bold icons icon-Text-bold", title: "Bold" }, - italic: { component: ToolbarButton, className: "ql-italic icons icon-Text-italic", title: "Italic" }, - underline: { - component: ToolbarButton, - className: "ql-underline icons icon-Text-underline", - presetValue: 2, - title: "Underline" - }, - strike: { - component: ToolbarButton, - className: "ql-strike icons icon-Text-strikethrough", - presetValue: 3, - title: "Strike" - }, - superScript: { - component: ToolbarButton, - className: "ql-script icons icon-Text-superscript", - value: "super", - title: "Superscript" - }, - subScript: { - component: ToolbarButton, - className: "ql-script icons icon-Text-subscript", - value: "sub", - title: "Subscript" - }, - size: { component: ToolbarDropdown, className: "size ql-size", value: FONT_SIZE_LIST, title: "Font size" }, - orderedList: { - component: ToolbarButton, - className: "ql-list icons icon-List-numbers", - value: "ordered", - title: "Default list" - }, - bulletList: { - component: ToolbarButton, - className: "ql-list icons icon-List-bullets", - value: "bullet", - title: "Bullet list" - }, - lowerAlphaList: { - component: ToolbarButton, - className: "ql-list icons icon-List-lower-alpha", - value: "lower-alpha", - presetValue: 2, - title: "Lower alpha list", - children: IconLowerAlpha - }, - checkList: { - component: ToolbarButton, - className: "ql-list icons icon-List-checklist", - value: "check", - presetValue: 3, - title: "Check list" - }, - minIndent: { - component: ToolbarButton, - className: "ql-indent icons icon-Text-indent-right", - value: "-1", - title: "Decrease indent" - }, - plusIndent: { - component: ToolbarButton, - className: "ql-indent icons icon-Text-indent-left", - value: "+1", - title: "Increase indent" - }, - direction: { - component: ToolbarButton, - className: "ql-direction direction icons", - value: "rtl", - presetValue: 2, - title: "Text direction" - }, - link: { component: ToolbarButton, className: "ql-link icons icon-Hyperlink", title: "Insert/edit link" }, - image: { - component: ToolbarButton, - className: "ql-image icons icon-Image", - presetValue: 2, - title: "Insert/edit image" - }, - video: { - component: ToolbarButton, - className: "ql-video icons icon-Film", - presetValue: 3, - title: "Insert/edit video" - }, - formula: { - component: ToolbarButton, - className: "ql-formula icons icon-Insert-edit-math", - presetValue: 3, - title: "Insert/edit formula" - }, - blockquote: { component: ToolbarButton, className: "ql-blockquote icons icon-Blockquote", title: "Blockquote" }, - codeBlock: { component: ToolbarButton, className: "ql-code-block icons icon-Code-block", title: "Code block" }, - code: { component: ToolbarButton, className: "ql-code icons icon-Inline-code", title: "Code" }, - viewCode: { - component: ToolbarButton, - className: "ql-view-code icons icon-View-edit-code", - title: "View/edit Code" - }, - align: { component: ToolbarButton, className: "ql-align icons icon-Text-align-left", title: "Left align" }, - centerAlign: { - component: ToolbarDropdown, - className: "ql-align icons", - value: ["center", "justify"], - title: "Center align" - }, - rightAlign: { - component: ToolbarButton, - className: "ql-align icons icon-Text-align-right", - value: "right", - title: "Right align" - }, - font: { component: ToolbarDropdown, className: "ql-font font", value: FONT_LIST, title: "Font type" }, - color: { component: ToolbarDropdown, className: "ql-color icons", value: [], title: "Font color" }, - background: { component: ToolbarDropdown, className: "ql-background icons", value: [], title: "Font background" }, - header: { - component: ToolbarDropdown, - className: "ql-header", - value: ["1", "2", "3", "4", "5", "6", false], - title: "Font header" - }, - clean: { component: ToolbarButton, className: "ql-clean icons icon-Clear-formating", title: "Clear formatting" }, - fullscreen: { - component: FullscreenButton, - title: "Fullscreen", - custom: true - }, - tableBetter: { - component: ToolbarButton, - className: "ql-table-better icons icon-Table", - title: "Create Table", - presetValue: 2 - } -}; - -type ToolbarGroupType = { - [k: string]: string[]; -}; - -export const TOOLBAR_GROUP: ToolbarGroupType = { - history: ["undo", "redo"], - fontStyle: ["bold", "italic", "underline", "strike"], - fontScript: ["superScript", "subScript"], - list: ["orderedList", "bulletList", "lowerAlphaList", "checkList"], - indent: ["minIndent", "plusIndent", "direction"], - align: ["align", "centerAlign", "rightAlign"], - fontColor: ["font", "size", "color", "background"], - embed: ["link", "image", "video", "formula"], - header: ["header"], - code: ["blockquote", "code", "codeBlock", "viewCode"], - remove: ["clean"], - view: ["fullscreen"], - tableBetter: ["tableBetter"] -}; - -export type toolbarContentType = { - presetValue?: number; - children: Array; -}; - -export const DEFAULT_TOOLBAR: toolbarContentType[] = [ - { - presetValue: 2, - children: TOOLBAR_GROUP.history - }, - { - presetValue: 1, - children: TOOLBAR_GROUP.fontStyle - }, - { - presetValue: 3, - children: TOOLBAR_GROUP.fontScript - }, - { - presetValue: 1, - children: TOOLBAR_GROUP.list - }, - { - presetValue: 1, - children: TOOLBAR_GROUP.indent - }, - { - presetValue: 2, - children: TOOLBAR_GROUP.align - }, - { - presetValue: 2, - children: TOOLBAR_GROUP.fontColor - }, - { - presetValue: 1, - children: TOOLBAR_GROUP.embed - }, - { - presetValue: 3, - children: TOOLBAR_GROUP.header - }, - { - presetValue: 2, - children: TOOLBAR_GROUP.code - }, - { - presetValue: 1, - children: TOOLBAR_GROUP.remove - }, - { - presetValue: 2, - children: TOOLBAR_GROUP.view - }, - { - presetValue: 2, - children: TOOLBAR_GROUP.tableBetter - } -]; - -export const IMG_MIME_TYPES = ["image/png", "image/jpeg"]; diff --git a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/customToolbars.d.ts b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/customToolbars.d.ts deleted file mode 100644 index ab02d0d63a..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/customToolbars.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Quill from "quill"; - -export type ToolbarContextType = { - presetValue: number; -}; - -export type ToolbarConsumerContext = { - presetValue?: number; -}; - -export type ToolbarButtonProps = { - className?: string; - onClick?: () => void; - value?: any; - title: string; -} & ToolbarConsumerContext & - PropsWithChildren; - -export type CustomToolbarProps = { - quill: Quill; -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/presets.ts b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/presets.ts deleted file mode 100644 index f4534048b8..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/presets.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { RichTextContainerProps } from "typings/RichTextProps"; -import { DEFAULT_TOOLBAR, TOOLBAR_GROUP, type toolbarContentType } from "./constants"; - -export function createPreset(config?: Partial): toolbarContentType[] { - return config?.preset !== "custom" - ? DEFAULT_TOOLBAR - : config?.toolbarConfig === "basic" - ? defineBasicGroups(config) - : defineAdvancedGroups(config!); -} - -function defineBasicGroups(widgetProps: Partial): toolbarContentType[] { - const enabledGroups: Array = Object.entries(widgetProps).map(([prop, enabled]) => { - if (Object.hasOwn(TOOLBAR_GROUP, prop) && enabled) { - return { - children: TOOLBAR_GROUP[prop] - }; - } else { - return undefined; - } - }); - return enabledGroups.filter(x => x !== undefined) as toolbarContentType[]; -} - -function defineAdvancedGroups(widgetProps: Partial): toolbarContentType[] { - const { advancedConfig: items } = widgetProps; - - const result: toolbarContentType[] = [ - { - children: [] - } - ]; - items?.forEach(item => { - if (item.ctItemType === "separator") { - result.push({ - children: [] - }); - } else { - result[result.length - 1].children.push(item.ctItemType); - } - }); - - return result; -} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/useEmbedModal.ts b/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/useEmbedModal.ts deleted file mode 100644 index 34aee064a9..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/components/CustomToolbars/useEmbedModal.ts +++ /dev/null @@ -1,252 +0,0 @@ -import Quill, { Range } from "quill"; -import { Delta } from "quill/core"; -import Emitter from "quill/core/emitter"; -import { Dispatch, MutableRefObject, SetStateAction, useState } from "react"; -import { RichTextContainerProps } from "typings/RichTextProps"; -import { IMG_MIME_TYPES } from "./constants"; -import { - imageConfigType, - type linkConfigType, - type videoConfigType, - type videoEmbedConfigType, - viewCodeConfigType -} from "../../utils/formats"; -import { type ChildDialogProps } from "../ModalDialog/Dialog"; -import { type VideoFormType } from "../ModalDialog/VideoDialog"; - -type ModalReturnType = { - showDialog: boolean; - setShowDialog: Dispatch>; - dialogConfig: ChildDialogProps; - customLinkHandler(value: any): void; - customVideoHandler(value: any): void; - customViewCodeHandler(value: any): void; - customImageUploadHandler(value: any): void; -}; - -export function useEmbedModal( - ref: MutableRefObject, - props: Pick -): ModalReturnType { - const [showDialog, setShowDialog] = useState(false); - const [dialogConfig, setDialogConfig] = useState({}); - const openDialog = (): void => { - setShowDialog(true); - }; - const closeDialog = (): void => { - setShowDialog(false); - setTimeout(() => ref.current?.focus(), 50); - }; - const customLinkHandler = (value: any): void => { - const selection = ref.current?.getSelection(); - const text = selection ? ref.current?.getText(selection.index, selection.length) : ""; - if (value) { - setDialogConfig({ - dialogType: "link", - config: { - onSubmit: (value: linkConfigType) => { - const index = selection?.index ?? 0; - const length = selection?.length ?? 0; - const textToDisplay = value.text ?? value.href; - const linkDelta = new Delta().retain(index).delete(length).insert(textToDisplay); - ref.current?.updateContents(linkDelta, Emitter.sources.SILENT); - ref.current?.setSelection(index, textToDisplay.length); - ref.current?.format("link", value); - closeDialog(); - }, - onClose: closeDialog, - defaultValue: { ...value, text }, - formOrientation: props.formOrientation - } - }); - openDialog(); - } else { - ref.current?.format("link", false); - closeDialog(); - } - }; - - const customVideoHandler = (value: any): void => { - const selection = ref.current?.getSelection(); - if (value === true || value.type === "video") { - setDialogConfig({ - dialogType: "video", - config: { - onSubmit: (submittedValue: VideoFormType) => { - if ( - Object.hasOwn(submittedValue, "src") && - (submittedValue as videoConfigType).src !== undefined - ) { - const currentValue = submittedValue as videoConfigType; - if (value.type === "video") { - const index = selection?.index ?? 0; - const length = selection?.length ?? 1; - const videoConfig = { - width: currentValue.width, - height: currentValue.height - }; - // update existing video value - const delta = new Delta().retain(index).retain(length, videoConfig); - ref.current?.updateContents(delta, Emitter.sources.USER); - } else { - // insert new video - const delta = new Delta() - .retain(selection?.index ?? 0) - .delete(selection?.length ?? 0) - .insert( - { video: currentValue }, - { width: currentValue.width, height: currentValue.height } - ); - ref.current?.updateContents(delta, Emitter.sources.USER); - } - } else { - const currentValue = submittedValue as videoEmbedConfigType; - const res = ref.current?.clipboard.convert({ - html: currentValue.embedcode - }); - if (res) { - // insert video via embed code; - const delta = new Delta() - .retain(selection?.index ?? 0) - .delete(selection?.length ?? 0) - .concat(res); - ref.current?.updateContents(delta, Emitter.sources.USER); - } - } - - closeDialog(); - }, - onClose: closeDialog, - selection: ref.current?.getSelection(), - defaultValue: { ...value }, - formOrientation: props.formOrientation - } - }); - openDialog(); - } else { - ref.current?.format("link", false); - closeDialog(); - } - }; - - const customViewCodeHandler = (value: any): void => { - if (value === true) { - setDialogConfig({ - dialogType: "view-code", - config: { - currentCode: ref.current?.getSemanticHTML(), - onSubmit: (value: viewCodeConfigType) => { - const newDelta = ref.current?.clipboard.convert({ html: value.src }); - if (newDelta) { - ref.current?.setContents(newDelta, Emitter.sources.USER); - } - closeDialog(); - }, - onClose: closeDialog, - formOrientation: props.formOrientation - } - }); - openDialog(); - } else { - ref.current?.format("link", false); - closeDialog(); - } - }; - - const customImageUploadHandler = (value: any): void => { - const selection = ref.current?.getSelection(true); - setDialogConfig({ - dialogType: "image", - config: { - onSubmit: (value: imageConfigType) => { - const defaultImageConfig = { - alt: value.alt, - width: value.width, - height: value.keepAspectRatio ? undefined : value.height - }; - - if (value.src) { - const index = selection?.index ?? 0; - const length = 1; - const imageConfig = defaultImageConfig; - // update existing image attribute - const imageUpdateDelta = new Delta().retain(index).retain(length, imageConfig); - ref.current?.updateContents(imageUpdateDelta, Emitter.sources.USER); - } else { - // upload new image - if (selection) { - if (value.files) { - uploadImage(ref, selection, value); - } else if (value.entityGuid) { - const imageConfig = { - ...defaultImageConfig, - "data-src": value.entityGuid - }; - const delta = new Delta() - .retain(selection.index) - .delete(selection.length) - .insert({ image: value.entityGuid }, imageConfig); - ref.current?.updateContents(delta, Emitter.sources.USER); - } - } - } - closeDialog(); - }, - onClose: closeDialog, - defaultValue: { ...value }, - formOrientation: props.formOrientation - } - }); - openDialog(); - }; - - return { - showDialog, - setShowDialog, - dialogConfig, - customLinkHandler, - customVideoHandler, - customViewCodeHandler, - customImageUploadHandler - }; -} - -function uploadImage(ref: MutableRefObject, range: Range, options: imageConfigType): void { - const uploads: File[] = []; - const { files } = options; - if (files) { - Array.from(files).forEach(file => { - if (file && IMG_MIME_TYPES.includes(file.type)) { - uploads.push(file); - } - }); - if (uploads.length > 0) { - if (!ref.current?.scroll.query("image")) { - return; - } - const promises = uploads.map>(file => { - return new Promise(resolve => { - const reader = new FileReader(); - reader.onload = () => { - resolve(reader.result as string); - }; - reader.readAsDataURL(file); - }); - }); - Promise.all(promises).then(images => { - const update = images.reduce((delta: Delta, image) => { - return delta.insert( - { image }, - { - alt: options.alt, - width: options.width, - height: options.height - } - ); - }, new Delta().retain(range.index).delete(range.length)) as Delta; - ref.current?.updateContents(update, Emitter.sources.USER); - ref.current?.setSelection(range.index + images.length, Emitter.sources.SILENT); - }); - } - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/DynamicTableStyles.tsx b/packages/pluggableWidgets/rich-text-web/src/components/DynamicTableStyles.tsx new file mode 100644 index 0000000000..98a4830d2e --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/DynamicTableStyles.tsx @@ -0,0 +1,77 @@ +import { useEffect, useRef } from "react"; +import { Editor } from "@tiptap/react"; + +export interface DynamicTableStylesProps { + editor: Editor | null; +} + +export function DynamicTableStyles({ editor }: DynamicTableStylesProps): null { + const styleElementRef = useRef(null); + + useEffect(() => { + if (!editor) return; + + // Create style element if it doesn't exist + if (!styleElementRef.current) { + styleElementRef.current = document.createElement("style"); + styleElementRef.current.setAttribute("data-tiptap-table-styles", ""); + document.head.appendChild(styleElementRef.current); + } + + const updateStyles = (): void => { + if (!styleElementRef.current) return; + + // Find all elements with data-background-color attribute + const elements = document.querySelectorAll("[data-background-color]"); + const colorsInUse = new Set(); + + elements.forEach(element => { + const color = element.getAttribute("data-background-color"); + if (color) { + colorsInUse.add(color); + // Add color-specific class to element + const sanitizedColor = color.replace(/[^a-zA-Z0-9]/g, ""); + const colorClass = `bg-color-${sanitizedColor}`; + + if (!element.classList.contains(colorClass)) { + element.classList.add(colorClass); + } + } + }); + + // Generate CSS rules only for colors that are actually in use + const cssRules: string[] = []; + colorsInUse.forEach(color => { + const sanitizedColor = color.replace(/[^a-zA-Z0-9]/g, ""); + cssRules.push(`.bg-color-${sanitizedColor} { background-color: ${color} !important; }`); + }); + + // Update style element + styleElementRef.current.textContent = cssRules.join("\n"); + }; + + // Update styles on editor update + editor.on("update", updateStyles); + editor.on("selectionUpdate", updateStyles); + + // Initial update + updateStyles(); + + // Cleanup + return () => { + editor.off("update", updateStyles); + editor.off("selectionUpdate", updateStyles); + }; + }, [editor]); + + // Cleanup on unmount + useEffect(() => { + return () => { + if (styleElementRef.current && styleElementRef.current.parentNode) { + styleElementRef.current.parentNode.removeChild(styleElementRef.current); + } + }; + }, []); + + return null; +} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/DynamicTextColorStyles.tsx b/packages/pluggableWidgets/rich-text-web/src/components/DynamicTextColorStyles.tsx new file mode 100644 index 0000000000..e286ff5816 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/DynamicTextColorStyles.tsx @@ -0,0 +1,81 @@ +import { useEffect, useRef } from "react"; +import { Editor } from "@tiptap/react"; + +export interface DynamicTextColorStylesProps { + editor: Editor | null; +} + +export function DynamicTextColorStyles({ editor }: DynamicTextColorStylesProps): null { + const styleElementRef = useRef(null); + const processedColorsRef = useRef>(new Set()); + + useEffect(() => { + if (!editor) return; + + // Create style element if it doesn't exist + if (!styleElementRef.current) { + styleElementRef.current = document.createElement("style"); + styleElementRef.current.setAttribute("data-tiptap-text-color-styles", ""); + document.head.appendChild(styleElementRef.current); + } + + const updateStyles = (): void => { + if (!styleElementRef.current) return; + + // Find all elements with data-text-color attribute + const elements = document.querySelectorAll("[data-text-color]"); + const colorsInUse = new Set(); + + elements.forEach(element => { + const color = element.getAttribute("data-text-color"); + if (color) { + colorsInUse.add(color); + // Add color-specific class to element + const sanitizedColor = color.replace(/[^a-zA-Z0-9]/g, ""); + const colorClass = `text-color-${sanitizedColor}`; + + if (!element.classList.contains(colorClass)) { + element.classList.add(colorClass); + } + } + }); + + // Generate CSS rules only for colors that are actually in use + const cssRules: string[] = []; + colorsInUse.forEach(color => { + const sanitizedColor = color.replace(/[^a-zA-Z0-9]/g, ""); + cssRules.push(`.text-color-${sanitizedColor} { color: ${color} !important; }`); + }); + + // Update style element + styleElementRef.current.textContent = cssRules.join("\n"); + + // Update processed colors reference + processedColorsRef.current = colorsInUse; + }; + + // Update styles on editor update + editor.on("update", updateStyles); + editor.on("selectionUpdate", updateStyles); + + // Initial update + updateStyles(); + + // Cleanup + return () => { + editor.off("update", updateStyles); + editor.off("selectionUpdate", updateStyles); + }; + }, [editor]); + + // Cleanup on unmount + useEffect(() => { + return () => { + if (styleElementRef.current && styleElementRef.current.parentNode) { + styleElementRef.current.parentNode.removeChild(styleElementRef.current); + } + }; + }, []); + + return null; +} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx b/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx index a13fdd1730..004e804021 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx @@ -1,206 +1,275 @@ -import Quill, { EmitterSource, QuillOptions, Range } from "quill"; -import Delta from "quill-delta"; -import { - CSSProperties, - forwardRef, - Fragment, - MutableRefObject, - useContext, - useEffect, - useLayoutEffect, - useRef -} from "react"; +import type { Editor as TipTapEditor } from "@tiptap/core"; +import { Link } from "@tiptap/extension-link"; +import { Subscript } from "@tiptap/extension-subscript"; +import { Superscript } from "@tiptap/extension-superscript"; +import { TableHeader } from "@tiptap/extension-table-header"; +import { TableRow } from "@tiptap/extension-table-row"; +import { TaskItem } from "@tiptap/extension-task-item"; +import { TaskList } from "@tiptap/extension-task-list"; +import { TextStyle } from "@tiptap/extension-text-style"; +import { Underline } from "@tiptap/extension-underline"; +import { EditorContent, useEditor } from "@tiptap/react"; +import { StarterKit } from "@tiptap/starter-kit"; +import { forwardRef, ReactElement, useImperativeHandle } from "react"; +import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; +import { EditorContextProvider, useCurrentEditor } from "./EditorContext"; +import { HighlightedCodeEditor } from "./HighlightedCodeEditor"; +import { Toolbar } from "./toolbars"; import { RichTextContainerProps } from "../../typings/RichTextProps"; -import { EditorDispatchContext } from "../store/EditorProvider"; -import { SET_FULLSCREEN_ACTION } from "../store/store"; -import "../utils/customPluginRegisters"; -import "../utils/formats/quill-table-better/assets/css/quill-table-better.scss"; -import { getResizeModuleConfig } from "../utils/formats/resizeModuleConfig"; -import { ACTION_DISPATCHER } from "../utils/helpers"; -import { getKeyboardBindings } from "../utils/modules/keyboard"; -import { getIndentHandler } from "../utils/modules/toolbarHandlers"; -import MxUploader from "../utils/modules/uploader"; -import MxQuill, { MxQuillModulesOptions } from "../utils/MxQuill"; -import { useEmbedModal } from "./CustomToolbars/useEmbedModal"; -import Dialog from "./ModalDialog/Dialog"; +import { FontFamilyClass } from "../extensions/FontFamilyClass"; +import { FontSize } from "../extensions/FontSize"; +import { Fullscreen } from "../extensions/Fullscreen"; +import { GenericEmbed } from "../extensions/GenericEmbed"; +import { ImageResize } from "../extensions/ImageResize"; +import { Indent } from "../extensions/Indent"; +import { KeyboardNavigation } from "../extensions/KeyboardNavigation"; +import { TableBackgroundColor } from "../extensions/TableBackgroundColor"; +import { TableCellBackgroundColor } from "../extensions/TableCellBackgroundColor"; +import { TextAlign } from "../extensions/TextAlignClass"; +import { TextColorClass } from "../extensions/TextColorClass"; +import { TextDirection } from "../extensions/TextDirection"; +import { TextHighlightClass } from "../extensions/TextHighlightClass"; +import { YouTubeResize } from "../extensions/YouTubeResize"; +import { ConfirmDialog } from "./toolbars/components/ConfirmDialog"; +import { ToolbarGroupsConfig } from "./toolbars/ToolbarConfig"; export interface EditorProps extends Pick< RichTextContainerProps, - "imageSource" | "imageSourceContent" | "enableDefaultUpload" + | "styleDataFormat" + | "imageSourceContent" + | "preset" + | "toolbarConfig" + | "toolbarLocation" + | "advancedConfig" + | "customFonts" + | "onFocus" + | "onBlur" + | "onLoad" + | "onChangeType" + | "onChange" > { - options: MxQuillModulesOptions; defaultValue?: string; - onTextChange?: (...args: [delta: Delta, oldContent: Delta, source: EmitterSource]) => void; - onSelectionChange?: (...args: [range: Range, oldRange: Range, source: EmitterSource]) => void; - formOrientation: "horizontal" | "vertical"; - theme: string; - style?: CSSProperties; - className?: string; - toolbarId?: string | Array; + onUpdate?: (html: string) => void; readOnly?: boolean; + className?: string; + toolbarGroups?: ToolbarGroupsConfig; +} + +export interface EditorHandle { + getHTML: () => string; + getText: () => string; + focus: () => void; + blur: () => void; + getEditor: () => TipTapEditor | null; +} + +interface EditorInnerProps extends Pick< + RichTextContainerProps, + "imageSourceContent" | "preset" | "toolbarConfig" | "advancedConfig" | "customFonts" +> { + showToolbar: boolean; + readOnly: boolean; + className?: string; + toolbarGroups?: ToolbarGroupsConfig; +} + +function EditorInner({ + showToolbar, + readOnly, + className, + imageSourceContent, + preset, + toolbarConfig, + toolbarGroups, + advancedConfig, + customFonts +}: EditorInnerProps): ReactElement { + const { editor, codeViewState, codeViewDispatch } = useCurrentEditor(); + + const handleSaveCode = (): void => { + if (!editor) return; + + // Update editor content with modified HTML + editor.commands.setContent(codeViewState.htmlCode); + codeViewDispatch({ type: "SAVE_CODE_CHANGES" }); + }; + + const handleCancelCode = (): void => { + codeViewDispatch({ type: "CANCEL_CODE_CHANGES" }); + }; + + const handleHtmlChange = (value: string): void => { + codeViewDispatch({ type: "UPDATE_HTML_CODE", html: value }); + }; + + return ( + <> +
+ {showToolbar && !readOnly && ( + + )} + {codeViewState.isCodeView ? ( + + ) : ( + + )} +
+ {codeViewState.showConfirm && ( + + )} + + ); } -// Editor is an uncontrolled React component -const Editor = forwardRef((props: EditorProps, ref: MutableRefObject) => { +const Editor = forwardRef((props, ref) => { const { - theme, defaultValue, - style, - className, - toolbarId, - onTextChange, - onSelectionChange, + onUpdate, readOnly, - options: mxOptions + className, + styleDataFormat = "inline", + toolbarLocation, + ...others } = props; - const containerRef = useRef(null); - const modalRef = useRef(null); - const onTextChangeRef = useRef(onTextChange); - const onSelectionChangeRef = useRef(onSelectionChange); - const contextDispatch = useContext(EditorDispatchContext); - const { - showDialog, - setShowDialog, - dialogConfig, - customLinkHandler, - customVideoHandler, - customViewCodeHandler, - customImageUploadHandler - } = useEmbedModal(ref, props); - const customIndentHandler = getIndentHandler(ref); - - // quill instance is not changing, thus, the function reference has to stays. - useLayoutEffect(() => { - onTextChangeRef.current = onTextChange; - onSelectionChangeRef.current = onSelectionChange; - }, [onTextChange, onSelectionChange]); - - // update quills content on value change. - useEffect(() => { - // if there is an update comes from external element - if (!ref.current?.hasFocus() && defaultValue !== ref.current?.getSemanticHTML()) { - const newContent = ref.current?.clipboard.convert({ - html: defaultValue - }); - if (newContent && newContent !== ref.current?.getContents()) { - ref.current?.setContents(newContent); + const extensions = [ + StarterKit, + TextStyle, + Underline, + Superscript, + Subscript, + TaskList, + TaskItem.configure({ + nested: true + }), + Link.configure({ + openOnClick: false, + HTMLAttributes: { + class: "tiptap-link" } - } - }, [ref, defaultValue]); - - // use effect for constructing Quill instance - useEffect( - () => { - const container = containerRef.current; - if (container) { - const editorDiv = container.ownerDocument.createElement<"div">("div"); - const editorContainer = container.appendChild(editorDiv); - - const toolbar = toolbarId - ? { - container: Array.isArray(toolbarId) ? toolbarId : `#${toolbarId}`, - handlers: { - link: customLinkHandler, - video: customVideoHandler, - indent: customIndentHandler, - "view-code": customViewCodeHandler, - image: customImageUploadHandler - } - } - : // we cannot set toolbar to false, because "table-better" needs toolbar module - // hidden toolbar will be set with display: none - []; - - // Quill instance configurations. - const options: QuillOptions = { - theme, - modules: { - keyboard: { - bindings: getKeyboardBindings() - }, - table: false, - "table-better": { - language: "en_US", - menus: ["column", "row", "merge", "table", "cell", "wrap", "copy", "delete", "grid"], - toolbarTable: !readOnly - }, - toolbar, - ...getResizeModuleConfig(readOnly) - }, - readOnly - }; - - const quill = new MxQuill(editorContainer, options); - ref.current = quill; - quill.registerCustomModules(mxOptions); - - const delta = quill.clipboard.convert({ html: defaultValue ?? "" }); - quill.updateContents(delta, Quill.sources.SILENT); - - quill.on(Quill.events.TEXT_CHANGE, (...arg) => { - onTextChangeRef.current?.(...arg); - }); - quill.on(Quill.events.SELECTION_CHANGE, (...arg) => { - onSelectionChangeRef.current?.(...arg); - }); - quill.on(ACTION_DISPATCHER, (...arg: any[]) => { - if (arg[0]) { - if (arg[0].href) { - customLinkHandler(arg[0]); - } else if (arg[0].src) { - // open dialog editor for updating image or video - if (arg[0].type === "video") { - customVideoHandler(arg[0]); - } else { - customImageUploadHandler(arg[0]); - } - } else if (arg[0].type) { - if (arg[0].type === SET_FULLSCREEN_ACTION) { - if (contextDispatch) { - contextDispatch(arg[0]); - } - } else if (arg[0].type === "image") { - // open dialog editor for updating image (triggered by module uploader) - customImageUploadHandler(arg[0]); - } - } - } - }); + }), + FontFamilyClass.configure({ + types: ["textStyle"], + styleDataFormat + }), + FontSize.configure({ + types: ["textStyle"], + styleDataFormat + }), + TextAlign.configure({ + types: ["heading", "paragraph"], + alignments: ["left", "center", "right", "justify"], + styleDataFormat + }), + Indent.configure({ + types: ["paragraph", "heading", "blockquote"], + minIndent: 0, + maxIndent: 10, + indentStep: 1, + styleDataFormat + }), + KeyboardNavigation.configure({ + wrapperSelector: ".tiptap-wrapper", + toolbarSelector: ".tiptap-toolbar", + statusBarSelector: ".rich-text-status-bar", + widgetSelector: ".widget-rich-text" + }), + TextDirection.configure({ + types: ["paragraph", "heading"], + directions: ["ltr", "rtl"], + defaultDirection: "ltr" + }), + Fullscreen.configure({ + widgetSelector: ".widget-rich-text", + fullscreenClass: "fullscreen" + }), + TextColorClass.configure({ types: ["textStyle"], styleDataFormat }), + TextHighlightClass.configure({ multicolor: true, styleDataFormat }), + TableBackgroundColor.configure({ resizable: true, styleDataFormat }), + TableRow, + TableHeader, + TableCellBackgroundColor.configure({ styleDataFormat }), + ImageResize.configure({ + inline: true, + allowBase64: true, + HTMLAttributes: { + class: "tiptap-image" + } + }), + YouTubeResize.configure({ + inline: false, + width: 640, + height: 480, + HTMLAttributes: { + class: "tiptap-video" + } + }), + GenericEmbed.configure({ + inline: false, + HTMLAttributes: { + class: "tiptap-embed" } + }) + ]; - return () => { - ref.current = null; - if (container) { - container.innerHTML = ""; - } - }; + const editor = useEditor({ + extensions, + content: defaultValue || "", + editable: !readOnly, + onUpdate: ({ editor }) => { + const html = editor.getHTML(); + onUpdate?.(html); }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [ref, toolbarId] - ); - - useEffect(() => { - // if image source is set from entity, handle upload differently - if (props.imageSource && props.imageSource.status === "available") { - (ref.current?.getModule("uploader") as MxUploader)?.setEntityUpload?.(true); + onFocus: () => { + executeAction(props.onFocus); + }, + onBlur: () => { + executeAction(props.onBlur); + if (props.onChangeType === "onLeave") { + executeAction(props.onChange); + } + }, + onCreate: () => { + executeAction(props.onLoad); } - }, [props.imageSource, props.imageSource?.status, ref]); + }); + + useImperativeHandle(ref, () => ({ + getHTML: () => editor?.getHTML() || "", + getText: () => editor?.getText() || "", + focus: () => editor?.commands.focus(), + blur: () => editor?.commands.blur(), + getEditor: () => editor + })); + + if (!editor) { + return null; + } + + const shouldHideToolbar = toolbarLocation === "hide"; return ( - -
-
- setShowDialog(open)} - parentNode={modalRef.current?.ownerDocument.body} - imageSource={props.imageSource} - imageSourceContent={props.imageSourceContent} - enableDefaultUpload={props.enableDefaultUpload} - {...dialogConfig} - > -
+ + + ); }); diff --git a/packages/pluggableWidgets/rich-text-web/src/components/EditorContext.tsx b/packages/pluggableWidgets/rich-text-web/src/components/EditorContext.tsx new file mode 100644 index 0000000000..88b96b57b0 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/EditorContext.tsx @@ -0,0 +1,100 @@ +import { Editor } from "@tiptap/react"; +import { html as beautifyHtml } from "js-beautify"; +import { createContext, useContext, useReducer, ReactNode, ReactElement, Dispatch } from "react"; + +// Code view state +export interface CodeViewState { + isCodeView: boolean; + htmlCode: string; + showConfirm: boolean; +} + +// Code view actions +export type CodeViewAction = + | { type: "ENTER_CODE_VIEW"; html: string } + | { type: "EXIT_CODE_VIEW_REQUEST" } + | { type: "SAVE_CODE_CHANGES" } + | { type: "CANCEL_CODE_CHANGES" } + | { type: "UPDATE_HTML_CODE"; html: string }; + +// Code view reducer +function codeViewReducer(state: CodeViewState, action: CodeViewAction): CodeViewState { + switch (action.type) { + case "ENTER_CODE_VIEW": + // Beautify HTML for better readability + const beautifiedHtml = beautifyHtml(action.html, { + indent_size: 2, + indent_char: " ", + max_preserve_newlines: 1, + preserve_newlines: true, + wrap_line_length: 0, + end_with_newline: false + }); + return { + isCodeView: true, + htmlCode: beautifiedHtml, + showConfirm: false + }; + case "EXIT_CODE_VIEW_REQUEST": + return { + ...state, + showConfirm: true + }; + case "SAVE_CODE_CHANGES": + return { + isCodeView: false, + htmlCode: "", + showConfirm: false + }; + case "CANCEL_CODE_CHANGES": + return { + isCodeView: false, + htmlCode: "", + showConfirm: false + }; + case "UPDATE_HTML_CODE": + return { + ...state, + htmlCode: action.html + }; + default: + return state; + } +} + +// Initial state +const initialCodeViewState: CodeViewState = { + isCodeView: false, + htmlCode: "", + showConfirm: false +}; + +interface EditorContextValue { + editor: Editor | null; + codeViewState: CodeViewState; + codeViewDispatch: Dispatch; +} + +const EditorContext = createContext(undefined); + +export function EditorContextProvider({ + editor, + children +}: { + editor: Editor | null; + children: ReactNode; +}): ReactElement { + const [codeViewState, codeViewDispatch] = useReducer(codeViewReducer, initialCodeViewState); + + return ( + {children} + ); +} + +export function useCurrentEditor(): EditorContextValue { + const context = useContext(EditorContext); + if (context === undefined) { + throw new Error("useCurrentEditor must be used within EditorContextProvider"); + } + return context; +} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx b/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx index 99e7174af3..9381c6a710 100644 --- a/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx +++ b/packages/pluggableWidgets/rich-text-web/src/components/EditorWrapper.tsx @@ -1,231 +1,143 @@ -import { If } from "@mendix/widget-plugin-component-kit/If"; +import { ReactElement, useRef, useState, useEffect } from "react"; +import { RichTextContainerProps } from "typings/RichTextProps"; import { useDebounceWithStatus } from "@mendix/widget-plugin-hooks/useDebounceWithStatus"; import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; -import classNames from "classnames"; -import Quill from "quill"; -import "quill/dist/quill.core.css"; -import "quill/dist/quill.snow.css"; -import { CSSProperties, ReactElement, useCallback, useContext, useEffect, useRef, useState } from "react"; -import { RichTextContainerProps } from "typings/RichTextProps"; -import { EditorContext, EditorProvider } from "../store/EditorProvider"; -import { useActionEvents } from "../store/useActionEvents"; -import MendixTheme from "../utils/themes/mxTheme"; -import { MxQuillModulesOptions } from "../utils/MxQuill"; -import { createPreset } from "./CustomToolbars/presets"; -import Editor from "./Editor"; -import { StickySentinel } from "./StickySentinel"; -import Toolbar from "./Toolbar"; +import Editor, { EditorHandle } from "./Editor"; +import { StatusBar, StatusBarMetricType } from "./StatusBar"; export interface EditorWrapperProps extends RichTextContainerProps { - editorHeight?: string | number; - editorWidth?: string | number; - style?: CSSProperties; className?: string; - toolbarOptions?: Array; } -function EditorWrapperInner(props: EditorWrapperProps): ReactElement { +function EditorWrapper(props: EditorWrapperProps): ReactElement { const { - id, stringAttribute, - style, className, + styleDataFormat, + imageSourceContent, preset, + toolbarConfig, + history, + fontStyle, + fontScript, + list, + indent, + embed, + align, + code, + fontColor, + header, + view, + remove, + tableBetter, + advancedConfig, + enableStatusBar, + statusBarContent, + customFonts, toolbarLocation, - onChange, - onChangeType, - onBlur, + readOnlyStyle, onFocus, + onBlur, onLoad, - readOnlyStyle, - toolbarOptions, - enableStatusBar, - statusBarContent, - tabIndex, - imageSource, - imageSourceContent, - enableDefaultUpload, - formOrientation, - defaultFontFamily, - defaultFontSize + onChangeType, + onChange } = props; - - const globalState = useContext(EditorContext); - const isFirstLoad = useRef(false); - const quillRef = useRef(null); - const actionEvents = useActionEvents({ onBlur, onFocus, onChange, onChangeType, quill: quillRef?.current }); - const toolbarRef = useRef(null); - const [wordCount, setWordCount] = useState(0); - - const { isFullscreen } = globalState; + const editorRef = useRef(null); + const [editorText, setEditorText] = useState(""); const [setAttributeValueDebounce] = useDebounceWithStatus( - (string?: string) => { - if (stringAttribute.value !== string) { - stringAttribute.setValue(string); + (html?: string) => { + if (stringAttribute.value !== html) { + stringAttribute.setValue(html); + if (onChangeType === "onDataChange") { executeAction(onChange); } } }, 200, - onChange?.isExecuting ?? false + false ); - const calculateCounts = useCallback( - (quill: Quill | null): void => { - if (enableStatusBar && quill) { - if (statusBarContent === "wordCount") { - const text = quill?.getText().trim(); - setWordCount(text && text.length > 0 ? text.split(/\s+/).length : 0); - } else if (statusBarContent === "characterCount") { - const text = quill?.getText().trimEnd() || ""; - setWordCount(text.length); - } else if (statusBarContent === "characterCountHtml") { - const html = quill?.getSemanticHTML() || ""; - setWordCount(html.length); - } - } - }, - [enableStatusBar, statusBarContent] - ); - - useEffect(() => { - if (quillRef.current) { - calculateCounts(quillRef.current); + const handleUpdate = (html: string): void => { + if (stringAttribute.value !== html) { + setAttributeValueDebounce(html); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [stringAttribute.value]); + }; + // Update editor text for status bar when editor content changes useEffect(() => { - if (quillRef.current) { - (quillRef.current?.theme as MendixTheme).updateDefaultFontFamily(defaultFontFamily?.value); - (quillRef.current?.theme as MendixTheme).updateDefaultFontSize(defaultFontSize?.value); + if (editorRef.current && enableStatusBar) { + const text = editorRef.current.getText(); + setEditorText(text); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [defaultFontFamily?.value, quillRef.current, defaultFontSize?.value]); + }, [stringAttribute.value, enableStatusBar]); - useEffect(() => { - if (quillRef.current) { - if (!isFirstLoad.current) { - executeAction(onLoad); - isFirstLoad.current = true; - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [quillRef.current]); + // Determine status bar content based on metric type + const statusBarContentValue = (() => { + if (!enableStatusBar) return ""; - const onTextChange = useCallback(() => { - const semanticHTML = quillRef.current?.getSemanticHTML() || ""; - if (stringAttribute.value !== semanticHTML) { - setAttributeValueDebounce(semanticHTML); + switch (statusBarContent) { + case "wordCount": + case "characterCount": + return editorText; + case "characterCountHtml": + return stringAttribute.value || ""; + default: + return editorText; } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [quillRef.current, stringAttribute, calculateCounts]); - - const toolbarId = `widget_${id.replaceAll(".", "_")}_toolbar`; - const shouldHideToolbar = (stringAttribute.readOnly && readOnlyStyle !== "text") || toolbarLocation === "hide"; - const toolbarPreset = shouldHideToolbar ? [] : createPreset(props); + })(); return ( -
{ - // click on other parts of editor, such as the toolbar, should also set focus - if (!quillRef?.current?.hasFocus()) { - if ( - toolbarRef.current === (e.target as HTMLDivElement) || - toolbarRef.current?.contains(e.target as Node) || - e.target === quillRef?.current?.container.parentElement - ) { - quillRef?.current?.focus(); - } - } - }} - spellCheck={props.spellCheck} - tabIndex={tabIndex ?? -1} - {...actionEvents} - > - - - -
- - + {stringAttribute.status === "available" && ( + <> + - - -
- -
- - - {wordCount} - {` ${statusBarContent === "wordCount" ? "word" : "character"}`} - {wordCount === 1 ? "" : "s"} - - -
+ {enableStatusBar && ( + + )} + + )}
); } -export default function EditorWrapper(props: EditorWrapperProps): ReactElement { - return ( - - - - ); -} +export default EditorWrapper; diff --git a/packages/pluggableWidgets/rich-text-web/src/components/EmbedResize.tsx b/packages/pluggableWidgets/rich-text-web/src/components/EmbedResize.tsx new file mode 100644 index 0000000000..e9289280ea --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/EmbedResize.tsx @@ -0,0 +1,106 @@ +import { NodeViewWrapper, NodeViewProps } from "@tiptap/react"; +import { useState, useRef, ReactElement, MouseEvent as ReactMouseEvent } from "react"; + +export function EmbedResize(props: NodeViewProps): ReactElement { + const { node, updateAttributes } = props; + const [isResizing, setIsResizing] = useState(false); + const [size, setSize] = useState({ + width: node.attrs.width || 640, + height: node.attrs.height || 480 + }); + const containerRef = useRef(null); + const startPos = useRef({ x: 0, y: 0, width: 0, height: 0 }); + const currentSize = useRef({ width: size.width, height: size.height }); + + const handleMouseDown = (e: ReactMouseEvent, corner: string): void => { + e.preventDefault(); + e.stopPropagation(); + + const container = containerRef.current; + if (!container) return; + + const rect = container.getBoundingClientRect(); + startPos.current = { + x: e.clientX, + y: e.clientY, + width: rect.width, + height: rect.height + }; + + setIsResizing(true); + + const handleMouseMove = (moveEvent: MouseEvent): void => { + const deltaX = moveEvent.clientX - startPos.current.x; + + let newWidth = startPos.current.width; + let newHeight = startPos.current.height; + + if (corner === "se" || corner === "sw" || corner === "ne" || corner === "nw") { + newWidth = startPos.current.width + (corner.includes("e") ? deltaX : -deltaX); + const aspectRatio = startPos.current.height / startPos.current.width; + newHeight = newWidth * aspectRatio; + } + + if (newWidth > 50) { + const roundedWidth = Math.round(newWidth); + const roundedHeight = Math.round(newHeight); + + currentSize.current = { + width: roundedWidth, + height: roundedHeight + }; + + setSize({ + width: roundedWidth, + height: roundedHeight + }); + } + }; + + const handleMouseUp = (): void => { + setIsResizing(false); + updateAttributes({ + width: currentSize.current.width, + height: currentSize.current.height + }); + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + }; + + return ( + +
+ ' + rows={6} + autoFocus + /> +
+ + {/* Detection Status */} + {detectedPlatform && ( +
+ ✓ Valid iframe detected +
+ Source: {detectedPlatform} +
+ )} + + {validationError &&
⚠️ {validationError}
} + +
+ ⚠️ Only use embed codes from trusted platforms. Embed codes from untrusted sources may + pose security risks. +
+
+ )} + + {/* Actions */} +
+ + +
+ +
+ + ); +} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/colorPickerHelpers.ts b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/colorPickerHelpers.ts new file mode 100644 index 0000000000..d496f7247b --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/colorPickerHelpers.ts @@ -0,0 +1,70 @@ +import { Editor } from "@tiptap/react"; +import { ColorPickerCommand } from "../ToolbarConfig"; + +interface ColorPickerHelpers { + getDefaultColor: (pickerType: ColorPickerCommand) => string; + handleColorChange: (editor: Editor, pickerType: ColorPickerCommand, color: string) => void; +} + +export const colorPickerHelpers: ColorPickerHelpers = { + getDefaultColor: (pickerType: ColorPickerCommand): string => { + if (pickerType === "textHighlight") return "#ffff00"; + if (pickerType === "cellBackground" || pickerType === "tableBackground") return "#ffffff"; + if (pickerType === "tableBorderColor") return "#000000"; + return "#000000"; + }, + + handleColorChange: (editor: Editor, pickerType: ColorPickerCommand, color: string): void => { + if (pickerType === "textColor") { + (editor.chain().focus() as any).setTextColor(color).run(); + } else if (pickerType === "textHighlight") { + (editor.chain().focus() as any).setTextHighlight(color).run(); + } else if (pickerType === "cellBackground") { + editor.chain().focus().setCellAttribute("backgroundColor", color).run(); + } else if (pickerType === "tableBackground") { + const { state } = editor; + const { $from } = state.selection; + + for (let depth = $from.depth; depth > 0; depth--) { + const node = $from.node(depth); + if (node.type.name === "table") { + const pos = $from.before(depth); + editor + .chain() + .focus() + .command(({ tr }) => { + tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + backgroundColor: color + }); + return true; + }) + .run(); + break; + } + } + } else if (pickerType === "tableBorderColor") { + const { state } = editor; + const { $from } = state.selection; + + for (let depth = $from.depth; depth > 0; depth--) { + const node = $from.node(depth); + if (node.type.name === "table") { + const pos = $from.before(depth); + editor + .chain() + .focus() + .command(({ tr }) => { + tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + borderColor: color + }); + return true; + }) + .run(); + break; + } + } + } + } +}; diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/configurationHelpers.ts b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/configurationHelpers.ts new file mode 100644 index 0000000000..470e7dc77f --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/configurationHelpers.ts @@ -0,0 +1,260 @@ +import { Editor } from "@tiptap/react"; +import { ConfigurationSection } from "../ToolbarConfig"; + +// Helper to get table node attributes +function getTableAttributes(editor: Editor): Record | null { + const { state } = editor; + const { $from } = state.selection; + + for (let depth = $from.depth; depth > 0; depth--) { + const node = $from.node(depth); + if (node.type.name === "table") { + return node.attrs; + } + } + return null; +} + +// Helper to get cell attributes +function getCellAttributes(editor: Editor): Record | null { + const { state } = editor; + const { selection } = state; + + // Try to find a cell in the current selection + const cellPos = selection.$from.pos; + const resolved = state.doc.resolve(cellPos); + + for (let depth = resolved.depth; depth > 0; depth--) { + const node = resolved.node(depth); + if (node.type.name === "tableCell" || node.type.name === "tableHeader") { + return node.attrs; + } + } + return null; +} + +// Table configuration sections +export function createTableConfigurationSections(editor: Editor): ConfigurationSection[] { + return [ + { + id: "tableBackground", + label: "Background Color", + type: "colorPicker", + defaultColor: "#ffffff", + getCurrentValue: () => { + const attrs = getTableAttributes(editor); + return attrs?.backgroundColor || null; + }, + onChange: (color: string) => { + const { state } = editor; + const { $from } = state.selection; + + for (let depth = $from.depth; depth > 0; depth--) { + const node = $from.node(depth); + if (node.type.name === "table") { + const pos = $from.before(depth); + editor + .chain() + .focus() + .command(({ tr }) => { + tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + backgroundColor: color + }); + return true; + }) + .run(); + break; + } + } + } + }, + { + id: "tableBorderColor", + label: "Border Color", + type: "colorPicker", + defaultColor: "#000000", + getCurrentValue: () => { + const attrs = getTableAttributes(editor); + return attrs?.borderColor || null; + }, + onChange: (color: string) => { + const { state } = editor; + const { $from } = state.selection; + + for (let depth = $from.depth; depth > 0; depth--) { + const node = $from.node(depth); + if (node.type.name === "table") { + const pos = $from.before(depth); + editor + .chain() + .focus() + .command(({ tr }) => { + tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + borderColor: color + }); + return true; + }) + .run(); + break; + } + } + } + }, + { + id: "tableBorderStyle", + label: "Border Style", + type: "dropdown", + options: [ + { value: "none", label: "None" }, + { value: "solid", label: "Solid" }, + { value: "dashed", label: "Dashed" }, + { value: "dotted", label: "Dotted" }, + { value: "double", label: "Double" }, + { value: "groove", label: "Groove" }, + { value: "ridge", label: "Ridge" }, + { value: "inset", label: "Inset" }, + { value: "outset", label: "Outset" } + ], + getCurrentValue: () => { + const attrs = getTableAttributes(editor); + return attrs?.borderStyle || "none"; + }, + onChange: (style: string) => { + (editor.chain().focus() as any).setTableBorderStyle(style).run(); + } + }, + { + id: "tableBorderWidth", + label: "Border Width", + type: "dropdown", + options: [ + { value: "0", label: "0" }, + { value: "1px", label: "1px" }, + { value: "2px", label: "2px" }, + { value: "3px", label: "3px" }, + { value: "4px", label: "4px" }, + { value: "5px", label: "5px" } + ], + getCurrentValue: () => { + const attrs = getTableAttributes(editor); + return attrs?.borderWidth || "0"; + }, + onChange: (width: string) => { + (editor.chain().focus() as any).setTableBorderWidth(width).run(); + } + } + ]; +} + +// Cell configuration sections +export function createCellConfigurationSections(editor: Editor): ConfigurationSection[] { + return [ + { + id: "cellBackground", + label: "Background Color", + type: "colorPicker", + defaultColor: "#ffffff", + getCurrentValue: () => { + const attrs = getCellAttributes(editor); + return attrs?.backgroundColor || null; + }, + onChange: (color: string) => { + editor.chain().focus().setCellAttribute("backgroundColor", color).run(); + } + }, + { + id: "cellBorderColor", + label: "Border Color", + type: "colorPicker", + defaultColor: "#000000", + getCurrentValue: () => { + const attrs = getCellAttributes(editor); + return attrs?.borderColor || null; + }, + onChange: (color: string) => { + editor.chain().focus().setCellAttribute("borderColor", color).run(); + } + }, + { + id: "cellBorderStyle", + label: "Border Style", + type: "dropdown", + options: [ + { value: "none", label: "None" }, + { value: "solid", label: "Solid" }, + { value: "dashed", label: "Dashed" }, + { value: "dotted", label: "Dotted" }, + { value: "double", label: "Double" }, + { value: "groove", label: "Groove" }, + { value: "ridge", label: "Ridge" }, + { value: "inset", label: "Inset" }, + { value: "outset", label: "Outset" } + ], + getCurrentValue: () => { + const attrs = getCellAttributes(editor); + return attrs?.borderStyle || "none"; + }, + onChange: (style: string) => { + editor.chain().focus().setCellAttribute("borderStyle", style).run(); + } + }, + { + id: "cellBorderWidth", + label: "Border Width", + type: "dropdown", + options: [ + { value: "0", label: "0" }, + { value: "1px", label: "1px" }, + { value: "2px", label: "2px" }, + { value: "3px", label: "3px" }, + { value: "4px", label: "4px" }, + { value: "5px", label: "5px" } + ], + getCurrentValue: () => { + const attrs = getCellAttributes(editor); + return attrs?.borderWidth || "0"; + }, + onChange: (width: string) => { + editor.chain().focus().setCellAttribute("borderWidth", width).run(); + } + }, + { + id: "cellWidth", + label: "Column Width", + type: "numberInput", + min: 25, + max: 1000, + step: 1, + placeholder: "Auto", + unit: "px", + getCurrentValue: () => { + const attrs = getCellAttributes(editor); + const colwidth = attrs?.colwidth; + + if (colwidth && Array.isArray(colwidth) && colwidth.length > 0) { + return colwidth[0]; + } + + return null; + }, + onChange: (value: string) => { + if (!value || value.trim() === "") { + editor.chain().focus().setCellAttribute("colwidth", null).run(); + return; + } + + const numValue = parseInt(value, 10); + + if (isNaN(numValue)) { + return; + } + + const clampedValue = Math.max(25, Math.min(1000, numValue)); + + editor.chain().focus().setCellAttribute("colwidth", [clampedValue]).run(); + } + } + ]; +} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/fontHelpers.ts b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/fontHelpers.ts new file mode 100644 index 0000000000..08e70b1eaa --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/fontHelpers.ts @@ -0,0 +1,76 @@ +import { CustomFontsType } from "../../../../typings/RichTextProps"; +import { ToolbarButtonConfig } from "../ToolbarConfig"; + +export const FONT_LIST = [ + { value: "Default", description: "Default", style: "" }, + { value: "andale-mono", description: "Andale Mono", style: "'andale mono', monospace" }, + { value: "arial", description: "Arial", style: "arial, helvetica, sans-serif" }, + { value: "arial-black", description: "Arial Black", style: "'arial black', sans-serif" }, + { value: "book-antiqua", description: "Book Antiqua", style: "'book antiqua', palatino, serif" }, + { value: "comic-sans", description: "Comic Sans MS", style: "'comic sans ms', sans-serif" }, + { value: "courier-new", description: "Courier New", style: "'courier new', courier, monospace" }, + { value: "helvetica", description: "Helvetica", style: "helvetica, arial, sans-serif" }, + { value: "impact", description: "Impact", style: "impact, sans-serif" }, + { value: "serif", description: "Serif", style: "serif" }, + { value: "symbol", description: "Symbol", style: "symbol" }, + { value: "terminal", description: "Terminal", style: "terminal, monaco, monospace" }, + { value: "times-new-roman", description: "Times New Roman", style: "'times new roman', times, serif" }, + { value: "trebuchet", description: "Trebuchet MS", style: "'trebuchet ms', geneva, sans-serif" } +]; + +export const FONT_SIZE_LIST = [ + "8px", + "9px", + "10px", + "12px", + "14px", + "16px", + "20px", + "24px", + "32px", + "42px", + "54px", + "68px", + "84px", + "98px" +]; + +export function AddCustomFontsToFontFamilyDropdown( + fontFamilyButton: ToolbarButtonConfig, + customFonts: CustomFontsType[] +): ToolbarButtonConfig { + if (!customFonts || customFonts.length === 0) { + return fontFamilyButton; + } + + // Map valid custom fonts to dropdown options + const customFontOptions = customFonts.map(font => { + // Transform font name to kebab-case value + // "Roboto" → "roboto" + // "Times New Roman" → "times-new-roman" + const value = font.fontName.toLowerCase().replace(/\s+/g, "-"); + + return { + value, + label: font.fontName, + attrs: { + fontFamily: font.fontStyle, + fontValue: value + }, + command: "setFontFamily" + }; + }); + + const allOptions = [...(fontFamilyButton.dropdownOptions || []), ...customFontOptions]; + + // Sort alphabetically, keeping "Default" first + const defaultOpt = allOptions.find(opt => opt.value === "Default"); + const sortedOthers = allOptions + .filter(opt => opt.value !== "Default") + .sort((a, b) => a.label.localeCompare(b.label)); + + return { + ...fontFamilyButton, + dropdownOptions: defaultOpt ? [defaultOpt, ...sortedOthers] : sortedOthers + }; +} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/toolbarTypes.ts b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/toolbarTypes.ts new file mode 100644 index 0000000000..fff6d8355d --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/helpers/toolbarTypes.ts @@ -0,0 +1,121 @@ +import { Editor } from "@tiptap/react"; +import { ReactNode } from "react"; +import { ToolbarButtonConfig } from "../ToolbarConfig"; + +// ============================================================================ +// Toolbar Button Component Props +// ============================================================================ + +/** + * Props for toolbar button components that render a single button + */ +export interface BaseToolbarButtonProps { + config: ToolbarButtonConfig; +} + +/** + * Props for toolbar button components that support image source content + * (used for dialog buttons that need to display Mendix entity selectors) + */ +export interface DialogToolbarButtonProps extends BaseToolbarButtonProps { + imageSourceContent?: ReactNode; +} + +// ============================================================================ +// Dialog Component Props +// ============================================================================ + +/** + * Base props for all dialog components + */ +export interface BaseDialogProps { + onClose: () => void; + referenceElement: HTMLElement | null; +} + +/** + * Props for ColorPicker component + */ +export interface ColorPickerProps extends BaseDialogProps { + defaultColor?: string; + onColorChange: (color: string) => void; +} + +/** + * Props for ImageDialog component + */ +export interface ImageDialogProps { + onClose: () => void; + referenceElement: HTMLElement | null; + imageSourceContent?: ReactNode; +} + +/** + * Props for LinkDialog component + */ +export interface LinkDialogProps { + onClose: () => void; + referenceElement: HTMLElement | null; +} + +/** + * Props for VideoDialog component + */ +export interface VideoDialogProps { + onClose: () => void; + referenceElement: HTMLElement | null; +} + +/** + * Props for ConfirmDialog component + */ +export interface ConfirmDialogProps { + title: string; + message: string; + confirmLabel?: string; + cancelLabel?: string; + onConfirm: () => void; + onCancel: () => void; +} + +/** + * Props for TableGridSelector component + */ +export interface TableGridSelectorProps { + editor: Editor; + onClose: () => void; + referenceElement: HTMLElement | null; +} + +// ============================================================================ +// Image-related Types +// ============================================================================ + +/** + * Entity image data from Mendix database + */ +export interface EntityImage { + id: string; + url: string; + thumbnailUrl?: string; +} + +/** + * Image source modes for ImageDialog + */ +export type ImageSourceMode = "url" | "upload" | "entity"; + +// ============================================================================ +// Constants +// ============================================================================ + +/** + * Maximum file size for image uploads (5MB) + */ +export const MAX_FILE_SIZE = 5 * 1024 * 1024; + +/** + * Maximum table dimensions for TableGridSelector + */ +export const MAX_TABLE_ROWS = 10; +export const MAX_TABLE_COLS = 10; diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/hooks/useDropdown.ts b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/hooks/useDropdown.ts new file mode 100644 index 0000000000..7cccda2249 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/hooks/useDropdown.ts @@ -0,0 +1,96 @@ +import { useEffect, useRef, RefObject } from "react"; +import { useFloating, offset, flip, shift, autoUpdate, Placement } from "@floating-ui/react"; + +export interface UseDropdownOptions { + isOpen: boolean; + onClose: () => void; + placement?: Placement; + offsetValue?: number; + referenceElement?: HTMLElement | null; +} + +export interface UseDropdownReturn { + refs: { + reference: RefObject; + floating: RefObject; + setReference: (node: HTMLElement | null) => void; + setFloating: (node: HTMLElement | null) => void; + }; + floatingStyles: { + position: "fixed" | "absolute"; + top: number; + left: number; + }; +} + +/** + * Common hook for dropdown/popover positioning and click-outside handling + * Uses Floating UI for positioning and handles click-outside to close + */ +export function useDropdown({ + isOpen, + onClose, + placement = "bottom-start", + offsetValue = 4, + referenceElement +}: UseDropdownOptions): UseDropdownReturn { + const ignoreClickRef = useRef(null); + + const { x, y, strategy, refs } = useFloating({ + placement, + strategy: "fixed", + middleware: [offset(offsetValue), flip(), shift({ padding: 8 })], + whileElementsMounted: autoUpdate, + open: isOpen + }); + + // Set reference element if provided externally + useEffect(() => { + if (referenceElement && refs.reference.current !== referenceElement) { + refs.setReference(referenceElement); + ignoreClickRef.current = referenceElement; + } + }, [referenceElement, refs]); + + // Handle click outside to close + useEffect(() => { + if (!isOpen) return; + + const handleClickOutside = (event: MouseEvent): void => { + const target = event.target as Node; + const floatingEl = refs.floating.current; + const referenceEl = refs.reference.current || ignoreClickRef.current; + + // Close if click is outside both the dropdown and the reference element + if ( + floatingEl && + floatingEl instanceof HTMLElement && + !floatingEl.contains(target) && + referenceEl && + referenceEl instanceof HTMLElement && + !referenceEl.contains(target) + ) { + onClose(); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isOpen, onClose, refs.floating, refs.reference]); + + return { + refs: { + reference: refs.reference as RefObject, + floating: refs.floating as RefObject, + setReference: refs.setReference, + setFloating: refs.setFloating + }, + floatingStyles: { + position: strategy, + top: y ?? 0, + left: x ?? 0 + } + }; +} diff --git a/packages/pluggableWidgets/rich-text-web/src/components/toolbars/index.ts b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/index.ts new file mode 100644 index 0000000000..39d901e45b --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/components/toolbars/index.ts @@ -0,0 +1,19 @@ +export { default as Toolbar } from "./Toolbar"; +export { ColorPickerToolbarButton } from "./components/ColorPicker"; +export { DialogToolbarButton } from "./components/Dialog"; +export { TableGridToolbarButton } from "./components/TableGrid"; +export { ToolbarButton } from "./components/ToolbarButton"; +export { ColorPicker } from "./components/ColorPicker"; +export { TableGridSelector } from "./components/TableGridSelector"; +export { ToolbarDropdown } from "./components/ToolbarDropdown"; +export { ImageDialog } from "./components/ImageDialog"; +export { VideoDialog } from "./components/VideoDialog"; +export { TOOLBAR_GROUPS } from "./ToolbarConfig"; +export type { + ToolbarButtonConfig, + ToolbarGroupConfig, + ToolbarActionType, + ColorPickerCommand, + DialogCommand +} from "./ToolbarConfig"; +export * from "./helpers/colorPickerHelpers"; diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/FontFamilyClass.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/FontFamilyClass.ts new file mode 100644 index 0000000000..18ed50abb5 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/FontFamilyClass.ts @@ -0,0 +1,164 @@ +import { Extension, getStyleProperty } from "@tiptap/core"; +import { FontFamily } from "@tiptap/extension-font-family"; + +export type FontFamilyClassOptions = { + types: string[]; + styleDataFormat: "inline" | "class"; +}; + +declare module "@tiptap/core" { + interface Commands { + fontFamilyClass: { + /** + * Set the font family + * @param options - String (legacy) or object with fontFamily and optional fontValue + */ + setFontFamily: (options: string | { fontFamily: string; fontValue?: string }) => ReturnType; + /** + * Unset the font family + */ + unsetFontFamily: () => ReturnType; + }; + } +} + +/** + * Derive fontValue from fontFamily CSS value + * + * This function extracts a simple identifier from a CSS font-family value for use + * in dropdown matching. The derivation ensures consistent kebab-case formatting. + * + * Algorithm: + * 1. Extract the first font from comma-separated list + * 2. Remove quotes (single or double) + * 3. Convert to lowercase + * 4. Replace spaces with hyphens (kebab-case) + * + * Examples: + * - "arial, helvetica, sans-serif" → "arial" + * - "'Roboto', sans-serif" → "roboto" + * - "'Times New Roman', serif" → "times-new-roman" + * + * Backward Compatibility: + * This derivation is used as a fallback for legacy content that lacks the + * data-font-value attribute. New content stores fontValue explicitly. + * + * @param fontFamily - CSS font-family value + * @returns Font identifier suitable for dropdown value matching + */ +function deriveFontValue(fontFamily: string): string { + if (!fontFamily) return ""; + + // Extract first font from CSS value + const firstFont = fontFamily.split(",")[0].trim(); + + // Remove quotes and normalize to kebab-case + return firstFont.replace(/['"]/g, "").toLowerCase().replace(/\s+/g, "-"); +} + +export const FontFamilyClass = Extension.create({ + name: "fontFamilyClass", + + addOptions() { + return { + types: ["textStyle"], + styleDataFormat: "inline" + }; + }, + + addExtensions() { + const styleDataFormat = this.options.styleDataFormat; + + return [ + FontFamily.extend({ + addGlobalAttributes() { + return [ + { + types: this.options.types, + attributes: { + fontFamily: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return ( + element.getAttribute("data-font-family") || + element.style.fontFamily || + null + ); + } else { + const value = + getStyleProperty(element, "font-family") ?? element.style.fontFamily; + return value?.replace(/['"]+/g, "") || null; + } + }, + renderHTML: attributes => { + if (!attributes.fontFamily) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-font-family": attributes.fontFamily, + class: "has-font-family" + }; + } else { + return { + style: `font-family: ${attributes.fontFamily}` + }; + } + } + }, + fontValue: { + default: null, + parseHTML: element => { + // Read from data-font-value attribute (new format) + return element.getAttribute("data-font-value") || null; + }, + renderHTML: attributes => { + if (!attributes.fontValue) { + return {}; + } + // Render as data-font-value for identification + return { + "data-font-value": attributes.fontValue + }; + } + } + } + } + ]; + }, + + addCommands() { + return { + setFontFamily: + (options: string | { fontFamily: string; fontValue?: string }) => + ({ commands }) => { + let fontFamily: string; + let fontValue: string; + + if (typeof options === "string") { + // Backward compatibility: string parameter (legacy code) + // Auto-derive fontValue from fontFamily CSS value + fontFamily = options; + fontValue = deriveFontValue(options); + } else { + // New format: object parameter (toolbar dropdown uses this) + // fontValue can be explicit or auto-derived if missing + fontFamily = options.fontFamily; + fontValue = options.fontValue || deriveFontValue(options.fontFamily); + } + + return commands.setMark("textStyle", { fontFamily, fontValue }); + }, + unsetFontFamily: + () => + ({ commands }) => { + return commands.unsetMark("textStyle"); + } + }; + } + }) + ]; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/FontSize.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/FontSize.ts new file mode 100644 index 0000000000..a41830c809 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/FontSize.ts @@ -0,0 +1,88 @@ +import { Extension, getStyleProperty } from "@tiptap/core"; + +export interface FontSizeOptions { + types: string[]; + styleDataFormat: "inline" | "class"; +} + +declare module "@tiptap/core" { + interface Commands { + fontSize: { + /** + * Set the font size + */ + setFontSize: (size: string) => ReturnType; + /** + * Unset the font size + */ + unsetFontSize: () => ReturnType; + }; + } +} + +export const FontSize = Extension.create({ + name: "fontSize", + + addOptions() { + return { + types: ["textStyle"], + styleDataFormat: "inline" + }; + }, + + addGlobalAttributes() { + return [ + { + types: this.options.types, + attributes: { + fontSize: { + default: null, + parseHTML: element => { + if (this.options.styleDataFormat === "class") { + return element.getAttribute("data-font-size") || element.style.fontSize || null; + } else { + const value = getStyleProperty(element, "font-size") ?? element.style.fontSize; + return value?.replace(/['"]+/g, "") || null; + } + }, + renderHTML: attributes => { + if (!attributes.fontSize) { + return {}; + } + + if (this.options.styleDataFormat === "class") { + // Extract numeric value without unit suffix (e.g., "16px" -> "16") + const match = attributes.fontSize.match(/^(\d+(?:\.\d+)?)/); + const numericValue = match ? match[1] : attributes.fontSize; + + return { + "data-font-size": numericValue, + class: "has-font-size" + }; + } else { + return { + style: `font-size: ${attributes.fontSize}` + }; + } + } + } + } + } + ]; + }, + + addCommands() { + return { + setFontSize: + fontSize => + ({ chain }) => { + return chain().setMark("textStyle", { fontSize }).run(); + }, + unsetFontSize: + () => + ({ chain }) => { + return chain().setMark("textStyle", { fontSize: null }).removeEmptyTextStyle().run(); + } + }; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/Fullscreen.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/Fullscreen.ts new file mode 100644 index 0000000000..63ff73bcec --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/Fullscreen.ts @@ -0,0 +1,81 @@ +import { Extension } from "@tiptap/core"; + +export interface FullscreenOptions { + widgetSelector: string; + fullscreenClass: string; +} + +declare module "@tiptap/core" { + interface Commands { + fullscreen: { + /** + * Toggle fullscreen mode + */ + toggleFullscreen: () => ReturnType; + /** + * Enter fullscreen mode + */ + enterFullscreen: () => ReturnType; + /** + * Exit fullscreen mode + */ + exitFullscreen: () => ReturnType; + }; + } +} + +export const Fullscreen = Extension.create({ + name: "fullscreen", + + addOptions() { + return { + widgetSelector: ".widget-rich-text", + fullscreenClass: "fullscreen" + }; + }, + + addCommands() { + return { + toggleFullscreen: () => () => { + const widgetEl = document.querySelector(this.options.widgetSelector); + if (widgetEl) { + widgetEl.classList.toggle(this.options.fullscreenClass); + return true; + } + return false; + }, + + enterFullscreen: () => () => { + const widgetEl = document.querySelector(this.options.widgetSelector); + if (widgetEl && !widgetEl.classList.contains(this.options.fullscreenClass)) { + widgetEl.classList.add(this.options.fullscreenClass); + return true; + } + return false; + }, + + exitFullscreen: () => () => { + const widgetEl = document.querySelector(this.options.widgetSelector); + if (widgetEl && widgetEl.classList.contains(this.options.fullscreenClass)) { + widgetEl.classList.remove(this.options.fullscreenClass); + return true; + } + return false; + } + }; + }, + + addKeyboardShortcuts() { + return { + Escape: () => { + // Only handle ESC if fullscreen is active + const widgetEl = document.querySelector(this.options.widgetSelector); + if (widgetEl?.classList.contains(this.options.fullscreenClass)) { + return this.editor.commands.exitFullscreen(); + } + // Pass through to other handlers if not in fullscreen + return false; + } + }; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/GenericEmbed.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/GenericEmbed.ts new file mode 100644 index 0000000000..9387d3117b --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/GenericEmbed.ts @@ -0,0 +1,136 @@ +import { Node, mergeAttributes } from "@tiptap/core"; +import { ReactNodeViewRenderer } from "@tiptap/react"; +import { EmbedResize } from "../components/EmbedResize"; + +export interface GenericEmbedOptions { + inline: boolean; + HTMLAttributes: Record; +} + +export interface GenericEmbedAttributes { + src: string; + width?: number; + height?: number; + title?: string | null; + frameborder?: string; + allow?: string | null; + allowfullscreen?: boolean; +} + +declare module "@tiptap/core" { + interface Commands { + genericEmbed: { + setGenericEmbed: (options: GenericEmbedAttributes) => ReturnType; + }; + } +} + +export const GenericEmbed = Node.create({ + name: "genericEmbed", + + group: "block", + + atom: true, + + addOptions() { + return { + inline: false, + HTMLAttributes: {} + }; + }, + + addAttributes() { + return { + src: { + default: null, + parseHTML: element => element.querySelector("iframe")?.getAttribute("src"), + renderHTML: attributes => { + return { src: attributes.src }; + } + }, + width: { + default: 640, + parseHTML: element => { + const iframe = element.querySelector("iframe"); + const widthAttr = iframe?.getAttribute("width"); + return widthAttr ? parseInt(widthAttr, 10) : 640; + } + }, + height: { + default: 480, + parseHTML: element => { + const iframe = element.querySelector("iframe"); + const heightAttr = iframe?.getAttribute("height"); + return heightAttr ? parseInt(heightAttr, 10) : 480; + } + }, + title: { + default: null, + parseHTML: element => element.querySelector("iframe")?.getAttribute("title") + }, + frameborder: { + default: "0" + }, + allow: { + default: null + }, + allowfullscreen: { + default: true + } + }; + }, + + parseHTML() { + return [ + { + tag: "div[data-generic-embed]" + } + ]; + }, + + renderHTML({ HTMLAttributes }) { + // Security: Always add sandbox and other security attributes + const secureAttributes = { + src: HTMLAttributes.src, + width: HTMLAttributes.width, + height: HTMLAttributes.height, + title: HTMLAttributes.title, + frameborder: HTMLAttributes.frameborder || "0", + allow: HTMLAttributes.allow, + allowfullscreen: HTMLAttributes.allowfullscreen ? "" : undefined, + // Security attributes + sandbox: "allow-scripts allow-same-origin allow-popups allow-forms", + loading: "lazy", + referrerpolicy: "strict-origin-when-cross-origin" + }; + + return [ + "div", + mergeAttributes( + { + "data-generic-embed": "", + class: "generic-embed-wrapper" + }, + this.options.HTMLAttributes + ), + ["iframe", secureAttributes] + ]; + }, + + addCommands() { + return { + setGenericEmbed: + (options: GenericEmbedAttributes) => + ({ commands }) => { + return commands.insertContent({ + type: this.name, + attrs: options + }); + } + }; + }, + + addNodeView() { + return ReactNodeViewRenderer(EmbedResize); + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/ImageResize.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/ImageResize.ts new file mode 100644 index 0000000000..dfb100fc5b --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/ImageResize.ts @@ -0,0 +1,81 @@ +import { Image } from "@tiptap/extension-image"; +import { ReactNodeViewRenderer } from "@tiptap/react"; +import { ImageResize as ImageResizeComponent } from "../components/ImageResize"; + +export type ImageResizeOptions = { + inline?: boolean; + allowBase64?: boolean; + HTMLAttributes?: Record; +}; + +export const ImageResize = Image.extend({ + addOptions() { + return { + ...this.parent?.(), + inline: true, + allowBase64: true, + HTMLAttributes: { + class: "tiptap-image" + } + }; + }, + + addAttributes() { + return { + ...this.parent?.(), + width: { + default: null, + parseHTML: element => element.getAttribute("width"), + renderHTML: attributes => { + if (!attributes.width) { + return {}; + } + return { width: attributes.width }; + } + }, + height: { + default: null, + parseHTML: element => element.getAttribute("height"), + renderHTML: attributes => { + if (!attributes.height) { + return {}; + } + return { height: attributes.height }; + } + }, + dataEntity: { + default: null, + parseHTML: element => { + const value = element.dataset?.entity; + return value === "true" ? true : null; + }, + renderHTML: attributes => { + if (!attributes.dataEntity) { + return {}; + } + return { + "data-entity": "true" + }; + } + }, + dataEntityId: { + default: null, + parseHTML: element => { + return element.dataset?.src || null; + }, + renderHTML: attributes => { + if (!attributes.dataEntityId) { + return {}; + } + return { + "data-src": attributes.dataEntityId + }; + } + } + }; + }, + + addNodeView() { + return ReactNodeViewRenderer(ImageResizeComponent); + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/Indent.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/Indent.ts new file mode 100644 index 0000000000..2f20954250 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/Indent.ts @@ -0,0 +1,193 @@ +import { Extension } from "@tiptap/core"; +import { AllSelection, Plugin, PluginKey, TextSelection, Transaction } from "@tiptap/pm/state"; + +export interface IndentOptions { + types: string[]; + minIndent: number; + maxIndent: number; + indentStep: number; + styleDataFormat: "inline" | "class"; +} + +declare module "@tiptap/core" { + interface Commands { + indent: { + /** + * Increase indent + */ + increaseIndent: () => ReturnType; + /** + * Decrease indent + */ + decreaseIndent: () => ReturnType; + }; + } +} + +export const Indent = Extension.create({ + name: "indent", + + addOptions() { + return { + types: ["paragraph", "heading", "blockquote"], + minIndent: 0, + maxIndent: 10, + indentStep: 1, + styleDataFormat: "inline" + }; + }, + + addGlobalAttributes() { + return [ + { + types: this.options.types, + attributes: { + indent: { + default: 0, + parseHTML: element => { + if (this.options.styleDataFormat === "class") { + const indent = element.getAttribute("data-indent"); + return indent ? parseInt(indent, 10) : 0; + } else { + // Parse from inline style margin-left + const marginLeft = element.style.marginLeft; + if (marginLeft) { + const match = marginLeft.match(/(\d+(?:\.\d+)?)/); + if (match) { + return Math.round(parseFloat(match[1]) / 2); + } + } + return 0; + } + }, + renderHTML: attributes => { + if (!attributes.indent || attributes.indent === 0) { + return {}; + } + + if (this.options.styleDataFormat === "class") { + return { + "data-indent": attributes.indent, + class: `indent-${attributes.indent}` + }; + } else { + return { + style: `margin-left: ${attributes.indent * 2}em` + }; + } + } + } + } + } + ]; + }, + + addCommands() { + return { + increaseIndent: + () => + ({ tr, state, dispatch }) => { + const { selection } = state; + tr = tr.setSelection(selection); + tr = updateIndentLevel(tr, this.options, "increase"); + + if (tr.docChanged && dispatch) { + dispatch(tr); + return true; + } + + return false; + }, + decreaseIndent: + () => + ({ tr, state, dispatch }) => { + const { selection } = state; + tr = tr.setSelection(selection); + tr = updateIndentLevel(tr, this.options, "decrease"); + + if (tr.docChanged && dispatch) { + dispatch(tr); + return true; + } + + return false; + } + }; + }, + + addProseMirrorPlugins() { + const editor = this.editor; + + return [ + new Plugin({ + key: new PluginKey("indentTabHandler"), + props: { + handleKeyDown: (view, event) => { + // Only handle Tab and Shift-Tab + if (event.key !== "Tab") { + return false; + } + + // Only capture Tab if focus is INSIDE the editor content + // This allows Tab to work naturally when focus is outside (e.g., on toolbar buttons) + const editorDom = view.dom; + const activeElement = document.activeElement; + + if (!editorDom.contains(activeElement)) { + // Focus is outside editor - let Tab work naturally + return false; + } + + // Focus is inside editor - capture Tab for indentation + // Prevent default Tab behavior (focus movement) to keep focus in editor + event.preventDefault(); + event.stopPropagation(); + + // Execute the appropriate indent command + const result = event.shiftKey + ? editor.commands.decreaseIndent() + : editor.commands.increaseIndent(); + + // Return true to indicate we handled the event + return result; + } + } + }) + ]; + } +}); + +function updateIndentLevel(tr: Transaction, options: IndentOptions, direction: "increase" | "decrease"): Transaction { + const { doc, selection } = tr; + + if (!doc || !selection) return tr; + + if (!(selection instanceof TextSelection || selection instanceof AllSelection)) { + return tr; + } + + const { from, to } = selection; + + doc.nodesBetween(from, to, (node, pos) => { + if (options.types.includes(node.type.name)) { + const currentIndent = node.attrs.indent || 0; + let newIndent = currentIndent; + + if (direction === "increase") { + newIndent = Math.min(currentIndent + options.indentStep, options.maxIndent); + } else { + newIndent = Math.max(currentIndent - options.indentStep, options.minIndent); + } + + if (newIndent !== currentIndent) { + tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + indent: newIndent + }); + } + } + return true; + }); + + return tr; +} diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/KeyboardNavigation.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/KeyboardNavigation.ts new file mode 100644 index 0000000000..bf8536ea38 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/KeyboardNavigation.ts @@ -0,0 +1,111 @@ +import { Extension } from "@tiptap/core"; +import { Plugin, PluginKey } from "@tiptap/pm/state"; + +export interface KeyboardNavigationOptions { + wrapperSelector: string; + toolbarSelector: string; + statusBarSelector: string; + widgetSelector: string; +} + +export const KeyboardNavigation = Extension.create({ + name: "keyboardNavigation", + + addOptions() { + return { + wrapperSelector: ".tiptap-wrapper", + toolbarSelector: ".tiptap-toolbar", + statusBarSelector: ".rich-text-status-bar", + widgetSelector: ".widget-rich-text" + }; + }, + + addProseMirrorPlugins() { + const options = this.options; + + return [ + new Plugin({ + key: new PluginKey("keyboardNavigation"), + props: { + handleKeyDown: (view, event) => { + // Alt+F10: Focus toolbar or wrapper + if (event.altKey && event.key === "F10") { + event.preventDefault(); + event.stopPropagation(); + + // Find the widget container relative to this editor + const editorDom = view.dom; + const widget = editorDom.closest(options.widgetSelector); + + if (!widget) { + return false; + } + + // Try to find toolbar first + const toolbar = widget.querySelector(options.toolbarSelector); + + if (toolbar) { + // Toolbar exists - focus first enabled button + const firstButton = toolbar.querySelector("button:not([disabled])"); + if (firstButton) { + firstButton.focus(); + } else { + // No enabled buttons - focus toolbar container + (toolbar as HTMLElement).setAttribute("tabindex", "0"); + (toolbar as HTMLElement).focus(); + } + } else { + // Toolbar hidden - focus wrapper instead + const wrapper = widget.querySelector(options.wrapperSelector); + if (wrapper) { + wrapper.setAttribute("tabindex", "0"); + wrapper.focus(); + } + } + + return true; + } + + // Alt+F11: Focus status bar + if (event.altKey && event.key === "F11") { + event.preventDefault(); + event.stopPropagation(); + + const editorDom = view.dom; + const widget = editorDom.closest(options.widgetSelector); + + if (!widget) { + return false; + } + + const statusBar = widget.querySelector(options.statusBarSelector); + if (statusBar) { + statusBar.focus(); + } + + return true; + } + + // Escape: Return to editor (if focus is outside) + if (event.key === "Escape") { + const editorDom = view.dom; + const activeElement = document.activeElement; + + // Only handle if focus is NOT in editor + if (activeElement && !editorDom.contains(activeElement)) { + event.preventDefault(); + + // Return focus to editor, restoring cursor position + view.focus(); + + return true; + } + } + + return false; + } + } + }) + ]; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/TableBackgroundColor.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/TableBackgroundColor.ts new file mode 100644 index 0000000000..a5fd64ca95 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/TableBackgroundColor.ts @@ -0,0 +1,507 @@ +import { mergeAttributes } from "@tiptap/core"; +import { Table } from "@tiptap/extension-table"; +import { DOMOutputSpec, Node as ProseMirrorNode } from "@tiptap/pm/model"; +import { EditorView, NodeView } from "@tiptap/pm/view"; + +// Declare custom commands for table border styling +declare module "@tiptap/core" { + interface Commands { + tableBackgroundColor: { + setTableBorderStyle: (borderStyle: string) => ReturnType; + setTableBorderWidth: (borderWidth: string) => ReturnType; + }; + } +} + +// Helper to create colgroup (copied from TipTap source) +function createColGroup(node: any, cellMinWidth: number) { + let totalWidth = 0; + let fixedWidth = true; + const cols: any[] = []; + const row = node.firstChild; + + if (!row) { + return { colgroup: ["colgroup"], tableWidth: "", tableMinWidth: "" }; + } + + for (let i = 0, col = 0; i < row.childCount; i += 1) { + const { colspan, colwidth } = row.child(i).attrs; + for (let j = 0; j < colspan; j += 1, col += 1) { + const hasWidth = colwidth && colwidth[j]; + const cssWidth = hasWidth ? colwidth[j] + "px" : ""; + totalWidth += hasWidth ? colwidth[j] : cellMinWidth; + if (!hasWidth) { + fixedWidth = false; + } + cols.push(["col", cssWidth ? { style: `min-width: ${cssWidth}` } : {}]); + } + } + + const tableWidth = fixedWidth ? totalWidth + "px" : ""; + const tableMinWidth = fixedWidth ? "" : totalWidth + "px"; + + return { + colgroup: ["colgroup", {}, ...cols], + tableWidth, + tableMinWidth + }; +} + +export type TableBackgroundColorOptions = { + HTMLAttributes?: Record; + resizable?: boolean; + cellMinWidth?: number; + renderWrapper?: boolean; + styleDataFormat: "inline" | "class"; +}; + +export const TableBackgroundColor = Table.extend({ + addOptions() { + return { + ...this.parent?.(), + HTMLAttributes: {}, + resizable: true, + cellMinWidth: 25, + renderWrapper: false, + styleDataFormat: "inline" + }; + }, + + addAttributes() { + const styleDataFormat = this.options.styleDataFormat; + + return { + ...this.parent?.(), + backgroundColor: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.getAttribute("data-background-color") || null; + } else { + return element.style.backgroundColor || null; + } + }, + renderHTML: attributes => { + if (!attributes.backgroundColor) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-background-color": attributes.backgroundColor, + class: "has-background-color" + }; + } else { + // Return empty here - we'll handle it in the main renderHTML + return {}; + } + } + }, + // Table border color attribute - stores custom border color for the entire table + // Supports both inline style and class-based rendering modes + borderColor: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.getAttribute("data-border-color") || null; + } else { + return element.style.borderColor || null; + } + }, + renderHTML: attributes => { + if (!attributes.borderColor) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-border-color": attributes.borderColor, + class: "has-table-border" + }; + } else { + // Return empty here - we'll handle it in the main renderHTML + return {}; + } + } + }, + // Table border style attribute - supports CSS border-style values (solid, dashed, dotted, etc.) + borderStyle: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.getAttribute("data-border-style") || null; + } else { + return element.style.borderStyle || null; + } + }, + renderHTML: attributes => { + if (!attributes.borderStyle) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-border-style": attributes.borderStyle + }; + } else { + // Return empty here - we'll handle it in the main renderHTML + return {}; + } + } + }, + // Table border width attribute - stores border width in pixels (e.g., "1px", "2px") + borderWidth: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.getAttribute("data-border-width") || null; + } else { + return element.style.borderWidth || null; + } + }, + renderHTML: attributes => { + if (!attributes.borderWidth) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-border-width": attributes.borderWidth + }; + } else { + // Return empty here - we'll handle it in the main renderHTML + return {}; + } + } + } + }; + }, + + renderHTML({ node, HTMLAttributes }): DOMOutputSpec { + const { colgroup, tableWidth, tableMinWidth } = createColGroup(node, this.options.cellMinWidth || 25); + + // Get style attributes from node + const backgroundColor = node.attrs.backgroundColor; + const borderColor = node.attrs.borderColor; + const borderStyle = node.attrs.borderStyle; + const borderWidth = node.attrs.borderWidth; + + // Build the style string by merging table width, background, and border properties + let styleString = ""; + + // Add table width or min-width + if (tableWidth) { + styleString = `width: ${tableWidth}`; + } else if (tableMinWidth) { + styleString = `min-width: ${tableMinWidth}`; + } + + // Add background color if present (inline mode) + if (backgroundColor && this.options.styleDataFormat === "inline") { + styleString += styleString + ? `; background-color: ${backgroundColor}` + : `background-color: ${backgroundColor}`; + } + + // Add border properties if present (inline mode) + if (this.options.styleDataFormat === "inline") { + // If any border property is set, we need to ensure border-style is set for the border to be visible + const hasBorderProperties = borderColor || borderStyle || borderWidth; + + if (hasBorderProperties) { + // Always set border-style first (default to 'solid' if not specified) + const effectiveBorderStyle = borderStyle || "solid"; + styleString += styleString + ? `; border-style: ${effectiveBorderStyle}` + : `border-style: ${effectiveBorderStyle}`; + + // Set border-width (default to '1px' if not specified) + const effectiveBorderWidth = borderWidth || "1px"; + styleString += `; border-width: ${effectiveBorderWidth}`; + + // Set border-color if specified + if (borderColor) { + styleString += `; border-color: ${borderColor}`; + } + } + } + + // Build class-based attributes for border properties + const classAttrs: Record = {}; + if (this.options.styleDataFormat === "class") { + if (backgroundColor) { + classAttrs["data-background-color"] = backgroundColor; + classAttrs.class = "has-background-color"; + } + if (borderColor || borderStyle || borderWidth) { + if (borderColor) classAttrs["data-border-color"] = borderColor; + if (borderStyle) classAttrs["data-border-style"] = borderStyle; + if (borderWidth) classAttrs["data-border-width"] = borderWidth; + classAttrs.class = classAttrs.class ? `${classAttrs.class} has-table-border` : "has-table-border"; + } + } + + // Merge all attributes + const attrs = mergeAttributes( + this.options.HTMLAttributes || {}, + HTMLAttributes, + styleString ? { style: styleString } : {}, + this.options.styleDataFormat === "class" ? classAttrs : {} + ); + + const table: DOMOutputSpec = ["table", attrs, colgroup, ["tbody", 0]]; + + return this.options.renderWrapper ? ["div", { class: "tableWrapper" }, table] : table; + }, + + addCommands() { + return { + ...this.parent?.(), + setTableBorderStyle: + (borderStyle: string) => + ({ state, dispatch }) => { + const { selection } = state; + const { $from } = selection; + + for (let depth = $from.depth; depth > 0; depth--) { + const node = $from.node(depth); + if (node.type.name === "table") { + if (dispatch) { + const pos = $from.before(depth); + const tr = state.tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + borderStyle + }); + dispatch(tr); + } + return true; + } + } + return false; + }, + setTableBorderWidth: + (borderWidth: string) => + ({ state, dispatch }) => { + const { selection } = state; + const { $from } = selection; + + for (let depth = $from.depth; depth > 0; depth--) { + const node = $from.node(depth); + if (node.type.name === "table") { + if (dispatch) { + const pos = $from.before(depth); + const tr = state.tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + borderWidth + }); + dispatch(tr); + } + return true; + } + } + return false; + } + }; + }, + + addNodeView() { + // Only add custom NodeView if resizable, otherwise use default rendering + if (!this.options.resizable || !this.editor.isEditable) { + return null; + } + + return ({ node, getPos, view }) => { + return new TableBackgroundColorNodeView( + node, + view, + getPos as () => number, + this.options.cellMinWidth || 25, + this.options.styleDataFormat || "inline" + ); + }; + } +}); + +class TableBackgroundColorNodeView implements NodeView { + node: ProseMirrorNode; + view: EditorView; + getPos: () => number; + cellMinWidth: number; + styleDataFormat: "inline" | "class"; + dom: HTMLElement; + table: HTMLTableElement; + contentDOM: HTMLElement; + + constructor( + node: ProseMirrorNode, + view: EditorView, + getPos: () => number, + cellMinWidth: number, + styleDataFormat: "inline" | "class" + ) { + this.node = node; + this.view = view; + this.getPos = getPos; + this.cellMinWidth = cellMinWidth; + this.styleDataFormat = styleDataFormat; + + // Create wrapper div + this.dom = document.createElement("div"); + this.dom.className = "tableWrapper"; + + // Create table element + this.table = document.createElement("table"); + this.dom.appendChild(this.table); + + // Create colgroup + this.updateColgroup(); + + // Create tbody as contentDOM + this.contentDOM = document.createElement("tbody"); + this.table.appendChild(this.contentDOM); + + // Apply styling (background and border) + this.updateTableStyles(); + } + + updateColgroup() { + // Remove existing colgroup if any + const existingColgroup = this.table.querySelector("colgroup"); + if (existingColgroup) { + existingColgroup.remove(); + } + + // Create new colgroup + const { colgroup } = createColGroup(this.node, this.cellMinWidth); + const colgroupElement = this.createElementFromSpec(colgroup as unknown as DOMOutputSpec); + this.table.insertBefore(colgroupElement, this.contentDOM); + } + + updateTableStyles() { + const backgroundColor = this.node.attrs.backgroundColor; + const borderColor = this.node.attrs.borderColor; + const borderStyle = this.node.attrs.borderStyle; + const borderWidth = this.node.attrs.borderWidth; + const { tableWidth, tableMinWidth } = createColGroup(this.node, this.cellMinWidth); + + // Build style string for inline mode + let styleString = ""; + if (tableWidth) { + styleString = `width: ${tableWidth}`; + } else if (tableMinWidth) { + styleString = `min-width: ${tableMinWidth}`; + } + + if (this.styleDataFormat === "inline") { + if (backgroundColor) { + styleString += styleString + ? `; background-color: ${backgroundColor}` + : `background-color: ${backgroundColor}`; + } + + // If any border property is set, ensure border-style is set for visibility + const hasBorderProperties = borderColor || borderStyle || borderWidth; + if (hasBorderProperties) { + // Always set border-style first (default to 'solid' if not specified) + const effectiveBorderStyle = borderStyle || "solid"; + styleString += styleString + ? `; border-style: ${effectiveBorderStyle}` + : `border-style: ${effectiveBorderStyle}`; + + // Set border-width (default to '1px' if not specified) + const effectiveBorderWidth = borderWidth || "1px"; + styleString += `; border-width: ${effectiveBorderWidth}`; + + // Set border-color if specified + if (borderColor) { + styleString += `; border-color: ${borderColor}`; + } + } + } + + if (styleString) { + this.table.style.cssText = styleString; + } + + // Handle class-based mode + if (this.styleDataFormat === "class") { + // Background color + if (backgroundColor) { + this.table.setAttribute("data-background-color", backgroundColor); + this.table.classList.add("has-background-color"); + } else { + this.table.removeAttribute("data-background-color"); + this.table.classList.remove("has-background-color"); + } + + // Border properties + if (borderColor || borderStyle || borderWidth) { + if (borderColor) this.table.setAttribute("data-border-color", borderColor); + if (borderStyle) this.table.setAttribute("data-border-style", borderStyle); + if (borderWidth) this.table.setAttribute("data-border-width", borderWidth); + this.table.classList.add("has-table-border"); + } else { + this.table.removeAttribute("data-border-color"); + this.table.removeAttribute("data-border-style"); + this.table.removeAttribute("data-border-width"); + this.table.classList.remove("has-table-border"); + } + } + } + + createElementFromSpec(spec: DOMOutputSpec): HTMLElement { + if (typeof spec === "string") { + return document.createTextNode(spec) as any; + } + + const specArray = Array.from(spec as readonly any[]); + const [tag, attrs, ...children] = specArray; + const element = document.createElement(tag); + + if (attrs && typeof attrs === "object" && !Array.isArray(attrs)) { + Object.entries(attrs).forEach(([key, value]) => { + if (key === "style" && typeof value === "string") { + element.style.cssText = value; + } else if (key === "class") { + element.className = value as string; + } else { + element.setAttribute(key, value as string); + } + }); + } + + if (children) { + children.forEach((child: any) => { + if (child !== 0 && child) { + // 0 is content hole + element.appendChild(this.createElementFromSpec(child)); + } + }); + } + + return element; + } + + update(node: ProseMirrorNode) { + if (node.type !== this.node.type) { + return false; + } + + this.node = node; + this.updateColgroup(); + this.updateTableStyles(); + return true; + } + + ignoreMutation(mutation: MutationRecord) { + // Ignore attribute changes on the table (we manage them) + if (mutation.type === "attributes" && mutation.target === this.table) { + return true; + } + return false; + } + + destroy() { + // Cleanup if needed + } +} diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/TableCellBackgroundColor.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/TableCellBackgroundColor.ts new file mode 100644 index 0000000000..c869867fec --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/TableCellBackgroundColor.ts @@ -0,0 +1,216 @@ +import { mergeAttributes } from "@tiptap/core"; +import { TableCell } from "@tiptap/extension-table-cell"; +import { DOMOutputSpec } from "@tiptap/pm/model"; + +// Declare custom commands for cell border styling +declare module "@tiptap/core" { + interface Commands { + tableCellBackgroundColor: { + setCellBorderColor: (borderColor: string) => ReturnType; + setCellBorderStyle: (borderStyle: string) => ReturnType; + setCellBorderWidth: (borderWidth: string) => ReturnType; + }; + } +} + +export type TableCellBackgroundColorOptions = { + styleDataFormat: "inline" | "class"; +}; + +export const TableCellBackgroundColor = TableCell.extend({ + addOptions() { + return { + ...this.parent?.(), + styleDataFormat: "inline" + }; + }, + + addAttributes() { + const styleDataFormat = this.options.styleDataFormat; + + return { + ...this.parent?.(), + backgroundColor: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.getAttribute("data-background-color") || null; + } else { + return element.style.backgroundColor || null; + } + }, + renderHTML: attributes => { + if (!attributes.backgroundColor) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-background-color": attributes.backgroundColor, + class: "has-background-color" + }; + } else { + return { + style: `background-color: ${attributes.backgroundColor}` + }; + } + } + }, + // Cell border color attribute - stores custom border color for individual cells + borderColor: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.getAttribute("data-border-color") || null; + } else { + return element.style.borderColor || null; + } + }, + renderHTML: attributes => { + if (!attributes.borderColor) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-border-color": attributes.borderColor + }; + } else { + return { + style: `border-color: ${attributes.borderColor}` + }; + } + } + }, + // Cell border style attribute - supports CSS border-style values + borderStyle: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.getAttribute("data-border-style") || null; + } else { + return element.style.borderStyle || null; + } + }, + renderHTML: attributes => { + if (!attributes.borderStyle) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-border-style": attributes.borderStyle + }; + } else { + return { + style: `border-style: ${attributes.borderStyle}` + }; + } + } + }, + // Cell border width attribute - stores border width in pixels + borderWidth: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.getAttribute("data-border-width") || null; + } else { + return element.style.borderWidth || null; + } + }, + renderHTML: attributes => { + if (!attributes.borderWidth) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-border-width": attributes.borderWidth + }; + } else { + return { + style: `border-width: ${attributes.borderWidth}` + }; + } + } + } + }; + }, + + renderHTML({ HTMLAttributes }): DOMOutputSpec { + const backgroundColor = HTMLAttributes.backgroundColor; + const borderColor = HTMLAttributes.borderColor; + const borderStyle = HTMLAttributes.borderStyle; + const borderWidth = HTMLAttributes.borderWidth; + + let styleString = ""; + const classAttrs: Record = {}; + + // Inline mode: build style string with border defaults + if (this.options.styleDataFormat === "inline") { + // Background color + if (backgroundColor) { + styleString = `background-color: ${backgroundColor}`; + } + + // Border properties with defaults + const hasBorderProperties = borderColor || borderStyle || borderWidth; + if (hasBorderProperties) { + const effectiveBorderStyle = borderStyle || "solid"; + const effectiveBorderWidth = borderWidth || "1px"; + + styleString += styleString + ? `; border-style: ${effectiveBorderStyle}` + : `border-style: ${effectiveBorderStyle}`; + styleString += `; border-width: ${effectiveBorderWidth}`; + + if (borderColor) { + styleString += `; border-color: ${borderColor}`; + } + } + } + + // Class mode: use data attributes + if (this.options.styleDataFormat === "class") { + if (backgroundColor) { + classAttrs["data-background-color"] = backgroundColor; + classAttrs.class = "has-background-color"; + } + if (borderColor || borderStyle || borderWidth) { + if (borderColor) classAttrs["data-border-color"] = borderColor; + if (borderStyle) classAttrs["data-border-style"] = borderStyle; + if (borderWidth) classAttrs["data-border-width"] = borderWidth; + classAttrs.class = classAttrs.class ? `${classAttrs.class} has-cell-border` : "has-cell-border"; + } + } + + const attrs = mergeAttributes( + HTMLAttributes, + styleString ? { style: styleString } : {}, + this.options.styleDataFormat === "class" ? classAttrs : {} + ); + + return ["td", attrs, 0]; + }, + + addCommands() { + return { + ...this.parent?.(), + setCellBorderColor: + (borderColor: string) => + ({ commands }) => { + return commands.setCellAttribute("borderColor", borderColor); + }, + setCellBorderStyle: + (borderStyle: string) => + ({ commands }) => { + return commands.setCellAttribute("borderStyle", borderStyle); + }, + setCellBorderWidth: + (borderWidth: string) => + ({ commands }) => { + return commands.setCellAttribute("borderWidth", borderWidth); + } + }; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/TextAlignClass.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/TextAlignClass.ts new file mode 100644 index 0000000000..281e9bd466 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/TextAlignClass.ts @@ -0,0 +1,185 @@ +import { Extension } from "@tiptap/core"; +import "@tiptap/extension-text-align"; + +export interface TextAlignOptions { + types: string[]; + alignments: string[]; + defaultAlignment: string; + styleDataFormat: "inline" | "class"; +} + +declare module "@tiptap/core" { + interface Commands { + textAlign: { + /** + * Set the text alignment + */ + setTextAlign: (alignment: string) => ReturnType; + /** + * Unset the text alignment + */ + unsetTextAlign: () => ReturnType; + /** + * Toggle the text alignment + */ + toggleTextAlign: (alignment: string) => ReturnType; + }; + } +} + +export const TextAlign = Extension.create({ + name: "textAlign", + + addOptions() { + return { + types: [], + alignments: ["left", "center", "right", "justify"], + defaultAlignment: "left", + styleDataFormat: "inline" + }; + }, + + addGlobalAttributes() { + return [ + { + types: this.options.types, + attributes: { + textAlign: { + default: null, + parseHTML: element => { + if (this.options.styleDataFormat === "class") { + // Check for class-based alignment first + if (element.classList.contains("text-align-left")) return "left"; + if (element.classList.contains("text-align-center")) return "center"; + if (element.classList.contains("text-align-right")) return "right"; + if (element.classList.contains("text-align-justify")) return "justify"; + // Check data attribute + return element.getAttribute("data-text-align") || this.options.defaultAlignment; + } else { + // Inline mode: parse from style attribute (backwards compatible) + return element.style.textAlign || this.options.defaultAlignment; + } + }, + renderHTML: attributes => { + if (!attributes.textAlign) { + return {}; + } + + if (this.options.styleDataFormat === "class") { + return { + class: `text-align-${attributes.textAlign}`, + "data-text-align": attributes.textAlign + }; + } else { + return { + style: `text-align: ${attributes.textAlign}` + }; + } + } + } + } + } + ]; + }, + + addCommands() { + return { + setTextAlign: + (alignment: string) => + ({ tr, state, dispatch }) => { + const { selection } = state; + const { from, to } = selection; + + if (!this.options.alignments.includes(alignment)) { + return false; + } + + let applicable = false; + + state.doc.nodesBetween(from, to, (node, pos) => { + if (this.options.types.includes(node.type.name)) { + applicable = true; + if (dispatch) { + tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + textAlign: alignment + }); + } + } + }); + + if (!applicable) { + return false; + } + + if (dispatch) { + dispatch(tr); + } + + return true; + }, + unsetTextAlign: + () => + ({ tr, state, dispatch }) => { + const { selection } = state; + const { from, to } = selection; + let applicable = false; + + state.doc.nodesBetween(from, to, (node, pos) => { + if (this.options.types.includes(node.type.name)) { + applicable = true; + if (dispatch) { + const newAttrs = { ...node.attrs }; + delete newAttrs.textAlign; + tr.setNodeMarkup(pos, undefined, newAttrs); + } + } + }); + + if (!applicable) { + return false; + } + + if (dispatch) { + dispatch(tr); + } + + return true; + }, + toggleTextAlign: + (alignment: string) => + ({ tr, state, dispatch }) => { + const { selection } = state; + const { from, to } = selection; + + if (!this.options.alignments.includes(alignment)) { + return false; + } + + let applicable = false; + + state.doc.nodesBetween(from, to, (node, pos) => { + if (this.options.types.includes(node.type.name)) { + applicable = true; + if (dispatch) { + tr.setNodeMarkup(pos, undefined, { + ...node.attrs, + textAlign: alignment + }); + } + } + }); + + if (!applicable) { + return false; + } + + if (dispatch) { + dispatch(tr); + } + + return true; + } + }; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/TextColorClass.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/TextColorClass.ts new file mode 100644 index 0000000000..85916dc55b --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/TextColorClass.ts @@ -0,0 +1,114 @@ +import { Extension, getStyleProperty } from "@tiptap/core"; +import { TextStyle } from "@tiptap/extension-text-style"; + +export type TextColorClassOptions = { + types: string[]; + styleDataFormat: "inline" | "class"; +}; + +declare module "@tiptap/core" { + interface Commands { + textColorClass: { + /** + * Set the text color using class-based approach + */ + setTextColor: (color: string) => ReturnType; + /** + * Unset the text color + */ + unsetTextColor: () => ReturnType; + }; + } +} + +export const TextColorClass = Extension.create({ + name: "textColorClass", + + addOptions() { + return { + types: ["textStyle"], + styleDataFormat: "inline" + }; + }, + + addExtensions() { + const styleDataFormat = this.options.styleDataFormat; + + return [ + TextStyle.extend({ + parseHTML() { + return [ + { + tag: "span", + getAttrs: element => { + const htmlElement = element as HTMLElement; + const hasStyles = + styleDataFormat === "class" + ? htmlElement.dataset.textColor + : htmlElement.style.color; + + if (!hasStyles) { + return false; + } + + return {}; + } + } + ]; + } + }) + ]; + }, + + addGlobalAttributes() { + return [ + { + types: this.options.types, + attributes: { + textColor: { + default: null, + parseHTML: element => { + if (this.options.styleDataFormat === "class") { + return element.getAttribute("data-text-color") || element.style.color || null; + } else { + const value = getStyleProperty(element, "color") ?? element.style.color; + return value?.replace(/['"]+/g, "") || null; + } + }, + renderHTML: attributes => { + if (!attributes.textColor) { + return {}; + } + + if (this.options.styleDataFormat === "class") { + return { + "data-text-color": attributes.textColor, + class: "has-text-color" + }; + } else { + return { + style: `color: ${attributes.textColor}` + }; + } + } + } + } + } + ]; + }, + + addCommands() { + return { + setTextColor: + (color: string) => + ({ chain }) => { + return chain().setMark("textStyle", { textColor: color }).run(); + }, + unsetTextColor: + () => + ({ chain }) => { + return chain().setMark("textStyle", { textColor: null }).unsetMark("textStyle").run(); + } + }; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/TextDirection.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/TextDirection.ts new file mode 100644 index 0000000000..cea39c86b1 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/TextDirection.ts @@ -0,0 +1,76 @@ +import { Extension } from "@tiptap/core"; + +export interface TextDirectionOptions { + types: string[]; + directions: string[]; + defaultDirection: string; +} + +declare module "@tiptap/core" { + interface Commands { + textDirection: { + /** + * Set text direction + */ + setTextDirection: (direction: string) => ReturnType; + /** + * Unset text direction + */ + unsetTextDirection: () => ReturnType; + }; + } +} + +export const TextDirection = Extension.create({ + name: "textDirection", + + addOptions() { + return { + types: ["paragraph", "heading"], + directions: ["ltr", "rtl"], + defaultDirection: "ltr" + }; + }, + + addGlobalAttributes() { + return [ + { + types: this.options.types, + attributes: { + dir: { + default: this.options.defaultDirection, + parseHTML: element => element.getAttribute("dir") || this.options.defaultDirection, + renderHTML: attributes => { + if (!attributes.dir || attributes.dir === this.options.defaultDirection) { + return {}; + } + + return { + dir: attributes.dir + }; + } + } + } + } + ]; + }, + + addCommands() { + return { + setTextDirection: + direction => + ({ commands }) => { + if (!this.options.directions.includes(direction)) { + return false; + } + + return this.options.types.every(type => commands.updateAttributes(type, { dir: direction })); + }, + unsetTextDirection: + () => + ({ commands }) => { + return this.options.types.every(type => commands.resetAttributes(type, "dir")); + } + }; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/TextHighlightClass.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/TextHighlightClass.ts new file mode 100644 index 0000000000..6f402fbf8c --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/TextHighlightClass.ts @@ -0,0 +1,109 @@ +import { Extension } from "@tiptap/core"; +import { Highlight } from "@tiptap/extension-highlight"; + +export type TextHighlightClassOptions = { + multicolor: boolean; + styleDataFormat: "inline" | "class"; +}; + +declare module "@tiptap/core" { + interface Commands { + textHighlightClass: { + /** + * Set the text highlight color using class-based approach + */ + setTextHighlight: (color: string) => ReturnType; + /** + * Unset the text highlight + */ + unsetTextHighlight: () => ReturnType; + }; + } +} + +export const TextHighlightClass = Extension.create({ + name: "textHighlightClass", + + addOptions() { + return { + multicolor: true, + styleDataFormat: "inline" + }; + }, + + addExtensions() { + const styleDataFormat = this.options.styleDataFormat; + + return [ + Highlight.extend({ + addAttributes() { + return { + color: { + default: null, + parseHTML: element => { + if (styleDataFormat === "class") { + return element.dataset.textHighlight || null; + } else { + return element.style.backgroundColor || null; + } + }, + renderHTML: attributes => { + if (!attributes.color) { + return {}; + } + + if (styleDataFormat === "class") { + return { + "data-text-highlight": attributes.color, + class: "has-text-highlight" + }; + } else { + return { + style: `background-color: ${attributes.color}` + }; + } + } + } + }; + }, + parseHTML() { + return [ + { + tag: "mark", + getAttrs: element => { + const htmlElement = element as HTMLElement; + const hasStyles = + htmlElement.hasAttribute("style") || + htmlElement.hasAttribute("data-text-highlight") || + htmlElement.style.backgroundColor; + + if (!hasStyles) { + return false; + } + + return {}; + } + } + ]; + } + }).configure({ + multicolor: this.options.multicolor + }) + ]; + }, + + addCommands() { + return { + setTextHighlight: + (color: string) => + ({ chain }) => { + return chain().setHighlight({ color }).run(); + }, + unsetTextHighlight: + () => + ({ chain }) => { + return chain().unsetHighlight().run(); + } + }; + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/extensions/YouTubeResize.ts b/packages/pluggableWidgets/rich-text-web/src/extensions/YouTubeResize.ts new file mode 100644 index 0000000000..bb9fa8ef02 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/extensions/YouTubeResize.ts @@ -0,0 +1,9 @@ +import { Youtube } from "@tiptap/extension-youtube"; +import { ReactNodeViewRenderer } from "@tiptap/react"; +import { YouTubeResize as YouTubeResizeComponent } from "../components/YouTubeResize"; + +export const YouTubeResize = Youtube.extend({ + addNodeView() { + return ReactNodeViewRenderer(YouTubeResizeComponent); + } +}); diff --git a/packages/pluggableWidgets/rich-text-web/src/package.xml b/packages/pluggableWidgets/rich-text-web/src/package.xml index 2b355b444a..adce29db0f 100644 --- a/packages/pluggableWidgets/rich-text-web/src/package.xml +++ b/packages/pluggableWidgets/rich-text-web/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/rich-text-web/src/store/EditorProvider.tsx b/packages/pluggableWidgets/rich-text-web/src/store/EditorProvider.tsx deleted file mode 100644 index 7a61e034b3..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/store/EditorProvider.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { createContext, Dispatch, ReactElement, ReactNode, useReducer } from "react"; -import { EditorAction, editorReducer, EditorState, initialState } from "./store"; - -export const EditorContext = createContext(initialState); -export const EditorDispatchContext = createContext | null>(null); - -export function EditorProvider({ children }: { children: ReactNode }): ReactElement { - const [state, dispatch] = useReducer(editorReducer, initialState); - - return ( - - {children} - - ); -} diff --git a/packages/pluggableWidgets/rich-text-web/src/store/store.ts b/packages/pluggableWidgets/rich-text-web/src/store/store.ts deleted file mode 100644 index eb9a85b748..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/store/store.ts +++ /dev/null @@ -1,25 +0,0 @@ -export type EditorAction = { type: string; value: boolean }; - -export interface EditorState { - isFullscreen: boolean; -} - -export const initialState: EditorState = { - isFullscreen: false -}; - -export const SET_FULLSCREEN_ACTION = "SET_FULLSCREEN"; - -export function editorReducer(state: EditorState, action: EditorAction): EditorState { - switch (action.type) { - case SET_FULLSCREEN_ACTION: - if (action.value === undefined || action.value === null) { - return { ...state, isFullscreen: !state.isFullscreen }; - } else { - return { ...state, isFullscreen: action.value }; - } - - default: - return state; - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/store/useActionEvents.ts b/packages/pluggableWidgets/rich-text-web/src/store/useActionEvents.ts deleted file mode 100644 index 0c2eab3408..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/store/useActionEvents.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; -import Quill from "quill"; -import { FocusEvent, useMemo, useRef } from "react"; -import { RichTextContainerProps } from "typings/RichTextProps"; - -type UseActionEventsReturnValue = { - onFocus: (e: FocusEvent) => void; - onBlur: (e: FocusEvent) => void; -}; - -interface useActionEventsProps extends Pick< - RichTextContainerProps, - "onFocus" | "onBlur" | "onChange" | "onChangeType" -> { - quill?: Quill | null; -} - -function isInternalTarget( - currentTarget: EventTarget & Element, - relatedTarget: (EventTarget & Element) | null -): boolean | undefined { - return ( - currentTarget?.contains(relatedTarget) || - currentTarget?.ownerDocument.querySelector(".widget-rich-text-modal-body")?.contains(relatedTarget) - ); -} - -export function useActionEvents(props: useActionEventsProps): UseActionEventsReturnValue { - const editorValueRef = useRef(""); - return useMemo(() => { - return { - onFocus: (e: FocusEvent): void => { - const { relatedTarget, currentTarget } = e; - if (!isInternalTarget(currentTarget, relatedTarget)) { - executeAction(props.onFocus); - editorValueRef.current = props.quill?.getText() || ""; - } - }, - onBlur: (e: FocusEvent): void => { - const { relatedTarget, currentTarget } = e; - if (!isInternalTarget(currentTarget, relatedTarget)) { - executeAction(props.onBlur); - if (props.onChangeType === "onLeave") { - if (props.quill) { - // validate if the text really changed - const currentText = props.quill.getText(); - if (currentText !== editorValueRef.current) { - executeAction(props.onChange); - editorValueRef.current = currentText; - } - } else { - executeAction(props.onChange); - } - } - } - } - }; - }, [props.onFocus, props.quill, props.onBlur, props.onChangeType, props.onChange]); -} diff --git a/packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss b/packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss index a54e9ebf74..2addc33b47 100644 --- a/packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss +++ b/packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss @@ -1,173 +1,490 @@ +@use "TableStyle"; @use "RichTextIcons"; +@use "RichTextFormatStyle"; $rte-border-color-default: #ced0d3; $rte-gray-ligher: #f8f8f8; $rte-brand-primary: #264ae5; .widget-rich-text { + // CSS Variables for consistent theming + :root { + --white: #fff; + --black: #2e2b29; + --black-contrast: #110f0e; + --gray-1: rgba(61, 37, 20, 0.05); + --gray-2: rgba(61, 37, 20, 0.08); + --gray-3: rgba(61, 37, 20, 0.12); + --gray-4: rgba(53, 38, 28, 0.3); + --gray-5: rgba(28, 25, 23, 0.6); + --green: #22c55e; + --purple: #6a00f5; + --purple-contrast: #5800cc; + --purple-light: rgba(88, 5, 255, 0.05); + --yellow-contrast: #facc15; + --yellow: rgba(250, 204, 21, 0.4); + --yellow-light: #fffae5; + --red: #ff5c33; + --red-light: #ffebe5; + --shadow: 0px 12px 33px 0px rgba(0, 0, 0, 0.06), 0px 3.618px 9.949px 0px rgba(0, 0, 0, 0.04); + } + position: relative; + font-family: Helvetica, Arial, sans-serif; &.form-control { - padding: unset; - } - .ql-container { - border: unset; + padding: 0; + border: 1px solid $rte-border-color-default; + border-radius: 4px; overflow: hidden; + flex-direction: column; + } - .ql-tooltip { - position: fixed; - width: max-content; - left: 0; - z-index: 1; - } + &.form-control-static { + padding: 8px; } - &.fullscreen { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100vw; - height: 100vh; - z-index: 104; - background-color: white; - overflow: auto; + .tiptap-wrapper { display: flex; flex-direction: column; - padding: 20px; - box-sizing: border-box; - margin: 0; - border: none; - max-width: none; - max-height: none; - transform: none; - outline: none; + width: 100%; + } - min-width: 100vw; - min-height: 100vh; + .tiptap-editor { + min-height: 200px; + width: 100%; + padding: 12px; - .flexcontainer { - width: 100% !important; - max-width: 100% !important; - } - } + .ProseMirror { + outline: none; + min-height: 200px; - .flex-column-reverse { - .widget-rich-text-toolbar.ql-toolbar { - box-shadow: 2px 0 4px 0 var(--shadow-color-border, #00000014); + &:focus { + outline: none; + } - .ql-picker-options { - bottom: 100%; - top: unset; + p { + margin: 0 0 1em; } - } - } - .widget-rich-text-toolbar.ql-toolbar { - border: unset; - box-shadow: var(--shadow-small, 0 2px 4px 0) var(--shadow-color-border, #00000014); + // Indentation support + [data-indent] { + transition: margin-left 0.2s ease; + } - .ql-picker { - &.ql-font, - &.ql-header, - &.ql-size { - margin-top: 2px; - background-color: var(--gray-lighter, $rte-gray-ligher); - border-radius: 4px; - margin-right: 4px; + // Text alignment + [style*="text-align: left"] { + text-align: left; } - } - .ql-picker-options { - max-height: 250px; - overflow-y: auto; - } + [style*="text-align: center"] { + text-align: center; + } - .ql-formats { - button:focus { - font-weight: bold; + [style*="text-align: right"] { + text-align: right; } - } - } - .hide-toolbar .ql-toolbar { - display: none; - } + [style*="text-align: justify"] { + text-align: justify; + } - &-footer { - align-items: center; - color: var(--font-color-detail); - display: flex; - flex: 0 0 auto; - font-size: var(--navigation-sub-font-size); - height: var(--spacing-large); - overflow: hidden; - padding: 0 var(--form-label-gutter, 8px); - position: relative; - text-transform: none; - justify-content: end; + // Text direction + [dir="ltr"] { + direction: ltr; + } - &:not(.hide-status-bar) { - border-top: 1px solid var(--border-color-default, $rte-border-color-default); - } + [dir="rtl"] { + direction: rtl; + } - &:focus-within { - border-top: 1px solid var(--form-input-border-focus-color, var(--brand-primary, $rte-brand-primary)); - } - } + // Font size support + span[style*="font-size"] { + line-height: 1.4; + } + + // Text formatting styles + strong { + font-weight: 700; + } + + em { + font-style: italic; + } + + s { + text-decoration: line-through; + } + + u { + text-decoration: underline; + } + + // Font family support + span[style*="font-family"] { + // Inherit font-family from inline style + } + + a.tiptap-link { + color: #0066cc; + text-decoration: underline; + cursor: pointer; + + &:hover { + color: #0052a3; + } + + &:visited { + color: #551a8b; + } + } + + mark { + padding: 0.1em 0.2em; + border-radius: 2px; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + margin-top: 0.5em; + margin-bottom: 0.5em; + font-weight: 700; + } + + ul, + ol { + padding-left: 1.5em; + margin: 0.5em 0; + } + + // Task list styles + ul[data-type="taskList"] { + list-style: none; + padding-left: 0; + + li { + display: flex; + align-items: flex-start; + gap: 0.5em; + + > label { + flex: 0 0 auto; + margin-right: 0.5rem; + user-select: none; + cursor: pointer; + + input[type="checkbox"] { + cursor: pointer; + } + } + + > div { + flex: 1 1 auto; + } + } - &.editor-readPanel { - background-color: transparent; - .widget-rich-text-container { - border: unset; + p { + margin: 0; + } + + ul[data-type="taskList"] { + margin: 0.5em 0 0.5em 1.5em; + } + } + + code { + background-color: rgba(#616161, 0.1); + color: #616161; + padding: 0.2em 0.4em; + border-radius: 3px; + font-size: 0.85em; + font-family: "Courier New", Courier, monospace; + } + + pre { + background: #0d0d0d; + color: #fff; + font-family: "JetBrainsMono", monospace; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + + code { + color: inherit; + padding: 0; + background: none; + font-size: 0.8rem; + } + } + + blockquote { + padding-left: 1rem; + border-left: 2px solid rgba(#0d0d0d, 0.1); + } + + hr { + border: none; + border-top: 2px solid rgba(#0d0d0d, 0.1); + margin: 2rem 0; + } + + // Table styles + :first-child { + margin-top: 0; + } + + // Image and video styles + .image-wrapper { + display: inline-block; + position: relative; + margin: 0.5em 0; + line-height: 0; + + &.resizing { + outline: 2px solid var(--purple, #6a00f5); + } + + .image-container { + position: relative; + display: inline-block; + max-width: 100%; + + img { + display: block; + max-width: 100%; + height: auto; + border-radius: 4px; + } + + .resize-handles { + display: none; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + + .resize-handle { + position: absolute; + width: 12px; + height: 12px; + background: var(--purple, #6a00f5); + border: 2px solid white; + border-radius: 50%; + pointer-events: auto; + cursor: pointer; + z-index: 10; + + &.nw { + top: -6px; + left: -6px; + cursor: nw-resize; + } + + &.ne { + top: -6px; + right: -6px; + cursor: ne-resize; + } + + &.sw { + bottom: -6px; + left: -6px; + cursor: sw-resize; + } + + &.se { + bottom: -6px; + right: -6px; + cursor: se-resize; + } + } + } + + &:hover .resize-handles, + .resize-handles:hover { + display: block; + } + } + } + + // Embed and YouTube video resize styles + .embed-wrapper, + .youtube-wrapper { + display: inline-block; + position: relative; + margin: 0.5em 0; + line-height: 0; + + &.resizing { + outline: 2px solid var(--purple, #6a00f5); + } + + .embed-container, + .youtube-container { + position: relative; + display: inline-block; + max-width: 100%; + + iframe { + display: block; + border-radius: 8px; + } + + .resize-handles { + display: none; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + + .resize-handle { + position: absolute; + width: 12px; + height: 12px; + background: var(--purple, #6a00f5); + border: 2px solid white; + border-radius: 50%; + pointer-events: auto; + cursor: pointer; + z-index: 10; + + &.nw { + top: -6px; + left: -6px; + cursor: nw-resize; + } + + &.ne { + top: -6px; + right: -6px; + cursor: ne-resize; + } + + &.sw { + bottom: -6px; + left: -6px; + cursor: sw-resize; + } + + &.se { + bottom: -6px; + right: -6px; + cursor: se-resize; + } + } + } + + &:hover .resize-handles, + .resize-handles:hover { + display: block; + } + } + } + + iframe.tiptap-video { + max-width: 100%; + border-radius: 8px; + margin: 1em 0; + } } - } - &.editor-text { - .widget-rich-text-toolbar { - pointer-events: none; - background-color: var(--gray-lighter, $rte-gray-ligher); + &.resize-cursor { + cursor: ew-resize; + cursor: col-resize; } } - .ql-editor li[data-list="ordered"][data-custom-list="lower-alpha"] { - counter-increment: custom-list-alpha; - & > .ql-ui:before { - content: counter(custom-list-alpha, lower-alpha) ". "; + .rich-text-status-bar { + display: flex; + align-items: center; + justify-content: flex-end; + width: 100%; + height: 24px; + padding: 0 12px; + border-top: 1px solid var(--border-color-default, #d7d7d7); + background-color: var(--background-color-secondary, #f8f8f8); + font-size: 12px; + color: var(--font-color-secondary, #606060); + + .status-bar-text { + user-select: none; } - } - .ql-editor li[data-list="ordered"][data-custom-list="lower-roman"] { - counter-increment: custom-list-roman; - & > .ql-ui:before { - content: counter(custom-list-roman, lower-roman) ". "; + &:focus { + outline: 2px solid var(--brand-primary, $rte-brand-primary); + outline-offset: 2px; } } - .widget-rich-text-prompt { - position: absolute; + &.fullscreen { + position: fixed; top: 0; - bottom: 0; left: 0; right: 0; - align-content: center; - text-align: center; + bottom: 0; + z-index: 104; + background-color: white; + overflow: auto; + display: flex; + flex-direction: column; + padding: var(--spacing-medium, 16px); + box-sizing: border-box; + margin: var(--spacing-medium, 16px) var(--closed-sidebar-width); + border: none; + max-width: none; + max-height: none; + transform: none; + outline: none; + box-shadow: var(--shadow-medium); + + .tiptap-wrapper { + width: 100%; + max-width: 100%; + flex: 1; + } + + .tiptap-editor { + flex: 1; + } } - .sticky-sentinel.container-stuck { - & + .flexcontainer.flex-column { - overflow: visible; - .widget-rich-text-toolbar { - position: sticky; - z-index: 50; - top: 0; - background-color: white; - } + VIDEO, + IFRAME { + pointer-events: none; + } + + .code-editor { + width: 100%; + min-height: 300px; + padding: 12px; + font-family: "Courier New", Courier, monospace; + font-size: 13px; + line-height: 1.5; + border: 1px solid #ddd; + border-radius: 4px; + background: #f8f8f8; + color: #333; + resize: vertical; + + &:focus { + outline: none; + border-color: #0078d4; + background: white; } } +} + +.mx-scrollcontainer-wrapper:has(.widget-rich-text.fullscreen) { + isolation: auto; - .flexcontainer.flex-column { - overflow: visible; + .mx-scrollcontainer-wrapper { + isolation: auto; } } diff --git a/packages/pluggableWidgets/rich-text-web/src/ui/RichTextFormatStyle.scss b/packages/pluggableWidgets/rich-text-web/src/ui/RichTextFormatStyle.scss new file mode 100644 index 0000000000..1f307164ca --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/ui/RichTextFormatStyle.scss @@ -0,0 +1,66 @@ +@use "variables"; + +.widget-rich-text { + @each $name, $font in variables.$fonts { + .font-family-#{$name} { + @include variables.font($font); + } + + .tiptap-toolbar { + [data-value="#{$name}"] { + @include variables.font($font); + } + } + } + + @for $i from 1 through 10 { + .indent-#{$i} { + padding-left: #{$i * 3}px; + } + } + + p { + line-height: var(--line-height-base, 1); + } + + mark { + color: inherit; + background-color: inherit; + } + + .has-text-color { + color: attr(data-text-color type()); + } + + .has-text-highlight { + background-color: attr(data-text-highlight type()); + } + + .has-background-color { + background-color: attr(data-background-color type()); + } + + .has-font-family { + font-family: attr(data-font-family raw-string); + } + + .has-font-size { + font-size: attr(data-font-size px); + } + + .text-align-left { + text-align: left; + } + + .text-align-center { + text-align: center; + } + + .text-align-right { + text-align: right; + } + + .text-align-justify { + text-align: justify; + } +} diff --git a/packages/pluggableWidgets/rich-text-web/src/ui/RichTextIcons.scss b/packages/pluggableWidgets/rich-text-web/src/ui/RichTextIcons.scss index b95289c1e4..23c1d35583 100644 --- a/packages/pluggableWidgets/rich-text-web/src/ui/RichTextIcons.scss +++ b/packages/pluggableWidgets/rich-text-web/src/ui/RichTextIcons.scss @@ -68,21 +68,6 @@ $icons: ( } } - .ql-container { - .ql-resize-overlay { - .ql-resize-toolbar { - font-family: "RichTextIconFont" !important; - font-style: normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - button { - position: relative; - } - } - } - } - .icons { font-family: "RichTextIconFont" !important; font-size: 16px; @@ -95,61 +80,5 @@ $icons: ( content: $code; } } - - &.ql-color { - .ql-picker-label { - &:before { - content: map.get($icons, Text-color); - } - svg { - display: none; - } - } - } - - &.ql-background { - .ql-picker-label { - &:before { - content: map.get($icons, Text-background); - } - svg { - display: none; - } - } - } - - &.ql-direction { - &:before { - content: map.get($icons, Left-to-right); - } - svg { - display: none; - } - - &.ql-active { - &:before { - content: map.get($icons, Right-to-left); - } - - svg { - display: none; - } - } - } - - &.ql-align.ql-picker { - .ql-picker-label, - .ql-picker-item { - &[data-value="center"]:before { - content: map.get($icons, Text-align-center); - } - &[data-value="justify"]:before { - content: map.get($icons, Text-align-justify); - } - svg { - display: none; - } - } - } } } diff --git a/packages/pluggableWidgets/rich-text-web/src/ui/TableStyle.scss b/packages/pluggableWidgets/rich-text-web/src/ui/TableStyle.scss new file mode 100644 index 0000000000..6456e08151 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/ui/TableStyle.scss @@ -0,0 +1,56 @@ +.widget-rich-text { + table { + border-collapse: collapse; + margin: 0; + overflow: hidden; + table-layout: fixed; + width: 100%; + + td, + th { + border: 1px solid var(--gray-3, rgba(61, 37, 20, 0.12)); + box-sizing: border-box; + min-width: 1em; + padding: 6px 8px; + position: relative; + vertical-align: top; + + > * { + margin-bottom: 0; + } + } + + th { + background-color: var(--gray-1, rgba(61, 37, 20, 0.05)); + font-weight: 700; + text-align: left; + } + + .selectedCell:after { + background: var(--gray-2, rgba(61, 37, 20, 0.08)); + content: ""; + left: 0; + right: 0; + top: 0; + bottom: 0; + pointer-events: none; + position: absolute; + z-index: 2; + } + + .column-resize-handle { + background-color: var(--purple, #6a00f5); + bottom: -2px; + pointer-events: none; + position: absolute; + right: -2px; + top: 0; + width: 4px; + } + } + + .tableWrapper { + margin: 1.5rem 0; + overflow-x: auto; + } +} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.scss b/packages/pluggableWidgets/rich-text-web/src/ui/variables.scss similarity index 57% rename from packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.scss rename to packages/pluggableWidgets/rich-text-web/src/ui/variables.scss index 258576b2dc..44fe1b4d61 100644 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.scss +++ b/packages/pluggableWidgets/rich-text-web/src/ui/variables.scss @@ -10,6 +10,7 @@ $font-symbol: symbol; $font-terminal: terminal, monaco, monospace; $font-times-new-roman: "times new roman", times, serif; $font-trebuchet: "trebuchet ms", geneva, sans-serif; +$font-serif: Georgia, "times New roman", serif; $fonts: ( andale-mono: $font-andale-mono, @@ -26,39 +27,51 @@ $fonts: ( trebuchet: $font-trebuchet ); +$quill-fonts: ( + serif: $font-serif +); + @mixin font($font) { font-family: $font; } -.widget-rich-text { - .ql-toolbar { - .ql-picker { - &.ql-font { - width: 160px; - } - - &.size.ql-size { - width: 60px; - .ql-picker-label, - .ql-picker-item { - &::before { - content: attr(data-value); - } - } - } - } - @each $name, $font in $fonts { - [data-value="#{$name}"]::before { - @include font($font); - } - } - } +$font-sizes: (8px, 9px, 10px, 12px, 14px, 16px, 20px, 24px, 32px, 42px, 54px, 68px, 84px, 98px); - .ql-font { - @each $name, $font in $fonts { - &-#{$name} { - @include font($font); - } - } - } -} +// https://github.com/slab/quill/blob/main/packages/quill/src/themes/base.ts +$colors: ( + #000000, + #e60000, + #ff9900, + #ffff00, + #008a00, + #0066cc, + #9933ff, + #ffffff, + #facccc, + #ffebcc, + #ffffcc, + #cce8cc, + #cce0f5, + #ebd6ff, + #bbbbbb, + #f06666, + #ffc266, + #ffff66, + #66b966, + #66a3e0, + #c285ff, + #888888, + #a10000, + #b26b00, + #b2b200, + #006100, + #0047b2, + #6b24b2, + #444444, + #5c0000, + #663d00, + #666600, + #003700, + #002966, + #3d1466 +); diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/MxQuill.ts b/packages/pluggableWidgets/rich-text-web/src/utils/MxQuill.ts deleted file mode 100644 index 1d3710bd66..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/MxQuill.ts +++ /dev/null @@ -1,236 +0,0 @@ -/** - * https://github.com/slab/quill/ -Copyright (c) 2017-2024, Slab -Copyright (c) 2014, Jason Chen -Copyright (c) 2013, salesforce.com -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - * - * this file overrides Quill instance. - * allowing us to override certain function that is not easy to extend. - */ -import { type Blot, ParentBlot } from "parchment"; -import Quill, { EmitterSource, QuillOptions } from "quill"; -import TextBlot, { escapeText } from "quill/blots/text"; -import { Delta, Op } from "quill/core"; -import Editor from "quill/core/editor"; -import { STANDARD_LIST_TYPES } from "./formats/customList"; -import { FontStyleAttributor, formatCustomFonts } from "./formats/fonts"; -import CustomLink, { CustomLinkNoValidation } from "./formats/link"; -import { CustomFontsType } from "../../typings/RichTextProps"; - -interface ListItem { - child: Blot; - offset: number; - length: number; - indent: number; - type: string; -} - -/** - * Rich Text's extended Quill Editor - * allowing us to override certain editor's function, such as: getHTML - */ -class MxEditor extends Editor { - /** - * copied without modification from Quill's editor - * https://github.com/slab/quill/blob/main/packages/quill/src/core/editor.ts - */ - getHTML(index: number, length: number): string { - if (this.isBlank()) { - return ""; - } - const [line, lineOffset] = this.scroll.line(index); - if (line) { - const lineLength = line.length(); - const isWithinLine = line.length() >= lineOffset + length; - if (isWithinLine && !(lineOffset === 0 && length === lineLength)) { - return convertHTML(line, lineOffset, length, true); - } - return convertHTML(this.scroll, index, length, true); - } - return ""; - } -} - -export interface MxQuillModulesOptions { - fonts: CustomFontsType[]; - links: { - validate: boolean; - }; -} - -/** - * Extension's of quill to allow us to replace the editor instance. - */ -export default class MxQuill extends Quill { - constructor(container: HTMLElement | string, options: QuillOptions = {}) { - super(container, options); - this.editor = new MxEditor(this.scroll); - } - - setContents(dlta: Delta | Op[], source?: EmitterSource): Delta { - super.setContents(new Delta(), Quill.sources.SILENT); - return this.updateContents(this.getContents().transform(dlta as Delta, false), source); - } - - registerCustomModules(props: MxQuillModulesOptions): void { - const { fonts, links } = props; - const customFonts = formatCustomFonts(fonts); - const FontStyle = new FontStyleAttributor(customFonts); - Quill.register(FontStyle, true); - if (links.validate) { - Quill.register(CustomLink, true); - } else { - Quill.register(CustomLinkNoValidation, true); - } - } -} - -/** - * copied without modification from Quill's editor - * https://github.com/slab/quill/blob/main/packages/quill/src/core/editor.ts - */ -function getListType(type: string | undefined): [tag: string, attr: string] { - const tag = type === "ordered" || type === "lower-alpha" || type === "lower-roman" ? "ol" : "ul"; - switch (type) { - case "checked": - return [tag, ' data-list="checked"']; - case "unchecked": - return [tag, ' data-list="unchecked"']; - default: - return [tag, ""]; - } -} - -// based on LIST_STYLE variables sequence in https://github.com/slab/quill/blob/main/packages/quill/src/assets/core.styl -const ListSequence = ["ordered", "lower-alpha", "lower-roman"]; - -// construct proper "list-style-type" style attribute -function getExpectedType(type: string | undefined, indent: number): string { - // bullet is standard list type, convert to disc for correct css display - if (type === "bullet") { - return "disc"; - } - - // custom list type is not dependant on indent level, use as is - const isCustomList = !STANDARD_LIST_TYPES.find(x => x === type); - if (isCustomList && type) { - return type; - } - - // list type that is dependant on indent level, find the expected type based on indent level - const currentIndex = ListSequence.indexOf(type || "ordered"); - const expectedIndex = (currentIndex + indent) % 3; - const expectedType = ListSequence[expectedIndex] ?? type; - return expectedType === "ordered" ? "decimal" : expectedType === "bullet" ? "disc" : expectedType; -} - -/** - * Copy with modification from https://github.com/slab/quill/blob/main/packages/quill/src/core/editor.ts - */ -function convertListHTML(items: ListItem[], lastIndent: number, types: string[]): string { - if (items.length === 0) { - const [endTag] = getListType(types.pop()); - if (lastIndent <= 0) { - return ``; - } - return `${convertListHTML([], lastIndent - 1, types)}`; - } - const [{ child, offset, length, indent, type }, ...rest] = items; - const [tag, attribute] = getListType(type); - - if (indent > lastIndent) { - // modified by web-content: get proper list-style-type - const expectedType = getExpectedType(type, indent); - types.push(type); - if (indent === lastIndent + 1) { - return `<${tag} style="list-style-type: ${expectedType}">${convertHTML( - child, - offset, - length - )}${convertListHTML(rest, indent, types)}`; - } - return `<${tag}>
  • ${convertListHTML(items, lastIndent + 1, types)}`; - } - const previousType = types[types.length - 1]; - if (indent === lastIndent && type === previousType) { - return `
  • ${convertHTML(child, offset, length)}${convertListHTML(rest, indent, types)}`; - } - const [endTag] = getListType(types.pop()); - return `${convertListHTML(items, lastIndent - 1, types)}`; -} - -/** - * copied without modification from Quill's editor - * https://github.com/slab/quill/blob/main/packages/quill/src/core/editor.ts - * allowing us to use our own convertListHTML function. - */ -function convertHTML(blot: Blot, index: number, length: number, isRoot = false): string { - if ("html" in blot && typeof blot.html === "function") { - return blot.html(index, length); - } - if (blot instanceof TextBlot) { - const escapedText = escapeText(blot.value().slice(index, index + length)); - return escapedText; - } - if (blot instanceof ParentBlot) { - // TODO fix API - if (blot.statics.blotName === "list-container") { - const items: any[] = []; - blot.children.forEachAt(index, length, (child, offset, childLength) => { - const formats = "formats" in child && typeof child.formats === "function" ? child.formats() : {}; - items.push({ - child, - offset, - length: childLength, - indent: formats.indent || 0, - type: formats.list - }); - }); - return convertListHTML(items, -1, []); - } - const parts: string[] = []; - blot.children.forEachAt(index, length, (child, offset, childLength) => { - parts.push(convertHTML(child, offset, childLength)); - }); - if (isRoot || blot.statics.blotName === "list") { - return parts.join(""); - } - const { outerHTML, innerHTML } = blot.domNode as Element; - const [start, end] = outerHTML.split(`>${innerHTML}<`); - // TODO cleanup - if (start === "${parts.join("")}<${end}`; - } - return `${start}>${parts.join("")}<${end}`; - } - return blot.domNode instanceof Element ? blot.domNode.outerHTML : ""; -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/customPluginRegisters.ts b/packages/pluggableWidgets/rich-text-web/src/utils/customPluginRegisters.ts deleted file mode 100644 index c954bb99c5..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/customPluginRegisters.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Attributor } from "parchment"; -import Quill from "quill"; -import QuillResize from "quill-resize-module"; -import MxBlock from "./formats/block"; -import Button from "./formats/button"; -import CustomListItem from "./formats/customList"; -import "./formats/fonts"; -import "./formats/fontsize"; -import Formula from "./formats/formula"; -import CustomImage from "./formats/image"; -import { IndentLeftStyle, IndentRightStyle } from "./formats/indent"; -import QuillTableBetter from "./formats/quill-table-better/quill-table-better"; -import SoftBreak from "./formats/softBreak"; -import CustomVideo from "./formats/video"; -import { WhiteSpaceStyle } from "./formats/whiteSpace"; -import MxUploader from "./modules/uploader"; -import MendixTheme from "./themes/mxTheme"; -import MxScroll from "./modules/scroll"; -const direction = Quill.import("attributors/style/direction") as Attributor; -const alignment = Quill.import("attributors/style/align") as Attributor; - -class Empty { - doSomething(): string { - return ""; - } -} -/** - * Custom format registration for quill. - */ -Quill.debug("error"); -Quill.register({ "themes/snow": MendixTheme }, true); -Quill.register(CustomListItem, true); -Quill.register(WhiteSpaceStyle, true); -Quill.register(CustomVideo, true); -Quill.register(CustomImage, true); -Quill.register({ "formats/softbreak": SoftBreak }, true); -Quill.register(direction, true); -Quill.register(alignment, true); -Quill.register(IndentLeftStyle, true); -Quill.register(IndentRightStyle, true); -Quill.register(Formula, true); -Quill.register(Button, true); -Quill.register(MxBlock, true); -Quill.register({ "modules/uploader": MxUploader }, true); -Quill.register({ "blots/scroll": MxScroll }, true); -Quill.register("modules/resize", QuillResize, true); -// add empty handler for view code, this format is handled by toolbar's custom config via ViewCodeDialog -Quill.register({ "ui/view-code": Empty }); -Quill.register({ "modules/table-better": QuillTableBetter }, true); diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/embedCodeParser.ts b/packages/pluggableWidgets/rich-text-web/src/utils/embedCodeParser.ts new file mode 100644 index 0000000000..07109a5d37 --- /dev/null +++ b/packages/pluggableWidgets/rich-text-web/src/utils/embedCodeParser.ts @@ -0,0 +1,129 @@ +export interface ParsedEmbed { + valid: boolean; + error?: string; + src?: string; + width?: string; + height?: string; + title?: string | null; + frameborder?: string; + allow?: string | null; + allowfullscreen?: boolean; + domain?: string; +} + +// Whitelist of allowed domains for embed codes +const ALLOWED_DOMAINS = [ + "youtube.com", + "www.youtube.com", + "player.vimeo.com", + "vimeo.com", + "player.dailymotion.com", + "dailymotion.com", + "codepen.io", + "jsfiddle.net", + "stackblitz.com", + "codesandbox.io", + "maps.google.com", + "www.google.com" +]; + +function isDomainAllowed(hostname: string): boolean { + return ALLOWED_DOMAINS.some(allowed => hostname === allowed || hostname.endsWith("." + allowed)); +} + +export function parseEmbedCode(html: string): ParsedEmbed { + try { + // Parse HTML safely using DOMParser + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + + // Find iframe element + const iframe = doc.querySelector("iframe"); + + if (!iframe) { + return { + valid: false, + error: "No iframe found in embed code" + }; + } + + // Extract and validate src attribute + const src = iframe.getAttribute("src"); + + if (!src) { + return { + valid: false, + error: "Iframe missing src attribute" + }; + } + + // Security: Block javascript: URLs + if (src.toLowerCase().startsWith("javascript:")) { + return { + valid: false, + error: "JavaScript URLs are not allowed" + }; + } + + // Security: Block data: URLs + if (src.toLowerCase().startsWith("data:")) { + return { + valid: false, + error: "Data URLs are not allowed" + }; + } + + // Validate URL format + try { + const url = new URL(src); + + // Security: Only allow http and https protocols + if (url.protocol !== "http:" && url.protocol !== "https:") { + return { + valid: false, + error: "Only HTTP/HTTPS URLs are allowed" + }; + } + + // Check domain whitelist + const domain = url.hostname; + + if (!isDomainAllowed(domain)) { + return { + valid: false, + error: `Domain "${domain}" is not in the allowed list. Supported: YouTube, Vimeo, Dailymotion, CodePen, etc.` + }; + } + + // Extract safe attributes + const width = iframe.getAttribute("width") || "640"; + const height = iframe.getAttribute("height") || "480"; + const title = iframe.getAttribute("title") || null; + const frameborder = iframe.getAttribute("frameborder") || "0"; + const allow = iframe.getAttribute("allow") || null; + const allowfullscreen = iframe.hasAttribute("allowfullscreen"); + + return { + valid: true, + src, + width, + height, + title, + frameborder, + allow, + allowfullscreen, + domain + }; + } catch (_urlError) { + return { + valid: false, + error: "Invalid URL in src attribute" + }; + } + } catch (_parseError) { + return { + valid: false, + error: "Failed to parse embed code" + }; + } +} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats.d.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats.d.ts index 6058445ab1..28c292bda7 100644 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats.d.ts +++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats.d.ts @@ -28,3 +28,10 @@ export type imageConfigType = { entityGuid?: string; keepAspectRatio?: boolean; }; +export interface MxQuillModulesOptions { + styleDataFormat: "inline" | "class"; + fonts: CustomFontsType[]; + links: { + validate: boolean; + }; +} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/block.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/block.ts deleted file mode 100644 index d1c5e55d78..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/block.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Blot } from "parchment"; -import Block from "quill/blots/block"; - -class MxBlock extends Block { - isEmptyTailBlock(): boolean { - const hasNoValidChildren = - this.children.length === 0 || - (this.children.length === 1 && this.children.head?.statics.tagName?.toString().toUpperCase() === "BR"); - return hasNoValidChildren; - } - - html(): string { - // quill return empty paragraph when there is no content (just empty line) - // to preserve the line breaks, we add empty space - if (this.domNode.childElementCount === 1 && this.domNode.children[0] instanceof HTMLBRElement) { - return this.domNode.outerHTML.replace(/
    /g, "
    "); - } else if (this.domNode.childElementCount === 0 && this.domNode.textContent?.trim() === "") { - this.domNode.innerHTML = "
    "; - return this.domNode.outerHTML; - } else { - return this.domNode.outerHTML; - } - } - - static IsMxBlock(blot: Blot | null): blot is MxBlock { - return blot?.statics.blotName === "mx-block"; - } - - static IsEmptyBlock(blot: Blot | null): boolean { - return blot != null && MxBlock.IsMxBlock(blot) && blot.isEmptyTailBlock(); - } -} - -MxBlock.blotName = "mx-block"; -export default MxBlock; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/button.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/button.ts deleted file mode 100644 index e42f201da9..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/button.ts +++ /dev/null @@ -1,55 +0,0 @@ -import Inline from "quill/blots/inline"; -const ATTRIBUTES = ["alt", "height", "width", "class", "id"]; - -type buttonAttributes = { - id?: string; - class?: string; - alt?: string; - height?: string; - width?: string; -}; -class Button extends Inline { - static blotName = "button"; - static tagName = "BUTTON"; - - format(name: string, value: unknown): void { - if (name !== this.statics.blotName || !value) { - super.format(name, value); - } else { - // @ts-expect-error the constructor is generic function, ts will consider sanitize not exist - this.domNode.textContent = value; - } - } - - static create(value: unknown): HTMLElement { - const domNode = super.create(value) as HTMLElement; - domNode.setAttribute("type", "button"); - if (value as buttonAttributes) { - const buttonAttr = value as buttonAttributes; - if (buttonAttr.id) { - domNode.setAttribute("id", buttonAttr.id); - } - if (buttonAttr.class) { - domNode.setAttribute("class", buttonAttr.class); - } - if (buttonAttr.width) { - domNode.setAttribute("width", buttonAttr.width); - } - if (buttonAttr.height) { - domNode.setAttribute("height", buttonAttr.height); - } - } - return domNode; - } - - static formats(domNode: Element): any { - return ATTRIBUTES.reduce((formats: Record, attribute) => { - if (domNode.hasAttribute(attribute)) { - formats[attribute] = domNode.getAttribute(attribute); - } - return formats; - }, {}); - } -} - -export default Button; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.scss b/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.scss deleted file mode 100644 index 4412b69b83..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.scss +++ /dev/null @@ -1,7 +0,0 @@ -.widget-rich-text { - .ql-editor { - ol { - list-style-type: none; - } - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.ts deleted file mode 100644 index 19ea919c67..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.ts +++ /dev/null @@ -1,40 +0,0 @@ -import ListItem from "quill/formats/list"; -import "./customList.scss"; -/** - * adding custom list item, alowing extra list style - */ - -export const STANDARD_LIST_TYPES = ["ordered", "checked", "unchecked", "bullet"]; - -export default class CustomListItem extends ListItem { - format(name: string, value: string): void { - if (name === this.statics.blotName && value) { - if (!STANDARD_LIST_TYPES.find(x => x === value)) { - this.domNode.setAttribute("data-custom-list", value); - this.domNode.setAttribute("data-list", "ordered"); - } else { - this.domNode.setAttribute("data-list", value); - this.domNode.removeAttribute("data-custom-list"); - } - } else { - super.format(name, value); - } - } - - static create(value: any): any { - const node = super.create(value) as HTMLElement; - if (!STANDARD_LIST_TYPES.find(x => x === value)) { - node.setAttribute("data-custom-list", value); - node.setAttribute("data-list", "ordered"); - } else { - node.setAttribute("data-list", value); - node.removeAttribute("data-custom-list"); - } - node.setAttribute("title", this.blotName); - return node; - } - - static formats(domNode: HTMLElement): string | undefined { - return domNode.dataset.customList || domNode.dataset.list || undefined; - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts deleted file mode 100644 index 6b3e445bc0..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Scope, StyleAttributor } from "parchment"; -import { CustomFontsType } from "../../../typings/RichTextProps"; -import "./fonts.scss"; - -export const FONT_LIST = [ - { value: "andale-mono", description: "Andale Mono", style: "'andale mono', monospace" }, - { value: "arial", description: "Arial", style: "arial, helvetica, sans-serif" }, - { value: "arial-black", description: "Arial Black", style: "'arial black', sans-serif" }, - { value: "book-antiqua", description: "Book Antiqua", style: "'book antiqua', palatino, serif" }, - { value: "comic-sans", description: "Comic Sans MS", style: "'comic sans ms', sans-serif" }, - { value: "courier-new", description: "Courier New", style: "'courier new', courier, monospace" }, - { value: "helvetica", description: "Helvetica", style: "helvetica, arial, sans-serif" }, - { value: "impact", description: "Impact", style: "impact, sans-serif" }, - { value: "serif", description: "Serif", style: "serif" }, - { value: "symbol", description: "Symbol", style: "symbol" }, - { value: "terminal", description: "Terminal", style: "terminal, monaco, monospace" }, - { value: "times-new-roman", description: "Times New Roman", style: "'times new roman', times, serif" }, - { value: "trebuchet", description: "Trebuchet MS", style: "'trebuchet ms', geneva, sans-serif" } -]; - -const config = { - scope: Scope.INLINE -}; - -export class FontStyleAttributor extends StyleAttributor { - private fontList: typeof FONT_LIST = []; - - constructor(fontList: typeof FONT_LIST) { - super("font", "font-family", config); - this.fontList = fontList; - } - - add(node: HTMLElement, value: any): boolean { - if (!this.canAdd(node, value)) { - return false; - } - node.dataset.value = value; - const allFonts = [...FONT_LIST, ...this.fontList]; - const style = allFonts.find(x => x.value === value)?.style; - if (style) { - super.add(node, style); - } else { - return false; - } - return true; - } - - value(node: HTMLElement): any { - const value = node.dataset.value; - if (this.canAdd(node, value) && value) { - return value; - } - return ""; - } -} - -export function formatCustomFonts(fonts: CustomFontsType[] = []): typeof FONT_LIST { - return fonts.map(font => ({ - value: font.fontName?.toLowerCase().split(" ").join("-") ?? "", - description: font.fontName ?? "", - style: font.fontStyle ?? "" - })); -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fontsize.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fontsize.ts deleted file mode 100644 index 29335852a3..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fontsize.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Attributor } from "parchment"; -import Quill from "quill"; -const Size = Quill.import("attributors/style/size") as Attributor; - -import "./fonts.scss"; - -export const FONT_SIZE_LIST = [ - "8px", - "9px", - "10px", - "12px", - "14px", - "16px", - "20px", - "24px", - "32px", - "42px", - "54px", - "68px", - "84px", - "98px" -]; - -Size.whitelist = FONT_SIZE_LIST; -Quill.register(Size, true); diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/formula.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/formula.ts deleted file mode 100644 index 8c3d7525ac..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/formula.ts +++ /dev/null @@ -1,31 +0,0 @@ -import katex from "katex"; -import Embed from "quill/blots/embed"; - -class Formula extends Embed { - static blotName = "formula"; - static className = "ql-formula"; - static tagName = "SPAN"; - - html(): string { - const { formula } = this.value(); - return `${formula}`; - } - - static create(value: string): HTMLElement { - const node = super.create(value) as HTMLElement; - if (typeof value === "string") { - katex.render(value, node, { - throwOnError: false, - errorColor: "#f00" - }); - node.setAttribute("data-value", value); - } - return node; - } - - static value(domNode: Element): any { - return domNode.getAttribute("data-value"); - } -} - -export default Formula; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/header.scss b/packages/pluggableWidgets/rich-text-web/src/utils/formats/header.scss deleted file mode 100644 index f9c582044e..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/header.scss +++ /dev/null @@ -1,12 +0,0 @@ -.widget-rich-text { - .widget-rich-text-toolbar { - .ql-header { - .ql-picker-item, - .ql-picker-label { - &:before { - content: "Paragraph"; - } - } - } - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/image.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/image.ts deleted file mode 100644 index f9d3ae910e..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/image.ts +++ /dev/null @@ -1,38 +0,0 @@ -import Image from "quill/formats/image"; -import { fetchDocumentUrl } from "../mx-data"; - -const ATTRIBUTES = ["alt", "height", "width", "data-src"]; - -class CustomImage extends Image { - format(name: string, value: string): void { - if (ATTRIBUTES.indexOf(name) > -1) { - if (name === "src" && this.domNode.hasAttribute("data-src")) { - return; // Do not set src directly, use data-src instead - } - if (value) { - this.domNode.setAttribute(name, value); - } else { - this.domNode.removeAttribute(name); - } - } else { - super.format(name, value); - } - - if (name === "data-src" && !this.domNode.dataset.entity) { - this.domNode.setAttribute("src", fetchDocumentUrl(value, Date.now())); - // Mark the image as an entity to prevent further src changes - this.domNode.setAttribute("data-entity", "true"); - } - } - - static formats(domNode: Element): any { - return ATTRIBUTES.reduce((formats: Record, attribute) => { - if (domNode.hasAttribute(attribute)) { - formats[attribute] = domNode.getAttribute(attribute); - } - return formats; - }, {}); - } -} - -export default CustomImage; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/indent.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/indent.ts deleted file mode 100644 index d00f8a1ddc..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/indent.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Scope, StyleAttributor } from "parchment"; -import "./fonts.scss"; - -const INDENT_MAGIC_NUMBER = 3; -const indentLists = ["3em", "6em", "9em", "12em", "15em", "18em", "21em", "24em", "27em"]; - -/** - * overriding current quill's indent format by using inline style instead of classname - * toolbar's indent button also have to be overriden using getIndentHandler - */ -class IndentAttributor extends StyleAttributor { - add(node: HTMLElement, value: string | number): boolean { - let normalizedValue = 0; - if (value === "+1" || value === "-1") { - const indent = this.value(node) || 0; - normalizedValue = value === "+1" ? indent + INDENT_MAGIC_NUMBER : indent - INDENT_MAGIC_NUMBER; - } else if (typeof value === "number") { - const modValue = value % INDENT_MAGIC_NUMBER; - const indent = this.value(node) || 0; - normalizedValue = - indent + - (modValue === 1 - ? INDENT_MAGIC_NUMBER - : modValue === 2 || modValue === -1 - ? -INDENT_MAGIC_NUMBER - : value); - } - if (normalizedValue === 0) { - this.remove(node); - return true; - } - return super.add(node, `${normalizedValue}em`); - } - - canAdd(node: HTMLElement, value: string): boolean { - return super.canAdd(node, value); - } - - value(node: HTMLElement): number | undefined { - return parseInt(super.value(node).replace("em", ""), 10) || undefined; // Don't return NaN - } -} - -export const IndentLeftStyle = new IndentAttributor("indent-left", "padding-left", { - scope: Scope.BLOCK, - whitelist: indentLists -}); - -export const IndentRightStyle = new IndentAttributor("indent-right", "padding-right", { - scope: Scope.BLOCK, - whitelist: indentLists -}); diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/link.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/link.ts deleted file mode 100644 index 7279444450..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/link.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as linkify from "linkifyjs"; -import Link from "quill/formats/link"; -import { linkConfigType } from "../formats"; - -/** - * Custom Link handler, allowing extra config: target and default protocol. - */ -export default class CustomLink extends Link { - format(name: string, value: unknown): void { - if (name !== this.statics.blotName || !value) { - super.format(name, value); - } else if ((value as linkConfigType)?.href !== undefined) { - const linkConfig = value as linkConfigType; - // @ts-expect-error the constructor is generic function, ts will consider sanitize not exist - this.domNode.setAttribute("href", this.constructor.getLink(this.constructor.sanitize(linkConfig.href))); - this.domNode.textContent = linkConfig.text ?? linkConfig.href; - if (linkConfig.target) { - this.domNode.setAttribute("target", linkConfig.target); - } - if (linkConfig.title) { - this.domNode.setAttribute("title", linkConfig.title); - } - } else { - // @ts-expect-error the constructor is generic function, ts will consider sanitize not exist - this.domNode.setAttribute("href", this.constructor.getLink(this.constructor.sanitize(value))); - } - } - - static create(value: unknown): HTMLElement { - if ((value as linkConfigType)?.href !== undefined) { - const linkConfig = value as linkConfigType; - const node = super.create(linkConfig.href) as HTMLElement; - node.setAttribute("href", this.getLink(this.sanitize(linkConfig.href))); - node.setAttribute("rel", "noopener noreferrer"); - node.setAttribute("title", linkConfig.title ?? linkConfig.href); - node.setAttribute("target", linkConfig.target || "_blank"); - return node; - } else { - // @ts-expect-error type mismatch expected - return super.create(value); - } - } - - static getLink(url: string): string { - const foundLinks = linkify.find(url, { - defaultProtocol: "https" - }); - let results = url; - if (foundLinks && foundLinks.length > 0) { - results = foundLinks[0].href; - } - - return results; - } -} - -export class CustomLinkNoValidation extends CustomLink { - static getLink(url: string): string { - return url; - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/assets/css/quill-table-better.scss b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/assets/css/quill-table-better.scss deleted file mode 100644 index 7889ae6db5..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/assets/css/quill-table-better.scss +++ /dev/null @@ -1,684 +0,0 @@ -$line-color: #0589f3; -$focused-cell-background: rgba(158, 207, 250, 0.3); -$focused-border-color: #3779eb; -$tooltip-color-simple: #000000d9; -$tooltip-color-error: #db3700; -$hover-background: #f0f0f0; -$border-color-simple: #00000026; -$color-white: #fff; -$color-ccced1: #ccced1; -$border: 1px solid $color-ccced1; -$focused-border: 1px solid $focused-border-color; - -/*mixin*/ -@mixin boxShadow($color) { - box-shadow: 0 1px 2px 1px $color; -} - -@mixin qlTableTooltip($color) { - font-size: 12px; - min-width: 32px; - line-height: 20px; - padding: 6px; - white-space: nowrap; - color: $color-white; - text-align: center; - word-wrap: break-word; - background: $color; - border-radius: 6px; - position: absolute; - z-index: 11; - left: 50%; - bottom: -10px; - transform: translate(-50%, 100%); - - &::before { - @extend .ql-table-triangle-common; - border-bottom-color: $color !important; - top: -20px; - } - - &:hover { - display: block; - } - - &-hidden { - display: none !important; - } -} - -@mixin triangle($direction1, $direction2, $color1, $color2) { - &:not(.ql-table-triangle-none)::before { - @extend .ql-table-triangle-common; - #{$direction1}: -20px; - border-#{$direction2}-color: $color1 !important; - } - - &:not(.ql-table-triangle-none)::after { - @extend .ql-table-triangle-common; - #{$direction1}: -19px; - border-#{$direction2}-color: $color2 !important; - } -} - -/*extend-style*/ -.ql-cell-selected-after { - content: ""; - pointer-events: none; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: $focused-cell-background; -} - -.ql-editor td { - border: none; -} - -.ql-container:not(.ql-disabled) .ql-table-better.ql-table-grid { - td { - border: 1px dotted rgba(0, 0, 0, 0.1); - } -} - -.ql-table { - &-border-shadow { - @include boxShadow($border-color-simple); - background: $color-white; - border-radius: 2px; - border: $border; - } - - &-button-disabled { - background: #f2f2f2 !important; - pointer-events: none; - - svg { - .ql-fill { - fill: #999 !important; - } - - .ql-stroke { - stroke: #999 !important; - } - } - } - - &-center { - display: flex; - justify-content: center; - align-items: center; - } - - &-color-container { - border: $border; - height: 30px; - box-sizing: border-box; - display: flex; - - .label-field-view-color { - flex: 1; - - .clr-field { - width: 100%; - - &, - & .property-input { - height: 100%; - } - - .property-input { - @extend .ql-table-input; - border: 1px solid transparent; - color: fieldtext; - min-width: 110px; - } - - button { - height: 24px; - width: 24px; - right: 3px; - } - } - - label { - display: block; - } - } - - .color-picker { - width: 30px; - border-left: $border; - box-sizing: border-box; - position: relative; - @extend .ql-table-center; - - .color-button { - width: 20px; - height: 20px; - border: $border; - box-sizing: border-box; - cursor: pointer; - position: relative; - } - - .color-unselected { - position: relative; - - &::after { - content: ""; - position: absolute; - width: 1px; - height: 26px; - background: red; - transform-origin: 50%; - transform: rotate(45deg); - left: 50%; - top: -4px; - } - } - - .color-picker-select { - position: absolute; - right: 0; - bottom: 0; - width: 156px; - transform: translateY(100%); - background: $color-white; - z-index: 10; - @include boxShadow($color-ccced1); - - .erase-container { - align-items: center; - cursor: pointer; - display: flex; - height: 30px; - padding: 0 12px; - - &:hover { - background: #f0f0f0; - } - - & > button { - border: none; - outline: none; - background: inherit; - height: 100%; - cursor: pointer; - } - } - - & > .erase-container { - @extend .ql-table-input-focus; - margin-bottom: 4px; - } - - .color-list { - display: flex; - flex-wrap: wrap; - padding: 0 12px; - margin: 0; - justify-content: space-between; - - & > li { - list-style: none; - width: 24px; - height: 24px; - margin: 2px 0; - position: relative; - cursor: pointer; - - &[data-color="#ffffff"] { - border: $border; - box-sizing: border-box; - } - } - } - } - - .color-picker-palette { - width: 100%; - height: 100%; - position: absolute; - left: 0; - top: 0; - z-index: 1; - background: $color-white; - - .color-picker-wrap { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - } - } - } - } - - &-disabled { - background: #f2f2f2; - pointer-events: none; - } - - &-dropdown, - &-dropdown-properties { - display: flex; - height: 100%; - align-items: center; - position: relative; - padding: 0 4px; - - &:hover { - background: $hover-background; - } - - &-icon { - display: flex; - flex-shrink: 0; - } - - &-label { - width: 100%; - min-width: 100%; - line-height: 24px; - font-weight: bold; - margin-bottom: 6px; - display: block; - } - - &-list { - @extend .ql-table-border-shadow; - margin: 0; - min-width: 100px; - padding: 0; - position: absolute; - bottom: 0; - left: 0; - transform: translateY(100%); - z-index: 10; - - li { - line-height: 30px; - list-style: none; - padding-left: 10px; - - &:hover { - background-color: $hover-background; - } - } - } - - &-text { - align-items: center; - display: flex; - flex: 1; - height: 100%; - } - } - - &-dropdown-properties { - width: 80px; - height: 30px; - border: $border; - box-sizing: border-box; - - &:hover { - background: none; - } - } - - &-input { - background: inherit; - border: $border; - height: 30px; - outline: none; - padding-left: 6px; - width: 80px; - - &:focus::placeholder { - color: transparent; - } - - &:focus { - @extend .ql-table-input-focus; - } - - &:focus + label { - display: block; - } - - &:not(:placeholder-shown) + label { - display: block; - } - - &-focus { - border: $focused-border; - box-shadow: 0 0 0 3px #cae1fc; - } - } - - &-menus-container { - position: absolute; - display: flex; - align-items: center; - height: 40px; - width: fit-content; - padding: 4px; - box-sizing: border-box; - z-index: 1; - @extend .ql-table-border-shadow; - } - - &-properties-form { - @include boxShadow($color-ccced1); - background: $color-white; - padding-bottom: 8px; - position: fixed; - left: 0; - top: 0; - width: 320px; - z-index: 1; - - .properties-form-header { - height: 40px; - line-height: 40px; - padding: 0 12px; - border-bottom: $border; - margin: 0; - box-sizing: border-box; - color: #333; - font-size: 14px; - } - - .properties-form-row { - display: flex; - flex-wrap: wrap; - padding: 8px 12px; - justify-content: space-between; - - .ql-table-check-container { - display: flex; - border: $border; - align-items: center; - - & .ql-table-tooltip-hover { - padding: 6px 10px; - cursor: pointer; - - &:hover { - background: $hover-background; - } - } - - .ql-table-btns-checked { - background: #f0f7ff; - - & > svg path { - stroke: #2977ff; - } - } - } - } - - .properties-form-row-full { - .ql-table-color-container { - width: 100%; - - .property-input { - width: 100%; - } - } - } - - .property-input { - @extend .ql-table-input; - } - - .properties-form-action-row { - display: flex; - justify-content: space-around; - padding: 0 12px; - - & > button { - background: $color-white; - outline: none; - border: none; - height: 30px; - cursor: pointer; - @extend .ql-table-center; - flex: 1; - - & > span { - margin: 0 2px; - display: flex; - } - - &:hover { - background: $hover-background; - } - - &[disabled] { - background-color: transparent; - } - } - } - - .ql-table-color-selected { - @extend .ql-table-selected; - background-position: center; - } - - .ql-table-dropdown-selected { - @extend .ql-table-selected; - background-position: calc(100% - 10px) center; - } - } - - &-select-container { - @extend .ql-table-border-shadow; - @extend .ql-table-center; - box-sizing: border-box; - flex-direction: column; - padding: 2px; - position: absolute; - top: 24px; - width: 190px; - z-index: 10; - - .ql-table-select-list { - @extend .ql-table-center; - flex-wrap: wrap; - } - - .ql-table-select-label { - color: #222f3eb3; - line-height: 16px; - margin-top: 2px; - text-align: center; - width: 100%; - } - - span { - border: 1px solid black; - box-sizing: border-box; - height: 16px; - margin: 1px; - width: 16px; - } - } - - &-selected { - @extend .ql-table-input-focus; - background-image: url("../icon/check.png"); - background-repeat: no-repeat; - background-size: 16px; - box-sizing: border-box; - } - - &-temporary { - display: none; - } - - &-tooltip { - @include qlTableTooltip($tooltip-color-simple); - - &-error { - @include qlTableTooltip($tooltip-color-error); - - & { - white-space: pre-wrap; - z-index: 9; - } - } - } - - &-tooltip-hover { - display: flex; - position: relative; - - &:hover .ql-table-tooltip, - &:hover + .ql-table-tooltip { - display: block; - } - } - - &-triangle-common { - border: 10px solid transparent; - content: ""; - position: absolute; - left: 50%; - transform: translateX(-50%); - } - - &-triangle-down { - @include triangle("top", "bottom", $border-color-simple, $color-white); - } - - &-triangle-up { - @include triangle("bottom", "top", $border-color-simple, $color-white); - } -} - -.ql-operate { - &-block { - position: absolute; - z-index: 10; - border: 1px solid #979797; - cursor: nwse-resize; - - &-move { - cursor: crosshair; - border: none; - } - } - - &-drag-table { - border: 1px dashed #000; - position: absolute; - } - - &-line-container { - position: absolute; - z-index: 10; - @extend .ql-table-center; - - .ql-operate-line { - background-color: $line-color; - } - } -} - -.ql-cell { - &-focused { - position: relative; - - &::after { - @extend .ql-cell-selected-after; - border: $focused-border; - } - } - - &-selected { - position: relative; - - &::after { - @extend .ql-cell-selected-after; - } - } -} - -.label-field-view { - position: relative; - - &-input-wrapper { - position: relative; - height: 100%; - } - - &-input-wrapper > label, - label { - background: $color-white; - color: #999; - display: none; - position: absolute; - left: 0; - top: -8px; - transform: scale(0.75); - } - - &-status { - @extend .ql-table-tooltip-error; - max-width: 160px; - width: max-content; - } - - &-error { - & > input { - border-color: $tooltip-color-error !important; - animation: ql-table-input-shake 0.3s ease both; - - &:focus { - box-shadow: 0 0 0 3px #ff401f4d !important; - } - } - - & > label { - color: $tooltip-color-error; - } - } -} - -button.ql-table-better { - position: relative; -} - -ol.table-list-container { - counter-reset: list-0; // reset counter -} - -// animation -@keyframes ql-table-input-shake { - 20% { - transform: translateX(-2px); - } - - 40% { - transform: translateX(2px); - } - - 60% { - transform: translateX(-1px); - } - - 80% { - transform: translateX(1px); - } -} - -.ql-table-menus-container.ql-table-grid { - .grid-toggle { - color: var(--link-color); - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/assets/icon/check.png b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/assets/icon/check.png deleted file mode 100644 index 812b34da0f..0000000000 Binary files a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/assets/icon/check.png and /dev/null differ diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/config/index.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/config/index.ts deleted file mode 100644 index e2ef3703eb..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/config/index.ts +++ /dev/null @@ -1,448 +0,0 @@ -import type { Props, UseLanguageHandler } from "../types"; -import { convertUnitToInteger, isValidColor, isValidDimensions } from "../utils"; - -interface Options { - type: string; - attribute: Props; -} - -const CELL_ATTRIBUTE = ["data-row", "width", "height", "colspan", "rowspan", "style"]; - -const CELL_DEFAULT_VALUES: Props = { - "border-style": "none", - "border-color": "", - "border-width": "", - "background-color": "", - width: "", - height: "", - padding: "", - "text-align": "left", - "vertical-align": "middle" -}; - -const CELL_DEFAULT_WIDTH = 72; - -const CELL_PROPERTIES = [ - "border-style", - "border-color", - "border-width", - "background-color", - "width", - "height", - "padding", - "text-align", - "vertical-align" -]; - -const COLORS = [ - "aliceblue", - "antiquewhite", - "aqua", - "aquamarine", - "azure", - "beige", - "bisque", - "black", - "blanchedalmond", - "blue", - "blueviolet", - "brown", - "burlywood", - "cadetblue", - "chartreuse", - "chocolate", - "coral", - "cornflowerblue", - "cornsilk", - "crimson", - "currentcolor", - "currentcolor", - "cyan", - "darkblue", - "darkcyan", - "darkgoldenrod", - "darkgray", - "darkgreen", - "darkgrey", - "darkkhaki", - "darkmagenta", - "darkolivegreen", - "darkorange", - "darkorchid", - "darkred", - "darksalmon", - "darkseagreen", - "darkslateblue", - "darkslategray", - "darkslategrey", - "darkturquoise", - "darkviolet", - "deeppink", - "deepskyblue", - "dimgray", - "dimgrey", - "dodgerblue", - "firebrick", - "floralwhite", - "forestgreen", - "fuchsia", - "gainsboro", - "ghostwhite", - "gold", - "goldenrod", - "gray", - "green", - "greenyellow", - "grey", - "honeydew", - "hotpink", - "indianred", - "indigo", - "ivory", - "khaki", - "lavender", - "lavenderblush", - "lawngreen", - "lemonchiffon", - "lightblue", - "lightcoral", - "lightcyan", - "lightgoldenrodyellow", - "lightgray", - "lightgreen", - "lightgrey", - "lightpink", - "lightsalmon", - "lightseagreen", - "lightskyblue", - "lightslategray", - "lightslategrey", - "lightsteelblue", - "lightyellow", - "lime", - "limegreen", - "linen", - "magenta", - "maroon", - "mediumaquamarine", - "mediumblue", - "mediumorchid", - "mediumpurple", - "mediumseagreen", - "mediumslateblue", - "mediumspringgreen", - "mediumturquoise", - "mediumvioletred", - "midnightblue", - "mintcream", - "mistyrose", - "moccasin", - "navajowhite", - "navy", - "oldlace", - "olive", - "olivedrab", - "orange", - "orangered", - "orchid", - "palegoldenrod", - "palegreen", - "paleturquoise", - "palevioletred", - "papayawhip", - "peachpuff", - "peru", - "pink", - "plum", - "powderblue", - "purple", - "rebeccapurple", - "red", - "rosybrown", - "royalblue", - "saddlebrown", - "salmon", - "sandybrown", - "seagreen", - "seashell", - "sienna", - "silver", - "skyblue", - "slateblue", - "slategray", - "slategrey", - "snow", - "springgreen", - "steelblue", - "tan", - "teal", - "thistle", - "tomato", - "transparent", - "turquoise", - "violet", - "wheat", - "white", - "whitesmoke", - "yellow", - "yellowgreen" -]; - -const DEVIATION = 2; - -const TABLE_PROPERTIES = [ - "border-style", - "border-color", - "border-width", - "background-color", - "width", - "height", - "align" -]; - -function getCellProperties(attribute: Props, useLanguage: UseLanguageHandler) { - return { - title: useLanguage("cellProps"), - properties: [ - { - content: useLanguage("border"), - children: [ - { - category: "dropdown", - propertyName: "border-style", - value: attribute["border-style"], - options: ["dashed", "dotted", "double", "groove", "inset", "none", "outset", "ridge", "solid"] - }, - { - category: "color", - propertyName: "border-color", - value: attribute["border-color"], - attribute: { - type: "text", - placeholder: useLanguage("color") - }, - valid: isValidColor, - message: useLanguage("colorMsg") - }, - { - category: "input", - propertyName: "border-width", - value: convertUnitToInteger(attribute["border-width"]), - attribute: { - type: "text", - placeholder: useLanguage("width") - }, - valid: isValidDimensions, - message: useLanguage("dimsMsg") - } - ] - }, - { - content: useLanguage("background"), - children: [ - { - category: "color", - propertyName: "background-color", - value: attribute["background-color"], - attribute: { - type: "text", - placeholder: useLanguage("color") - }, - valid: isValidColor, - message: useLanguage("colorMsg") - } - ] - }, - { - content: useLanguage("dims"), - children: [ - { - category: "input", - propertyName: "width", - value: convertUnitToInteger(attribute["width"]), - attribute: { - type: "text", - placeholder: useLanguage("width") - }, - valid: isValidDimensions, - message: useLanguage("dimsMsg") - }, - { - category: "input", - propertyName: "height", - value: convertUnitToInteger(attribute["height"]), - attribute: { - type: "text", - placeholder: useLanguage("height") - }, - valid: isValidDimensions, - message: useLanguage("dimsMsg") - }, - { - category: "input", - propertyName: "padding", - value: convertUnitToInteger(attribute["padding"]), - attribute: { - type: "text", - placeholder: useLanguage("padding") - }, - valid: isValidDimensions, - message: useLanguage("dimsMsg") - } - ] - }, - { - content: useLanguage("tblCellTxtAlm"), - children: [ - { - category: "menus", - propertyName: "text-align", - value: attribute["text-align"], - menus: [ - { - icon: "icons icon-Text-align-left", - describe: useLanguage("alCellTxtL"), - align: "left" - }, - { - icon: "icons icon-Text-align-center", - describe: useLanguage("alCellTxtC"), - align: "center" - }, - { - icon: "icons icon-Text-align-right", - describe: useLanguage("alCellTxtR"), - align: "right" - }, - { - icon: "icons icon-Text-align-justify", - describe: useLanguage("jusfCellTxt"), - align: "justify" - } - ] - }, - { - category: "menus", - propertyName: "vertical-align", - value: attribute["vertical-align"], - menus: [ - { icon: "icons icon-Align-top", describe: useLanguage("alCellTxtT"), align: "top" }, - { icon: "icons icon-Align-middle", describe: useLanguage("alCellTxtM"), align: "middle" }, - { icon: "icons icon-Align-bottom", describe: useLanguage("alCellTxtB"), align: "bottom" } - ] - } - ] - } - ] - }; -} - -function getTableProperties(attribute: Props, useLanguage: UseLanguageHandler) { - return { - title: useLanguage("tblProps"), - properties: [ - { - content: useLanguage("border"), - children: [ - { - category: "dropdown", - propertyName: "border-style", - value: attribute["border-style"], - options: ["dashed", "dotted", "double", "groove", "inset", "none", "outset", "ridge", "solid"] - }, - { - category: "color", - propertyName: "border-color", - value: attribute["border-color"], - attribute: { - type: "text", - placeholder: useLanguage("color") - }, - valid: isValidColor, - message: useLanguage("colorMsg") - }, - { - category: "input", - propertyName: "border-width", - value: convertUnitToInteger(attribute["border-width"]), - attribute: { - type: "text", - placeholder: useLanguage("width") - }, - valid: isValidDimensions, - message: useLanguage("dimsMsg") - } - ] - }, - { - content: useLanguage("background"), - children: [ - { - category: "color", - propertyName: "background-color", - value: attribute["background-color"], - attribute: { - type: "text", - placeholder: useLanguage("color") - }, - valid: isValidColor, - message: useLanguage("colorMsg") - } - ] - }, - { - content: useLanguage("dimsAlm"), - children: [ - { - category: "input", - propertyName: "width", - value: convertUnitToInteger(attribute["width"]), - attribute: { - type: "text", - placeholder: useLanguage("width") - }, - valid: isValidDimensions, - message: useLanguage("dimsMsg") - }, - { - category: "input", - propertyName: "height", - value: convertUnitToInteger(attribute["height"]), - attribute: { - type: "text", - placeholder: useLanguage("height") - }, - valid: isValidDimensions, - message: useLanguage("dimsMsg") - }, - { - category: "menus", - propertyName: "align", - value: attribute["align"], - menus: [ - { icon: "icons icon-Text-align-left", describe: useLanguage("alTblL"), align: "left" }, - { icon: "icons icon-Text-align-center", describe: useLanguage("tblC"), align: "center" }, - { icon: "icons icon-Text-align-right", describe: useLanguage("alTblR"), align: "right" } - ] - } - ] - } - ] - }; -} - -function getProperties({ type, attribute }: Options, useLanguage: UseLanguageHandler) { - if (type === "table") return getTableProperties(attribute, useLanguage); - return getCellProperties(attribute, useLanguage); -} - -export { - CELL_ATTRIBUTE, - CELL_DEFAULT_VALUES, - CELL_DEFAULT_WIDTH, - CELL_PROPERTIES, - COLORS, - DEVIATION, - getProperties, - TABLE_PROPERTIES -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/header.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/header.ts deleted file mode 100644 index c40259ec60..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/header.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { BlockBlot } from "parchment"; -import Quill from "quill"; -import QuillHeader from "quill/formats/header"; -import type { Props, TableCellChildren } from "../types"; -import { getCellFormats, getCorrectCellBlot } from "../utils"; -import { ListContainer } from "./list"; -import { TableCell, TableCellBlock } from "./table"; - -const Header = QuillHeader as typeof BlockBlot; - -class TableHeader extends Header { - static blotName = "table-header"; - static className = "ql-table-header"; - - // @ts-ignore - next: this | null; - // @ts-ignore - parent: TableCell; - - static create(formats: Props) { - const { cellId, value } = formats; - const node = super.create(value); - node.setAttribute("data-cell", cellId); - return node; - } - - format(name: string, value: string | Props, isReplace?: boolean) { - if (name === "header") { - const _value = this.statics.formats(this.domNode).value; - const cellId = this.domNode.getAttribute("data-cell"); - if (_value == value || !value) { - this.replaceWith(TableCellBlock.blotName, cellId); - } else { - super.format("table-header", { cellId, value }); - } - } else if (name === "list") { - const [formats, cellId] = this.getCellFormats(this.parent); - if (isReplace) { - this.wrap(ListContainer.blotName, { ...formats, cellId }); - } else { - this.wrap(TableCell.blotName, formats); - } - return this.replaceWith("table-list", value); - } else if (name === TableCell.blotName) { - return this.wrap(name, value); - } else if (name === this.statics.blotName && !value) { - const cellId = this.domNode.getAttribute("data-cell"); - this.replaceWith(TableCellBlock.blotName, cellId); - } else { - super.format(name, value); - } - } - - static formats(domNode: HTMLElement) { - const cellId = domNode.getAttribute("data-cell"); - const value = this.tagName.indexOf(domNode.tagName) + 1; - return { cellId, value }; - } - - formats() { - const formats = this.attributes.values(); - const format = this.statics.formats(this.domNode, this.scroll); - if (format != null) { - formats[this.statics.blotName] = format; - } - return formats; - } - - getCellFormats(parent: TableCell | TableCellChildren) { - const cellBlot = getCorrectCellBlot(parent); - return getCellFormats(cellBlot!); - } -} - -Quill.register( - { - "formats/table-header": TableHeader - }, - true -); - -export default TableHeader; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/list.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/list.ts deleted file mode 100644 index fba43ab988..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/list.ts +++ /dev/null @@ -1,161 +0,0 @@ -// @ts-nocheck -import type { BlockBlot, ContainerBlot } from "parchment"; -import Quill from "quill"; -import QuillList from "quill/formats/list"; -import QuillContainer from "quill/blots/container"; -import { CELL_ATTRIBUTE } from "../config"; -import type { Props, TableCellChildren } from "../types"; -import { getCellFormats, getCorrectCellBlot } from "../utils"; -import { TableCell, TableCellBlock } from "./table"; - -const List = QuillList as typeof BlockBlot; -const Container = QuillContainer as typeof ContainerBlot; -const DEFAULT_ATTRIBUTE = ["colspan", "rowspan"]; - -class ListContainer extends Container { - next: this | null; - parent: TableCell; - - static create(value: Props) { - const node = super.create() as HTMLElement; - for (const key of DEFAULT_ATTRIBUTE) { - if (value[key] == "1") delete value[key]; - } - const keys = Object.keys(value); - for (const key of keys) { - if (key === "data-row") { - node.setAttribute(key, value[key]); - } else if (key === "cellId") { - node.setAttribute("data-cell", value[key]); - } else { - node.setAttribute(`data-${key}`, value[key]); - } - } - return node; - } - - format(name: string, value: string | Props) { - return this.wrap(name, value); - } - - static formats(domNode: HTMLElement) { - const formats = CELL_ATTRIBUTE.reduce((formats: Props, attr) => { - const name = attr.includes("data") ? attr : `data-${attr}`; - if (domNode.hasAttribute(name)) { - formats[attr] = domNode.getAttribute(name) ?? ""; - } - return formats; - }, {}); - formats["cellId"] = domNode.getAttribute("data-cell") ?? ""; - for (const key of DEFAULT_ATTRIBUTE) { - if (!formats[key]) formats[key] = "1"; - } - return formats; - } - - formats() { - const formats = this.statics.formats(this.domNode, this.scroll); - return { [this.statics.blotName]: formats }; - } -} -ListContainer.blotName = "table-list-container"; -ListContainer.className = "table-list-container"; -ListContainer.tagName = "OL"; - -class TableList extends List { - parent: ListContainer; - - format(name: string, value: string | Props, isReplace?: boolean) { - const list = this.formats()[this.statics.blotName]; - if (name === "list") { - const [formats, cellId] = this.getCellFormats(this.parent); - if (!value || value === list) { - this.setReplace(!!isReplace, formats); - return this.replaceWith(TableCellBlock.blotName, cellId); - } else if (value !== list) { - return this.replaceWith(this.statics.blotName, value); - } - } else if (name === ListContainer.blotName) { - if (typeof value === "string") { - value = { cellId: value }; - } - const [formats, cellId] = this.getCorrectCellFormats(value); - this.wrap(TableCell.blotName, formats); - this.wrap(name, { ...formats, cellId }); - } else if (name === "header") { - const [formats, cellId] = this.getCellFormats(this.parent); - this.setReplace(!!isReplace, formats); - return this.replaceWith("table-header", { cellId, value }); - } else if (name === TableCell.blotName) { - const listContainer = this.getListContainer(this.parent); - if (!listContainer) return; - const formats = listContainer.formats()[listContainer.statics.blotName]; - this.wrap(name, value); - // @ts-ignore - this.wrap(ListContainer.blotName, { ...formats, ...value }); - } else if (name === this.statics.blotName && !value) { - const [, cellId] = this.getCellFormats(this.parent); - this.replaceWith(TableCellBlock.blotName, cellId); - } else { - super.format(name, value); - } - } - - getCellFormats(parent: TableCell | TableCellChildren) { - const cellBlot = getCorrectCellBlot(parent); - return getCellFormats(cellBlot!); - } - - getCorrectCellFormats(value: Props): [Props, string] { - const cellBlot = getCorrectCellBlot(this.parent); - if (!cellBlot) { - const cellId = value["cellId"]; - const formats = { ...value }; - delete formats["cellId"]; - return [formats, cellId]; - } else { - const [formats, cellId] = getCellFormats(cellBlot); - const _formats = { ...formats, ...value }; - const _cellId = _formats["cellId"] || cellId; - delete _formats["cellId"]; - return [_formats, _cellId]; - } - } - - private getListContainer(blot: ListContainer) { - while (blot) { - if (blot.statics.blotName === ListContainer.blotName) { - return blot; - } - // @ts-ignore - blot = blot.parent; - } - return null; - } - - static register() { - Quill.register(ListContainer); - } - - setReplace(isReplace: boolean, formats: Props) { - if (isReplace) { - this.parent.replaceWith(TableCell.blotName, formats); - } else { - this.wrap(TableCell.blotName, formats); - } - } -} -TableList.blotName = "table-list"; -TableList.className = "table-list"; - -Quill.register( - { - "formats/table-list": TableList - }, - true -); - -ListContainer.allowedChildren = [TableList]; -TableList.requiredContainer = ListContainer; - -export { ListContainer, TableList as default }; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/table.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/table.ts deleted file mode 100644 index 4494b9b4ee..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/formats/table.ts +++ /dev/null @@ -1,785 +0,0 @@ -// @ts-nocheck -import type { BlockBlot, ContainerBlot, LinkedList } from "parchment"; -import Quill from "quill"; -import QuillBlock from "quill/blots/block"; -import QuillContainer from "quill/blots/container"; -import { CELL_ATTRIBUTE, CELL_DEFAULT_WIDTH, DEVIATION } from "../config"; -import type { Props, TableCellAllowedChildren } from "../types"; -import { filterWordStyle, getCellChildBlot, getCellFormats, getCellId, getCopyTd, getCorrectCellBlot } from "../utils"; -import TableHeader from "./header"; -import { ListContainer } from "./list"; - -const Block = QuillBlock as typeof BlockBlot; -const Container = QuillContainer as typeof ContainerBlot; -const TABLE_ATTRIBUTE = ["border", "cellspacing", "style", "data-class"]; -const COL_ATTRIBUTE = ["width"]; - -class TableCellBlock extends Block { - static blotName = "table-cell-block"; - static className = "ql-table-block"; - static tagName = "P"; - - next: this | null; - parent: TableCell; - - static create(value: string) { - const node = super.create(); - if (value) { - node.setAttribute("data-cell", value); - } else { - node.setAttribute("data-cell", cellId()); - } - return node; - } - - format(name: string, value: string | Props) { - const cellId = this.formats()[this.statics.blotName]; - if (name === TableCell.blotName && value) { - this.wrap(TableRow.blotName); - return this.wrap(name, value); - } else if (name === TableContainer.blotName) { - this.wrap(name, value); - } else if (name === "header") { - return this.replaceWith("table-header", { cellId, value }); - } else if (name === "table-header" && value) { - this.wrapTableCell(this.parent); - return this.replaceWith(name, value); - } else if (name === "list" || (name === "table-list" && value)) { - const formats = this.getCellFormats(this.parent); - this.wrap(ListContainer.blotName, { ...formats, cellId }); - return this.replaceWith("table-list", value); - } else { - super.format(name, value); - } - } - - formats() { - const formats = this.attributes.values(); - const format = this.domNode.getAttribute("data-cell"); - if (format != null) { - formats[this.statics.blotName] = format; - } - return formats; - } - - getCellFormats(parent: TableCell) { - const cellBlot = getCorrectCellBlot(parent); - if (!cellBlot) return {}; - const [formats] = getCellFormats(cellBlot); - return formats; - } - - wrapTableCell(parent: TableCell) { - const cellBlot = getCorrectCellBlot(parent); - if (!cellBlot) return; - const [formats] = getCellFormats(cellBlot); - this.wrap(TableCell.blotName, formats); - } -} - -class TableCell extends Container { - static blotName = "table-cell"; - static tagName = "TD"; - - children: LinkedList; - next: this | null; - parent: TableRow; - prev: this | null; - - checkMerge() { - if (super.checkMerge() && this.next.children.head != null && this.next.children.head.formats) { - const thisHead = this.children.head.formats()[this.children.head.statics.blotName]; - const thisTail = this.children.tail.formats()[this.children.tail.statics.blotName]; - const nextHead = this.next.children.head.formats()[this.next.children.head.statics.blotName]; - const nextTail = this.next.children.tail.formats()[this.next.children.tail.statics.blotName]; - const _thisHead = getCellId(thisHead); - const _thisTail = getCellId(thisTail); - const _nextHead = getCellId(nextHead); - const _nextTail = getCellId(nextTail); - return _thisHead === _thisTail && _thisHead === _nextHead && _thisHead === _nextTail; - } - return false; - } - - static create(value: Props) { - const node = super.create() as HTMLElement; - const keys = Object.keys(value); - for (const key of keys) { - value[key] && node.setAttribute(key, value[key]); - } - return node; - } - - static formats(domNode: Element) { - const rowspan = this.getEmptyRowspan(domNode); - const formats = CELL_ATTRIBUTE.reduce((formats: Props, attr) => { - if (domNode.hasAttribute(attr)) { - if (attr === "rowspan" && rowspan) { - formats[attr] = `${~~domNode.getAttribute(attr) - rowspan}`; - } else { - formats[attr] = filterWordStyle(domNode.getAttribute(attr)); - } - } - return formats; - }, {}); - if (this.hasColgroup(domNode)) { - delete formats["width"]; - if (formats["style"]) { - formats["style"] = formats["style"].replace(/width.*?;/g, ""); - } - } - return formats; - } - - formats(): { [key: string]: Props } { - const formats: Props = this.statics.formats(this.domNode, this.scroll); - const blotName: string = this.statics.blotName; - return { [blotName]: formats }; - } - - static getEmptyRowspan(domNode: Element) { - let nextNode = domNode.parentElement.nextElementSibling; - let rowspan = 0; - while (nextNode && nextNode.tagName === "TR" && !nextNode.innerHTML.replace(/\s/g, "")) { - rowspan++; - nextNode = nextNode.nextElementSibling; - } - return rowspan; - } - - static hasColgroup(domNode: Element) { - while (domNode && domNode.tagName !== "TBODY") { - domNode = domNode.parentElement; - } - while (domNode) { - if (domNode.tagName === "COLGROUP") { - return true; - } - domNode = domNode.previousElementSibling; - } - return false; - } - - html() { - const reg = /<(ol)[^>]*>]* data-list="bullet">(?:.*?)<\/li><\/(ol)>/gi; - return this.domNode.outerHTML.replace(reg, (match: string, $1: string, $2: string) => { - return match.replace($1, "ul").replace($2, "ul"); - }); - } - - row() { - return this.parent; - } - - rowOffset() { - if (this.row()) { - return this.row().rowOffset(); - } - return -1; - } - - setChildrenId(cellId: string) { - this.children.forEach((child: TableCellBlock | ListContainer | TableHeader) => { - child.domNode.setAttribute("data-cell", cellId); - }); - } - - table(): TableContainer { - let cur = this.parent; - while (cur != null && cur.statics.blotName !== "table-container") { - // @ts-expect-error - cur = cur.parent; - } - // @ts-expect-error - return cur; - } - - optimize(context?: unknown) { - super.optimize(context); - - this.children.forEach((child: TableCellAllowedChildren) => { - if (child.next == null) return; - const childFormats = getCellId(child.formats()[child.statics.blotName]); - const nextFormats = getCellId(child.next.formats()[child.next.statics.blotName]); - if (childFormats !== nextFormats) { - const next = this.splitAfter(child); - // @ts-expect-error - if (next) next.optimize(); - // We might be able to merge with prev now - if (this.prev) this.prev.optimize(); - } - }); - } -} - -class TableRow extends Container { - static blotName = "table-row"; - static tagName = "TR"; - - children: LinkedList; - next: this | null; - parent: TableBody; - prev: this | null; - - checkMerge() { - if (super.checkMerge() && this.next.children.head != null && this.next.children.head.formats) { - const thisHead = this.children.head.formats()[this.children.head.statics.blotName]; - const thisTail = this.children.tail.formats()[this.children.tail.statics.blotName]; - const nextHead = this.next.children.head.formats()[this.next.children.head.statics.blotName]; - const nextTail = this.next.children.tail.formats()[this.next.children.tail.statics.blotName]; - return ( - thisHead["data-row"] === thisTail["data-row"] && - thisHead["data-row"] === nextHead["data-row"] && - thisHead["data-row"] === nextTail["data-row"] - ); - } - return false; - } - - rowOffset() { - if (this.parent) { - return this.parent.children.indexOf(this); - } - return -1; - } -} - -class TableBody extends Container { - static blotName = "table-body"; - static tagName = "TBODY"; - - children: LinkedList; - next: this | null; - parent: TableContainer; -} - -class TableTemporary extends Block { - static blotName = "table-temporary"; - static className = "ql-table-temporary"; - static tagName = "temporary"; - - next: this | null; - - static create(value: Props) { - const node = super.create(); - const keys = Object.keys(value); - const className = TableContainer.defaultClassName; - for (const key of keys) { - if (key === "data-class" && !~value[key].indexOf(className)) { - node.setAttribute(key, `${className} ${value[key]}`); - } else { - node.setAttribute(key, value[key]); - } - } - return node; - } - - static formats(domNode: Element) { - return TABLE_ATTRIBUTE.reduce((formats: Props, attr) => { - if (domNode.hasAttribute(attr)) { - formats[attr] = domNode.getAttribute(attr); - } - return formats; - }, {}); - } - - formats(): { [key: string]: Props } { - const formats: Props = this.statics.formats(this.domNode, this.scroll); - const blotName: string = this.statics.blotName; - return { [blotName]: formats }; - } - - optimize(...args: unknown[]) { - if (this.statics.requiredContainer && this.parent instanceof this.statics.requiredContainer) { - const formats = this.formats()[this.statics.blotName]; - for (const key of TABLE_ATTRIBUTE) { - if (formats[key]) { - if (key === "data-class") { - this.parent.domNode.setAttribute("class", formats[key]); - } else { - this.parent.domNode.setAttribute(key, formats[key]); - } - } else { - this.parent.domNode.removeAttribute(key); - } - } - } - super.optimize(...args); - } -} - -class TableCol extends Block { - static blotName = "table-col"; - static tagName = "COL"; - - next: this | null; - - static create(value: Props) { - const node = super.create(); - const keys = Object.keys(value); - for (const key of keys) { - node.setAttribute(key, value[key]); - } - return node; - } - - static formats(domNode: Element) { - return COL_ATTRIBUTE.reduce((formats: Props, attr) => { - if (domNode.hasAttribute(attr)) { - formats[attr] = domNode.getAttribute(attr); - } - return formats; - }, {}); - } - - formats() { - const formats = this.statics.formats(this.domNode, this.scroll); - return { [this.statics.blotName]: formats }; - } - - html() { - return this.domNode.outerHTML; - } -} - -class TableColgroup extends Container { - static blotName = "table-colgroup"; - static tagName = "COLGROUP"; - - children: LinkedList; - next: this | null; -} - -class TableContainer extends Container { - static blotName = "table-container"; - static defaultClassName = "ql-table-better"; - static tagName = "TABLE"; - - children: LinkedList; - - colgroup() { - // @ts-expect-error - const [colgroup] = this.descendant(TableColgroup); - return colgroup || (this.findChild("table-colgroup") as TableColgroup); - } - - deleteColumn(changeTds: [Element, number][], delTds: Element[], deleteTable: () => void, cols: Element[] = []) { - const body = this.tbody(); - const tableCells = this.descendants(TableCell); - if (body == null || body.children.head == null) return; - if (delTds.length === tableCells.length) { - deleteTable(); - } else { - for (const [td, offset] of changeTds) { - this.setCellColspan(Quill.find(td) as TableCell, offset); - } - for (const td of [...delTds, ...cols]) { - if (td.parentElement.children.length === 1) { - this.setCellRowspan(td.parentElement.previousElementSibling); - } - td.remove(); - } - } - } - - deleteRow(rows: TableRow[], deleteTable: () => void) { - const body = this.tbody(); - if (body == null || body.children.head == null) return; - if (rows.length === body.children.length) { - deleteTable(); - } else { - const weakMap: WeakMap = new WeakMap(); - const columnCells: [TableRow, Props, TableCell | null, TableCell | null][] = []; - const keys: TableCell[] = []; - const maxColumns = this.getMaxColumns(body.children.head.children); - for (const row of rows) { - const prev = this.getCorrectRow(row, maxColumns); - prev && - prev.children.forEach((child: TableCell) => { - const rowspan = ~~child.domNode.getAttribute("rowspan") || 1; - if (rowspan > 1) { - const blotName = child.statics.blotName; - const [formats] = getCellFormats(child); - if (rows.includes(child.parent)) { - const next = child.parent?.next; - if (weakMap.has(child)) { - const { rowspan } = weakMap.get(child); - weakMap.set(child, { next, rowspan: rowspan - 1 }); - } else { - weakMap.set(child, { next, rowspan: rowspan - 1 }); - keys.push(child); - } - } else { - child.replaceWith(blotName, { ...formats, rowspan: rowspan - 1 }); - } - } - }); - } - for (const prev of keys) { - const [formats] = getCellFormats(prev); - const { right: position, width } = prev.domNode.getBoundingClientRect(); - const { next, rowspan } = weakMap.get(prev); - this.setColumnCells(next, columnCells, { position, width }, formats, rowspan, prev); - } - for (const [row, formats, ref, prev] of columnCells) { - const cell = this.scroll.create(TableCell.blotName, formats) as TableCell; - prev.moveChildren(cell); - const _cellId = cellId(); - cell.setChildrenId(_cellId); - row.insertBefore(cell, ref); - prev.remove(); - } - for (const row of rows) { - row.remove(); - } - } - } - - deleteTable() { - this.remove(); - } - - findChild(blotName: string) { - let child = this.children.head; - while (child) { - if (child.statics.blotName === blotName) { - return child; - } - child = child.next; - } - return null; - } - - getCopyTable() { - return this.domNode.outerHTML - .replace(/]*>(.*?)<\/temporary>/gi, "") - .replace(/]*>(.*?)<\/td>/gi, ($1: string) => { - return getCopyTd($1); - }); - } - - getCorrectRow(prev: TableRow, maxColumns: number) { - let isCorrect = false; - while (prev && !isCorrect) { - const prevMaxColumns = this.getMaxColumns(prev.children); - if (maxColumns === prevMaxColumns) { - isCorrect = true; - return prev; - } - prev = prev.prev; - } - return prev; - } - - getInsertRow(prev: TableRow, ref: TableRow | null, offset: number) { - const body = this.tbody(); - if (body == null || body.children.head == null) return; - const id = tableId(); - const row = this.scroll.create(TableRow.blotName) as TableRow; - const maxColumns = this.getMaxColumns(body.children.head.children); - const nextMaxColumns = this.getMaxColumns(prev.children); - if (nextMaxColumns === maxColumns) { - prev.children.forEach((child: TableCell) => { - const formats = { height: "24", "data-row": id }; - const colspan = ~~child.domNode.getAttribute("colspan") || 1; - this.insertTableCell(colspan, formats, row); - }); - return row; - } else { - const correctRow = this.getCorrectRow(prev.prev, maxColumns); - correctRow.children.forEach((child: TableCell) => { - const formats = { height: "24", "data-row": id }; - const colspan = ~~child.domNode.getAttribute("colspan") || 1; - const rowspan = ~~child.domNode.getAttribute("rowspan") || 1; - if (rowspan > 1) { - if (offset > 0 && !ref) { - this.insertTableCell(colspan, formats, row); - } else { - const [formats] = getCellFormats(child); - child.replaceWith(child.statics.blotName, { - ...formats, - rowspan: rowspan + 1 - }); - } - } else { - this.insertTableCell(colspan, formats, row); - } - }); - return row; - } - } - - getMaxColumns(children: LinkedList) { - return children.reduce((num: number, child: TableCell) => { - const colspan = ~~child.domNode.getAttribute("colspan") || 1; - return (num += colspan); - }, 0); - } - - insertColumn(position: number, isLast: boolean, w: number, offset: number) { - const colgroup = this.colgroup() as TableColgroup; - const body = this.tbody() as TableBody; - if (body == null || body.children.head == null) return; - const columnCells: [TableRow, string, TableCell | null, null][] = []; - const cols: [TableColgroup, TableCol | null][] = []; - let row = body.children.head; - while (row) { - if (isLast && offset > 0) { - const id = row.children.tail.domNode.getAttribute("data-row"); - columnCells.push([row, id, null, null]); - } else { - this.setColumnCells(row, columnCells, { position, width: w }); - } - row = row.next; - } - if (colgroup) { - if (isLast) { - cols.push([colgroup, null]); - } else { - let correctLeft = 0; - let correctRight = 0; - let col = colgroup.children.head; - while (col) { - const { left, width } = col.domNode.getBoundingClientRect(); - correctLeft = correctLeft ? correctLeft : left; - correctRight = correctLeft + width; - if (Math.abs(correctLeft - position) <= DEVIATION) { - cols.push([colgroup, col]); - break; - } else if (Math.abs(correctRight - position) <= DEVIATION && !col.next) { - cols.push([colgroup, null]); - break; - } - correctLeft += width; - col = col.next; - } - } - } - for (const [row, id, ref] of columnCells) { - if (!row) { - this.setCellColspan(ref, 1); - } else { - this.insertColumnCell(row, id, ref); - } - } - for (const [colgroup, ref] of cols) { - this.insertCol(colgroup, ref); - } - } - - insertCol(colgroup: TableColgroup, ref: TableCol | null) { - const col = this.scroll.create(TableCol.blotName, { width: `${CELL_DEFAULT_WIDTH}` }); - colgroup.insertBefore(col, ref); - } - - insertColumnCell(row: TableRow | null, id: string, ref: TableCell | null) { - const colgroup = this.colgroup(); - const formats = colgroup ? { "data-row": id } : { "data-row": id, width: `${CELL_DEFAULT_WIDTH}` }; - const cell = this.scroll.create(TableCell.blotName, formats) as TableCell; - const cellBlock = this.scroll.create(TableCellBlock.blotName, cellId()) as TableCellBlock; - cell.appendChild(cellBlock); - if (!row) { - const tbody = this.tbody(); - row = this.scroll.create(TableRow.blotName) as TableRow; - tbody.insertBefore(row, null); - } - row.insertBefore(cell, ref); - cellBlock.optimize(); - return cell; - } - - insertRow(index: number, offset: number) { - const body = this.tbody() as TableBody; - if (body == null || body.children.head == null) return; - const ref = body.children.at(index); - const prev = ref ? ref : body.children.at(index - 1); - const correctRow = this.getInsertRow(prev, ref, offset); - body.insertBefore(correctRow, ref); - } - - insertTableCell(colspan: number, formats: Props, row: TableRow) { - if (colspan > 1) { - Object.assign(formats, { colspan }); - } else { - delete formats["colspan"]; - } - const cell = this.scroll.create(TableCell.blotName, formats) as TableCell; - const cellBlock = this.scroll.create(TableCellBlock.blotName, cellId()) as TableCellBlock; - cell.appendChild(cellBlock); - row.appendChild(cell); - cellBlock.optimize(); - } - - optimize(context: unknown) { - super.optimize(context); - const temporaries = this.descendants(TableTemporary); - this.setClassName(temporaries); - if (temporaries.length > 1) { - temporaries.shift(); - for (const temporary of temporaries) { - temporary.remove(); - } - } - } - - setCellColspan(cell: TableCell, offset: number) { - const blotName = cell.statics.blotName; - const formats = cell.formats()[blotName]; - const colspan = (~~formats["colspan"] || 1) + offset; - if (colspan > 1) { - Object.assign(formats, { colspan }); - } else { - delete formats["colspan"]; - } - cell.replaceWith(blotName, formats); - } - - setCellRowspan(parentElement: Element) { - while (parentElement) { - const children = parentElement.querySelectorAll("td[rowspan]"); - if (children.length) { - for (const child of children) { - const cell = Quill.find(child) as TableCell; - const blotName = cell.statics.blotName; - const formats = cell.formats()[blotName]; - const rowspan = (~~formats["rowspan"] || 1) - 1; - const childBlot = getCellChildBlot(cell); - if (rowspan > 1) { - Object.assign(formats, { rowspan }); - } else { - delete formats["rowspan"]; - } - childBlot.format(blotName, formats); - } - break; - } - parentElement = parentElement.previousElementSibling; - } - } - - private setClassName(temporaries: TableTemporary[]) { - const defaultClassName = this.statics.defaultClassName; - const _temporary = temporaries[0]; - const _className = this.domNode.getAttribute("class"); - const getClassName = (className: string) => { - const classNames = (className || "").split(/\s+/); - if (!classNames.find(className => className === defaultClassName)) { - classNames.unshift(defaultClassName); - } - return classNames.join(" ").trim(); - }; - const setClass = (temporary: TableTemporary, _className: string) => { - const className = temporary.domNode.getAttribute("data-class"); - if (className !== _className && _className != null) { - temporary.domNode.setAttribute("data-class", getClassName(_className)); - } - if (!_className && !className) { - temporary.domNode.setAttribute("data-class", defaultClassName); - } - }; - if (!_temporary) { - const container = this.prev; - if (!container) return; - // @ts-expect-error - const [cell] = container.descendant(TableCell); - // @ts-expect-error - const [temporary] = container.descendant(TableTemporary); - if (!cell && temporary) { - setClass(temporary, _className); - } - } else { - setClass(_temporary, _className); - } - } - - private setColumnCells( - row: TableRow, - columnCells: [TableRow, Props | string, TableCell, TableCell][], - bounds: { position: number; width: number }, - formats?: Props, - rowspan?: number, - prev?: TableCell - ) { - if (!row) return; - const { position, width } = bounds; - let ref = row.children.head; - while (ref) { - const { left, right } = ref.domNode.getBoundingClientRect(); - const id: string = ref.domNode.getAttribute("data-row"); - if (typeof formats === "object") { - Object.assign(formats, { rowspan, "data-row": id }); - } - const props = formats || id; - if (Math.abs(left - position) <= DEVIATION) { - columnCells.push([row, props, ref, prev]); - break; - } else if (Math.abs(right - position) <= DEVIATION && !ref.next) { - columnCells.push([row, props, null, prev]); - break; - // rowspan > 1 (insertLeft, position + w is left) - } else if (Math.abs(left - position - width) <= DEVIATION) { - columnCells.push([row, props, ref, prev]); - break; - // rowspan > 1 (position between left and right, rowspan++) - } else if (position > left && position < right) { - columnCells.push([null, props, ref, prev]); - break; - } - ref = ref.next; - } - } - - tbody() { - // @ts-expect-error - const [body] = this.descendant(TableBody); - return body || (this.findChild("table-body") as TableBody); - } - - temporary() { - // @ts-expect-error - const [temporary] = this.descendant(TableTemporary); - return temporary; - } -} - -TableContainer.allowedChildren = [TableBody, TableTemporary, TableColgroup]; -TableBody.requiredContainer = TableContainer; -TableTemporary.requiredContainer = TableContainer; -TableColgroup.requiredContainer = TableContainer; - -TableBody.allowedChildren = [TableRow]; -TableRow.requiredContainer = TableBody; - -TableColgroup.allowedChildren = [TableCol]; -TableCol.requiredContainer = TableColgroup; - -TableRow.allowedChildren = [TableCell]; -TableCell.requiredContainer = TableRow; - -TableCell.allowedChildren = [TableCellBlock, TableHeader, ListContainer]; -TableCellBlock.requiredContainer = TableCell; -TableHeader.requiredContainer = TableCell; -ListContainer.requiredContainer = TableCell; - -function cellId() { - const id = Math.random().toString(36).slice(2, 6); - return `cell-${id}`; -} - -function tableId() { - const id = Math.random().toString(36).slice(2, 6); - return `row-${id}`; -} - -export { - cellId, - TableBody, - TableCell, - TableCellBlock, - TableCol, - TableColgroup, - TableContainer, - tableId, - TableRow, - TableTemporary -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/de_DE.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/de_DE.ts deleted file mode 100644 index 7335b4856a..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/de_DE.ts +++ /dev/null @@ -1,60 +0,0 @@ -export default { - col: "Spalte", - insColL: "Spalte links einfügen", - insColR: "Spalte rechts einfügen", - delCol: "Spalte löschen", - row: "Zeile", - insRowAbv: "Zeile oberhalb einfügen", - insRowBlw: "Zeile unterhalb einfügen", - delRow: "Zeile löschen", - mCells: "Zellen verbinden", - sCell: "Zelle teilen", - tblProps: "Tabelleneingenschaften", - cellProps: "Zelleneigenschaften", - insParaOTbl: "Absatz außerhalb der Tabelle einfügen", - insB4: "Davor einfügen", - insAft: "Danach einfügen", - copyTable: "Tabelle kopieren", - delTable: "Tabelle löschen", - showGrid: "Raster anzeigen", - border: "Rahmen", - color: "Farbe", - width: "Breite", - background: "Schattierung", - dims: "Maße", - height: "Höhe", - padding: "Abstand", - tblCellTxtAlm: "Ausrichtung", - alCellTxtL: "Zellentext links ausrichten", - alCellTxtC: "Zellentext mittig ausrichten", - alCellTxtR: "Zellentext rechts ausrichten", - jusfCellTxt: "Zellentext Blocksatz", - alCellTxtT: "Zellentext oben ausrichten", - alCellTxtM: "Zellentext mittig ausrichten", - alCellTxtB: "Zellentext unten ausrichten", - dimsAlm: "Maße und Ausrichtung", - alTblL: "Tabelle links ausrichten", - tblC: "Tabelle mittig ausrichten", - alTblR: "Tabelle rechts ausrichten", - save: "Speichern", - cancel: "Abbrechen", - colorMsg: 'Die Farbe ist ungültig. Probiere "#FF0000", "rgb(255,0,0)" oder "red".', - dimsMsg: 'Der Wert ist ungültig. Probiere "10px", "2em" oder einfach "2".', - colorPicker: "Farbwähler", - removeColor: "Farbe entfernen", - black: "Schwarz", - dimGrey: "Dunkelgrau", - grey: "Grau", - lightGrey: "Hellgrau", - white: "Weiß", - red: "Rot", - orange: "Orange", - yellow: "Gelb", - lightGreen: "Hellgrün", - green: "Grün", - aquamarine: "Aquamarin", - turquoise: "Türkis", - lightBlue: "Hellblau", - blue: "Blau", - purple: "Lila" -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/en_US.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/en_US.ts deleted file mode 100644 index 5d14d014c1..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/en_US.ts +++ /dev/null @@ -1,60 +0,0 @@ -export default { - col: "Column", - insColL: "Insert column left", - insColR: "Insert column right", - delCol: "Delete column", - row: "Row", - insRowAbv: "Insert row above", - insRowBlw: "Insert row below", - delRow: "Delete row", - mCells: "Merge cells", - sCell: "Split cell", - tblProps: "Table properties", - cellProps: "Cell properties", - insParaOTbl: "Insert paragraph outside the table", - insB4: "Insert before", - insAft: "Insert after", - copyTable: "Copy table", - delTable: "Delete table", - showGrid: "Show grid", - border: "Border", - color: "Color", - width: "Width", - background: "Background", - dims: "Dimensions", - height: "Height", - padding: "Padding", - tblCellTxtAlm: "Table cell text alignment", - alCellTxtL: "Align cell text to the left", - alCellTxtC: "Align cell text to the center", - alCellTxtR: "Align cell text to the right", - jusfCellTxt: "Justify cell text", - alCellTxtT: "Align cell text to the top", - alCellTxtM: "Align cell text to the middle", - alCellTxtB: "Align cell text to the bottom", - dimsAlm: "Dimensions and alignment", - alTblL: "Align table to the left", - tblC: "Center table", - alTblR: "Align table to the right", - save: "Save", - cancel: "Cancel", - colorMsg: 'The color is invalid. Try "#FF0000" or "rgb(255,0,0)" or "red".', - dimsMsg: 'The value is invalid. Try "10px" or "2em" or simply "2".', - colorPicker: "Color picker", - removeColor: "Remove color", - black: "Black", - dimGrey: "Dim grey", - grey: "Grey", - lightGrey: "Light grey", - white: "White", - red: "Red", - orange: "Orange", - yellow: "Yellow", - lightGreen: "Light green", - green: "Green", - aquamarine: "Aquamarine", - turquoise: "Turquoise", - lightBlue: "Light blue", - blue: "Blue", - purple: "Purple" -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/fr_FR.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/fr_FR.ts deleted file mode 100644 index f9dfa5738f..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/fr_FR.ts +++ /dev/null @@ -1,60 +0,0 @@ -export default { - col: "Colonne", - insColL: "Insérer colonne à gauche", - insColR: "Insérer colonne à droite", - delCol: "Supprimer la colonne", - row: "Ligne", - insRowAbv: "Insérer ligne au-dessus", - insRowBlw: "Insérer ligne en dessous", - delRow: "Supprimer la ligne", - mCells: "Fusionner les cellules", - sCell: "Diviser la cellule", - tblProps: "Propriétés du tableau", - cellProps: "Propriétés de la cellule", - insParaOTbl: "Insérer paragraphe en dehors du tableau", - insB4: "Insérer avant", - insAft: "Insérer après", - copyTable: "Copier le tableau", - delTable: "Supprimer le tableau", - showGrid: "Afficher la grille", - border: "Bordure", - color: "Couleur", - width: "Largeur", - background: "Arrière-plan", - dims: "Dimensions", - height: "Hauteur", - padding: "Marge intérieure", - tblCellTxtAlm: "Alignement du texte de la cellule du tableau", - alCellTxtL: "Aligner le texte de la cellule à gauche", - alCellTxtC: "Aligner le texte de la cellule au centre", - alCellTxtR: "Aligner le texte de la cellule à droite", - jusfCellTxt: "Justifier le texte de la cellule", - alCellTxtT: "Aligner le texte de la cellule en haut", - alCellTxtM: "Aligner le texte de la cellule au milieu", - alCellTxtB: "Aligner le texte de la cellule en bas", - dimsAlm: "Dimensions et alignement", - alTblL: "Aligner le tableau à gauche", - tblC: "Centrer le tableau", - alTblR: "Aligner le tableau à droite", - save: "Enregistrer", - cancel: "Annuler", - colorMsg: 'La couleur est invalide. Essayez "#FF0000" ou "rgb(255,0,0)" ou "rouge".', - dimsMsg: 'La valeur est invalide. Essayez "10px" ou "2em" ou simplement "2".', - colorPicker: "Sélecteur de couleur", - removeColor: "Supprimer la couleur", - black: "Noir", - dimGrey: "Gris foncé", - grey: "Gris", - lightGrey: "Gris clair", - white: "Blanc", - red: "Rouge", - orange: "Orange", - yellow: "Jaune", - lightGreen: "Vert clair", - green: "Vert", - aquamarine: "Aigue-marine", - turquoise: "Turquoise", - lightBlue: "Bleu clair", - blue: "Bleu", - purple: "Violet" -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/index.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/index.ts deleted file mode 100644 index 9b0aa6b702..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -// @ts-nocheck -import type { Props } from "../types"; -import de_DE from "./de_DE"; -import en_US from "./en_US"; -import fr_FR from "./fr_FR"; -import pl_PL from "./pl_PL"; -import ru_RU from "./ru_RU"; -import tr_TR from "./tr_TR"; -import zh_CN from "./zh_CN"; - -interface Config { - [propName: string]: Props; -} - -interface LanguageConfig { - name: string; - content: Props; -} - -class Language { - config: Config; - language: string | LanguageConfig; - name: string; - constructor(language?: string | LanguageConfig) { - this.config = { - en_US, - zh_CN, - fr_FR, - pl_PL, - de_DE, - ru_RU, - tr_TR - }; - this.init(language); - } - - changeLanguage(name: string) { - this.name = name; - } - - init(language: string | LanguageConfig) { - if (typeof language === "undefined" || typeof language === "string") { - this.changeLanguage(language || "en_US"); - } else { - const { name, content } = language; - content && this.registry(name, content); - name && this.changeLanguage(name); - } - } - - registry(name: string, content: Props) { - this.config = { - ...this.config, - [name]: content - }; - } - - useLanguage(name: string) { - return this.config[this.name][name]; - } -} - -export default Language; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/pl_PL.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/pl_PL.ts deleted file mode 100644 index 2e986e5410..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/pl_PL.ts +++ /dev/null @@ -1,60 +0,0 @@ -export default { - col: "Kolumna", - insColL: "Wstaw kolumnę z lewej", - insColR: "Wstaw kolumnę z prawej", - delCol: "Usuń kolumnę", - row: "Wiersz", - insRowAbv: "Wstaw wiersz powyżej", - insRowBlw: "Wstaw wiersz poniżej", - delRow: "Usuń wiersz", - mCells: "Scal komórki", - sCell: "Podziel komórkę", - tblProps: "Właściwości tabeli", - cellProps: "Właściwości komórki", - insParaOTbl: "Wstaw akapit poza tabelą", - insB4: "Wstaw przed", - insAft: "Wstaw po", - copyTable: "Kopiuj tabelę", - delTable: "Usuń tabelę", - showGrid: "Pokaż siatkę", - border: "Obramowanie", - color: "Kolor", - width: "Szerokość", - background: "Tło", - dims: "Wymiary", - height: "Wysokość", - padding: "Margines wewnętrzny", - tblCellTxtAlm: "Wyrównanie tekstu w komórce tabeli", - alCellTxtL: "Wyrównaj tekst w komórce do lewej", - alCellTxtC: "Wyrównaj tekst w komórce do środka", - alCellTxtR: "Wyrównaj tekst w komórce do prawej", - jusfCellTxt: "Wyjustuj tekst w komórce", - alCellTxtT: "Wyrównaj tekst w komórce do góry", - alCellTxtM: "Wyrównaj tekst w komórce do środka", - alCellTxtB: "Wyrównaj tekst w komórce do dołu", - dimsAlm: "Wymiary i wyrównanie", - alTblL: "Wyrównaj tabelę do lewej", - tblC: "Wyśrodkuj tabelę", - alTblR: "Wyrównaj tabelę do prawej", - save: "Zapisz", - cancel: "Anuluj", - colorMsg: 'Kolor jest nieprawidłowy. Spróbuj "#FF0000" lub "rgb(255,0,0)" lub "red".', - dimsMsg: 'Wartość jest nieprawidłowa. Spróbuj "10px" lub "2em" lub po prostu "2".', - colorPicker: "Wybór koloru", - removeColor: "Usuń kolor", - black: "Czarny", - dimGrey: "Przyciemniony szary", - grey: "Szary", - lightGrey: "Jasnoszary", - white: "Biały", - red: "Czerwony", - orange: "Pomarańczowy", - yellow: "Żółty", - lightGreen: "Jasnozielony", - green: "Zielony", - aquamarine: "Akwamaryna", - turquoise: "Turkusowy", - lightBlue: "Jasnoniebieski", - blue: "Niebieski", - purple: "Fioletowy" -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/ru_RU.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/ru_RU.ts deleted file mode 100644 index 9268f10b63..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/ru_RU.ts +++ /dev/null @@ -1,60 +0,0 @@ -export default { - col: "Столбец", - insColL: "Вставить столбец слева", - insColR: "Вставить столбец справа", - delCol: "Удалить столбец", - row: "Строка", - insRowAbv: "Вставить строку сверху", - insRowBlw: "Вставить строку снизу", - delRow: "Удалить строку", - mCells: "Объединить ячейки", - sCell: "Разбить ячейку", - tblProps: "Свойства таблицы", - cellProps: "Свойства ячейки", - insParaOTbl: "Вставить абзац за пределами таблицы", - insB4: "Вставить абзац перед", - insAft: "Вставить абзац после", - copyTable: "Копировать таблицу", - delTable: "Удалить таблицу", - showGrid: "Показать сетку", - border: "Обводка", - color: "Цвет", - width: "Ширина", - background: "Фон", - dims: "Размеры", - height: "Высота", - padding: "Отступ", - tblCellTxtAlm: "Выравнивание текста в ячейке таблицы", - alCellTxtL: "Выровнять текст в ячейке по левому краю", - alCellTxtC: "Выровнять текст в ячейке по центру", - alCellTxtR: "Выровнять текст в ячейке по правому краю", - jusfCellTxt: "Выровнять текст в ячейке по ширине", - alCellTxtT: "Выровнять текст в ячейке по верху", - alCellTxtM: "Выровнять текст в ячейке по середине", - alCellTxtB: "Выровнять текст в ячейке по низу", - dimsAlm: "Размеры и выравнивание", - alTblL: "Выровнять таблицу по левому краю", - tblC: "Центрировать таблицу", - alTblR: "Выровнять таблицу по правому краю", - save: "Сохранить", - cancel: "Отменить", - colorMsg: 'Неверный цвет. Попробуйте "#FF0000", "rgb(255,0,0)" или "red".', - dimsMsg: 'Недопустимое значение. Попробуйте "10px", "2em" или просто "2".', - colorPicker: "Выбор цвета", - removeColor: "Удалить цвет", - black: "Черный", - dimGrey: "Темно-серый", - grey: "Серый", - lightGrey: "Светло-серый", - white: "Белый", - red: "Красный", - orange: "Оранжевый", - yellow: "Желтый", - lightGreen: "Светло-зеленый", - green: "Зеленый", - aquamarine: "Аквамарин", - turquoise: "Бирюзовый", - lightBlue: "Светло-голубой", - blue: "Синий", - purple: "Фиолетовый" -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/tr_TR.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/tr_TR.ts deleted file mode 100644 index c3d64fa797..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/tr_TR.ts +++ /dev/null @@ -1,60 +0,0 @@ -export default { - col: "Sütun", - insColL: "Sola sütun ekle", - insColR: "Sağa sütun ekle", - delCol: "Sütunu sil", - row: "Satır", - insRowAbv: "Üstüne satır ekle", - insRowBlw: "Altına satır ekle", - delRow: "Satırı sil", - mCells: "Hücreleri birleştir", - sCell: "Hücreyi böl", - tblProps: "Tablo özellikleri", - cellProps: "Hücre özellikleri", - insParaOTbl: "Tablo dışında paragraf ekle", - insB4: "Öncesine ekle", - insAft: "Sonrasına ekle", - copyTable: "Tabloyu kopyala", - delTable: "Tabloyu sil", - showGrid: "Izgarayı göster", - border: "Kenarlık", - color: "Renk", - width: "Genişlik", - background: "Arka plan", - dims: "Boyutlar", - height: "Yükseklik", - padding: "Dolgu", - tblCellTxtAlm: "Tablo hücresi metin hizalaması", - alCellTxtL: "Hücre metnini sola hizala", - alCellTxtC: "Hücre metnini ortaya hizala", - alCellTxtR: "Hücre metnini sağa hizala", - jusfCellTxt: "Hücre metnini yasla", - alCellTxtT: "Hücre metnini üste hizala", - alCellTxtM: "Hücre metnini ortaya hizala", - alCellTxtB: "Hücre metnini alta hizala", - dimsAlm: "Boyutlar ve hizalama", - alTblL: "Tabloyu sola hizala", - tblC: "Tabloyu ortala", - alTblR: "Tabloyu sağa hizala", - save: "Kaydet", - cancel: "İptal", - colorMsg: 'Renk geçersiz. "#FF0000", "rgb(255,0,0)" veya "red" deneyin.', - dimsMsg: 'Değer geçersiz. "10px", "2em" veya sadece "2" deneyin.', - colorPicker: "Renk seçici", - removeColor: "Rengi kaldır", - black: "Siyah", - dimGrey: "Koyu gri", - grey: "Gri", - lightGrey: "Açık gri", - white: "Beyaz", - red: "Kırmızı", - orange: "Turuncu", - yellow: "Sarı", - lightGreen: "Açık yeşil", - green: "Yeşil", - aquamarine: "Akuamarin", - turquoise: "Turkuaz", - lightBlue: "Açık mavi", - blue: "Mavi", - purple: "Mor" -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/zh_CN.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/zh_CN.ts deleted file mode 100644 index c47d8c89d4..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/language/zh_CN.ts +++ /dev/null @@ -1,60 +0,0 @@ -export default { - col: "列", - insColL: "向左插入列", - insColR: "向右插入列", - delCol: "删除列", - row: "行", - insRowAbv: "在上面插入行", - insRowBlw: "在下面插入行", - delRow: "删除行", - mCells: "合并单元格", - sCell: "拆分单元格", - tblProps: "表格属性", - cellProps: "单元格属性", - insParaOTbl: "在表格外插入段落", - insB4: "在表格前面插入", - insAft: "在表格后面插入", - copyTable: "复制表格", - delTable: "删除表格", - showGrid: "显示网格", - border: "边框", - color: "颜色", - width: "宽度", - background: "背景", - dims: "尺寸", - height: "高度", - padding: "内边距", - tblCellTxtAlm: "单元格文本对齐方式", - alCellTxtL: "左对齐", - alCellTxtC: "水平居中对齐", - alCellTxtR: "右对齐", - jusfCellTxt: "两边对齐", - alCellTxtT: "顶端对齐", - alCellTxtM: "垂直居中对齐", - alCellTxtB: "底部对齐", - dimsAlm: "尺寸和对齐方式", - alTblL: "表格左对齐", - tblC: "表格居中", - alTblR: "表格右对齐", - save: "保存", - cancel: "取消", - colorMsg: '无效颜色,请使用 "#FF0000" 或者 "rgb(255,0,0)" 或者 "red"', - dimsMsg: '无效值, 请使用 "10px" 或者 "2em" 或者 "2"', - colorPicker: "颜色选择器", - removeColor: "删除颜色", - black: "黑色", - dimGrey: "暗灰色", - grey: "灰色", - lightGrey: "浅灰色", - white: "白色", - red: "红色", - orange: "橙色", - yellow: "黄色", - lightGreen: "浅绿色", - green: "绿色", - aquamarine: "海蓝色", - turquoise: "青绿色", - lightBlue: "浅蓝色", - blue: "蓝色", - purple: "紫色" -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/modules/clipboard.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/modules/clipboard.ts deleted file mode 100644 index 23ca90563f..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/modules/clipboard.ts +++ /dev/null @@ -1,58 +0,0 @@ -import Quill from "quill"; -import Module from "quill/core/module"; -import Delta from "quill-delta"; -import logger from "quill/core/logger.js"; -import type { Range, Props } from "../types"; -import { TableCellBlock, TableTemporary } from "../formats/table"; -import CustomClipboard from "../../../modules/clipboard"; - -const Clipboard = CustomClipboard as typeof Module; -const debug = logger("quill:clipboard"); - -class TableClipboard extends Clipboard { - // @ts-ignore - convert: ( - { - html, - text - }: { - html?: string; - text?: string; - }, - formats?: Record - ) => Delta; - - onPaste(range: Range, { text, html }: { text?: string; html?: string }) { - const formats = this.quill.getFormat(range.index) as Props; - const pastedDelta = this.getTableDelta({ text, html }, formats); - debug.log("onPaste", pastedDelta, { text, html }); - const delta = new Delta().retain(range.index).delete(range.length).concat(pastedDelta); - this.quill.updateContents(delta, Quill.sources.USER); - // range.length contributes to delta.length() - this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT); - this.quill.scrollSelectionIntoView(); - } - - private getTableDelta({ html, text }: { html?: string; text?: string }, formats: Props) { - const delta = this.convert({ text, html }, formats); - if (formats[TableCellBlock.blotName]) { - for (const op of delta.ops) { - // External copied tables or table contents copied within an editor. - // Subsequent version processing. - if ( - op?.attributes && - (op.attributes[TableTemporary.blotName] || op.attributes[TableCellBlock.blotName]) - ) { - return new Delta(); - } - // Process externally pasted lists or headers or text. - if (op?.attributes?.header || op?.attributes?.list || !op?.attributes?.[TableCellBlock.blotName]) { - op.attributes = { ...op.attributes, ...formats }; - } - } - } - return delta; - } -} - -export default TableClipboard; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/modules/toolbar.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/modules/toolbar.ts deleted file mode 100644 index ffb159d953..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/modules/toolbar.ts +++ /dev/null @@ -1,247 +0,0 @@ -// @ts-nocheck -import merge from "lodash.merge"; -import type { ContainerBlot } from "parchment"; -import { EmbedBlot } from "parchment"; -import type { Range } from "quill"; -import Quill from "quill"; -import Delta from "quill-delta"; -import QuillContainer from "quill/blots/container"; -import Module from "quill/core/module"; -import QuillToolbar from "quill/modules/toolbar"; -import TableHeader from "../formats/header"; -import type { CellSelection, QuillTableBetter, TableCell, TableCellAllowedChildren, TableCellChildren } from "../types"; -import { getCorrectCellBlot } from "../utils"; - -const Container = QuillContainer as typeof ContainerBlot; -const Toolbar = QuillToolbar as typeof Module; - -type Handler = (this: TableToolbar, value: any) => void; - -class TableToolbar extends Toolbar { - handlers: Record; - controls: [string, HTMLElement][]; - update: (range: Range | null) => void; - container?: HTMLElement | null; - - attach(input: HTMLElement) { - let format = Array.from(input.classList).find(className => { - return className.indexOf("ql-") === 0; - }); - if (!format) return; - format = format.slice("ql-".length); - if (input.tagName === "BUTTON") { - input.setAttribute("type", "button"); - } - if (this.handlers[format] == null && this.quill.scroll.query(format) == null) { - console.warn("ignoring attaching to nonexistent format", format, input); - return; - } - const eventName = input.tagName === "SELECT" ? "change" : "click"; - input.addEventListener(eventName, e => { - const { cellSelection } = this.getTableBetter(); - if (cellSelection?.selectedTds?.length > 1) { - this.cellSelectionAttach(input, format, e, cellSelection); - } else { - this.toolbarAttach(input, format, e); - } - }); - this.controls.push([format, input]); - } - - private cellSelectionAttach( - input: HTMLElement, - format: string, - e: Event | MouseEvent, - cellSelection: CellSelection - ) { - if (input.tagName === "SELECT") { - // @ts-ignore - if (input.selectedIndex < 0) return; - // @ts-ignore - const selected = input.options[input.selectedIndex]; - const val = typeof selected?.value === "string" ? selected?.value : true; - const value = cellSelection.getCorrectValue(format, val); - cellSelection.setSelectedTdsFormat(format, value); - } else { - // @ts-ignore - const val = input?.value || true; - const value = cellSelection.getCorrectValue(format, val); - cellSelection.setSelectedTdsFormat(format, value); - e.preventDefault(); - } - } - - getTableBetter() { - return this.quill.getModule("table-better") as QuillTableBetter; - } - - setTableFormat(range: Range, selectedTds: Element[], value: string, name: string, lines: TableCellChildren[]) { - let blot = null; - const { cellSelection, tableMenus } = this.getTableBetter(); - const _isReplace = isReplace(range, selectedTds, lines); - for (const line of lines) { - const isReplace = getHeaderReplace(selectedTds, name, line, _isReplace); - blot = line.format(name, value, isReplace) as TableCellChildren; - } - if (selectedTds.length < 2) { - if (_isReplace || lines.length === 1) { - const cell = getCorrectCellBlot(blot); - Promise.resolve().then(() => { - if (cell && this.quill.root.contains(cell.domNode)) { - cellSelection.setSelected(cell.domNode, false); - } else { - cellSelection.setSelected(selectedTds[0], false); - } - }); - } else { - cellSelection.setSelected(selectedTds[0], false); - } - this.quill.setSelection(range, Quill.sources.SILENT); - } - tableMenus.updateMenus(); - return blot; - } - - private toolbarAttach(input: HTMLElement, format: string, e: Event | MouseEvent) { - let value; - if (input.tagName === "SELECT") { - // @ts-expect-error - if (input.selectedIndex < 0) return; - // @ts-expect-error - const selected = input.options[input.selectedIndex]; - if (selected.hasAttribute("selected")) { - value = false; - } else { - value = selected.value || false; - } - } else { - if (input.classList.contains("ql-active")) { - value = false; - } else { - // @ts-expect-error - value = input.value || !input.hasAttribute("value"); - } - e.preventDefault(); - } - this.quill.focus(); - const [range] = this.quill.selection.getRange(); - if (this.handlers[format] != null) { - this.handlers[format].call(this, value); - } else if ( - // @ts-expect-error - this.quill.scroll.query(format).prototype instanceof EmbedBlot - ) { - value = prompt(`Enter ${format}`); // eslint-disable-line no-alert - if (!value) return; - this.quill.updateContents( - new Delta() - .retain(range.index) - .delete(range.length) - .insert({ [format]: value }), - Quill.sources.USER - ); - } else { - this.quill.format(format, value, Quill.sources.USER); - } - this.update(range); - } -} - -function containers(blot: TableCell, index = 0, length = Number.MAX_VALUE) { - const getContainers = (blot: TableCell | TableCellAllowedChildren, blotIndex: number, blotLength: number) => { - // @ts-ignore - let containers: Container[] = []; - let lengthLeft = blotLength; - blot.children.forEachAt( - blotIndex, - blotLength, - // @ts-ignore - (child, childIndex, childLength) => { - if (child instanceof Container) { - containers.push(child); - containers = containers.concat(getContainers(child, childIndex, lengthLeft)); - } - lengthLeft -= childLength; - } - ); - return containers; - }; - return getContainers(blot, index, length); -} - -function getHeaderReplace(selectedTds: Element[], name: string, line: TableCellChildren, _isReplace: boolean) { - if (selectedTds.length === 1 && name === "list" && line.statics.blotName === TableHeader.blotName) { - return true; - } - return _isReplace; -} - -function getLength(blots: TableCellChildren[]): number { - return blots.reduce((sum, blot) => { - return (sum += blot.length()); - }, 0); -} - -function isReplace(range: Range, selectedTds: Element[], lines: TableCellChildren[]) { - if (selectedTds.length === 1) { - const cellBlot = Quill.find(selectedTds[0]) as TableCell; - const _containers = containers(cellBlot, range.index, range.length); - const length = getLength(_containers); - const _length = getLength(lines); - return length === _length; - } - return !!(selectedTds.length > 1); -} - -function tablehandler(value: string, selectedTds: Element[], name: string, lines?: TableCellChildren[]) { - const range = this.quill.getSelection(); - if (!lines) { - if (!range.length && selectedTds.length === 1) { - const [line] = this.quill.getLine(range.index); - lines = [line]; - } else { - lines = this.quill.getLines(range); - } - } - return this.setTableFormat(range, selectedTds, value, name, lines); -} - -TableToolbar.DEFAULTS = merge({}, Toolbar.DEFAULTS, { - handlers: { - header(value: string, lines?: TableCellChildren[]) { - const { cellSelection } = this.getTableBetter(); - const selectedTds = cellSelection?.selectedTds; - if (selectedTds?.length) { - return tablehandler.call(this, value, selectedTds, "header", lines); - } - this.quill.format("header", value, Quill.sources.USER); - }, - list(value: string, lines?: TableCellChildren[]) { - const { cellSelection } = this.getTableBetter(); - const selectedTds = cellSelection?.selectedTds; - if (selectedTds?.length) { - if (selectedTds.length === 1) { - const range = this.quill.getSelection(true); - const formats = this.quill.getFormat(range); - value = cellSelection.getListCorrectValue("list", value, formats); - } - return tablehandler.call(this, value, selectedTds, "list", lines); - } - - const range = this.quill.getSelection(true); - const formats = this.quill.getFormat(range); - if (value === "check") { - if (formats.list === "checked" || formats.list === "unchecked") { - this.quill.format("list", false, Quill.sources.USER); - } else { - this.quill.format("list", "unchecked", Quill.sources.USER); - } - } else { - this.quill.format("list", value, Quill.sources.USER); - } - }, - "table-better"() {} - } -}); - -export default TableToolbar; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/quill-table-better.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/quill-table-better.ts deleted file mode 100644 index d84d747c6d..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/quill-table-better.ts +++ /dev/null @@ -1,369 +0,0 @@ -// @ts-nocheck -import Quill from "quill"; -import Delta from "quill-delta"; -import Module from "quill/core/module"; -import type { EmitterSource, Range } from "quill"; -import type { BindingObject, Context, Props } from "./types"; -import { - cellId, - TableCellBlock, - TableCell, - TableRow, - TableBody, - TableTemporary, - TableContainer, - tableId, - TableCol, - TableColgroup -} from "./formats/table"; -import TableHeader from "./formats/header"; -import { ListContainer } from "./formats/list"; -import { matchTable, matchTableCell, matchTableCol, matchTableTemporary } from "./utils/clipboard-matchers"; -import Language from "./language"; -import CellSelection from "./ui/cell-selection"; -import OperateLine from "./ui/operate-line"; -import TableMenus from "./ui/table-menus"; -import { CELL_DEFAULT_WIDTH } from "./config"; -import ToolbarTable, { TableSelect } from "./ui/toolbar-table"; -import { getCellId, getCorrectCellBlot } from "./utils"; -import TableToolbar from "./modules/toolbar"; -import TableClipboard from "./modules/clipboard"; - -interface Options { - language?: - | string - | { - name: string; - content: Props; - }; - menus?: string[]; - toolbarButtons?: { - whiteList?: string[]; - singleWhiteList?: string[]; - }; - toolbarTable?: boolean; -} - -type Line = TableCellBlock | TableHeader | ListContainer; - -class Table extends Module { - language: Language; - cellSelection: CellSelection; - operateLine: OperateLine; - tableMenus: TableMenus; - tableSelect: TableSelect; - options: Options; - - static keyboardBindings: { [propName: string]: BindingObject }; - - static register() { - Quill.register(TableCellBlock, true); - Quill.register(TableCell, true); - Quill.register(TableRow, true); - Quill.register(TableBody, true); - Quill.register(TableTemporary, true); - Quill.register(TableContainer, true); - Quill.register(TableCol, true); - Quill.register(TableColgroup, true); - Quill.register( - { - "modules/toolbar": TableToolbar, - "modules/clipboard": TableClipboard - }, - true - ); - } - - constructor(quill: Quill, options: Options) { - super(quill, options); - quill.clipboard.addMatcher("td, th", matchTableCell); - quill.clipboard.addMatcher("tr", matchTable); - quill.clipboard.addMatcher("col", matchTableCol); - quill.clipboard.addMatcher("table", matchTableTemporary); - this.language = new Language(options?.language); - this.cellSelection = new CellSelection(quill, this); - this.operateLine = new OperateLine(quill, this); - this.tableMenus = new TableMenus(quill, this); - this.tableSelect = new TableSelect(); - if (!this.quill.options.readOnly) { - quill.root.addEventListener("keyup", this.handleKeyup.bind(this)); - quill.root.addEventListener("mousedown", this.handleMousedown.bind(this)); - quill.root.addEventListener("scroll", this.handleScroll.bind(this)); - this.registerToolbarTable(options?.toolbarTable); - } - } - - clearHistorySelected() { - const [table] = this.getTable(); - if (!table) return; - const selectedTds: Element[] = Array.from( - table.domNode.querySelectorAll("td.ql-cell-focused, td.ql-cell-selected") - ); - for (const td of selectedTds) { - td.classList && td.classList.remove("ql-cell-focused", "ql-cell-selected"); - } - } - - deleteTable() { - const [table] = this.getTable(); - if (table == null) return; - const offset = table.offset(); - table.remove(); - this.hideTools(); - this.quill.update(Quill.sources.USER); - this.quill.setSelection(offset, Quill.sources.SILENT); - } - - deleteTableTemporary(source: EmitterSource = Quill.sources.API) { - const temporaries = this.quill.scroll.descendants(TableTemporary); - for (const temporary of temporaries) { - temporary.remove(); - } - this.hideTools(); - this.quill.update(source); - } - - getTable( - range = this.quill.getSelection() - ): [null, null, null, -1] | [TableContainer, TableRow, TableCell, number] { - if (range == null) return [null, null, null, -1]; - const [block, offset] = this.quill.getLine(range.index); - if (block == null || block.statics.blotName !== TableCellBlock.blotName) { - return [null, null, null, -1]; - } - const cell = block.parent as TableCell; - const row = cell.parent as TableRow; - const table = row.parent.parent as TableContainer; - return [table, row, cell, offset]; - } - - handleKeyup(e: KeyboardEvent) { - this.cellSelection.handleKeyup(e); - if (e.ctrlKey && (e.key === "z" || e.key === "y")) { - this.hideTools(); - this.clearHistorySelected(); - } - this.updateMenus(e); - } - - handleMousedown(e: MouseEvent) { - this.tableSelect?.hide(this.tableSelect.root); - const table = (e.target as Element).closest("table.ql-table-better"); - if (!table) return this.hideTools(); - this.cellSelection.handleMousedown(e); - this.cellSelection.setDisabled(true); - } - - handleScroll() { - this.hideTools(); - this.tableMenus?.updateScroll(true); - } - - hideTools() { - this.cellSelection?.clearSelected(); - this.cellSelection?.setDisabled(false); - this.operateLine?.hideDragBlock(); - this.operateLine?.hideDragTable(); - this.operateLine?.hideLine(); - this.tableMenus?.hideMenus(); - this.tableMenus?.destroyTablePropertiesForm(); - } - - insertTable(rows: number, columns: number) { - const range = this.quill.getSelection(true); - if (range == null) return; - if (this.isTable(range)) return; - const style = `width: ${CELL_DEFAULT_WIDTH * columns}px;`; - const formats = this.quill.getFormat(range.index - 1); - const [, offset] = this.quill.getLine(range.index); - const isExtra = !!formats[TableCellBlock.blotName] || offset !== 0; - const _offset = isExtra ? 2 : 1; - const extraDelta = isExtra ? new Delta().insert("\n") : new Delta(); - const base = new Delta() - .retain(range.index) - .delete(range.length) - .concat(extraDelta) - .insert("\n", { [TableTemporary.blotName]: { style } }); - const delta = new Array(rows).fill(0).reduce(memo => { - const id = tableId(); - return new Array(columns).fill("\n").reduce((memo, text) => { - return memo.insert(text, { - [TableCellBlock.blotName]: cellId(), - [TableCell.blotName]: { "data-row": id, width: `${CELL_DEFAULT_WIDTH}` } - }); - }, memo); - }, base); - this.quill.updateContents(delta, Quill.sources.USER); - this.quill.setSelection(range.index + _offset, Quill.sources.SILENT); - this.showTools(); - this.tableMenus.showGrid(true); - } - - // Inserting tables within tables is currently not supported - private isTable(range: Range) { - const formats = this.quill.getFormat(range.index); - return !!formats[TableCellBlock.blotName]; - } - - private registerToolbarTable(toolbarTable: boolean) { - if (!toolbarTable) return; - Quill.register({ "formats/table-better": ToolbarTable }, true); - const toolbar = this.quill.getModule("toolbar") as TableToolbar; - const button = toolbar.container.querySelector("button.ql-table-better"); - if (!button || !this.tableSelect.root) return; - button.appendChild(this.tableSelect.root); - button.addEventListener("click", (e: MouseEvent) => { - this.tableSelect.handleClick(e, this.insertTable.bind(this)); - }); - this.quill.root.addEventListener("click", (e: MouseEvent) => { - const visible = e.composedPath().includes(button); - if (visible) return; - if (!this.tableSelect.root.classList.contains("ql-hidden")) { - this.tableSelect.hide(this.tableSelect.root); - } - }); - } - - showTools(force?: boolean) { - const [table, , cell] = this.getTable(); - if (!table || !cell) return; - this.cellSelection.setDisabled(true); - this.cellSelection.setSelected(cell.domNode, force); - this.tableMenus.showMenus(); - this.tableMenus.updateMenus(table.domNode); - this.tableMenus.updateTable(table.domNode); - } - - private updateMenus(e: KeyboardEvent) { - if (!this.cellSelection.selectedTds.length) return; - if (e.key === "Enter" || (e.ctrlKey && e.key === "v")) { - this.tableMenus.updateMenus(); - } - } -} - -const keyboardBindings = { - "table-cell down": makeTableArrowHandler(false), - "table-cell up": makeTableArrowHandler(true), - "table-cell-block backspace": makeCellBlockHandler("Backspace"), - "table-cell-block delete": makeCellBlockHandler("Delete"), - "table-header backspace": makeTableHeaderHandler("Backspace"), - "table-header delete": makeTableHeaderHandler("Delete"), - "table-header enter": { - key: "Enter", - collapsed: true, - format: ["table-header"], - suffix: /^$/, - handler(range: Range, context: Context) { - const [line, offset] = this.quill.getLine(range.index); - const delta = new Delta() - .retain(range.index) - .insert("\n", context.format) - .retain(line.length() - offset - 1) - .retain(1, { header: null }); - this.quill.updateContents(delta, Quill.sources.USER); - this.quill.setSelection(range.index + 1, Quill.sources.SILENT); - this.quill.scrollSelectionIntoView(); - } - }, - "table-list backspace": makeTableListHandler("Backspace"), - "table-list delete": makeTableListHandler("Delete"), - "table-list empty enter": { - key: "Enter", - collapsed: true, - format: ["table-list"], - empty: true, - handler(_range: Range, context: Context) { - const { line } = context; - const { cellId } = line.parent.formats()[line.parent.statics.blotName]; - const blot = line.replaceWith(TableCellBlock.blotName, cellId) as TableCellBlock; - const tableModule = this.quill.getModule("table-better"); - const cell = getCorrectCellBlot(blot); - cell && tableModule.cellSelection.setSelected(cell.domNode, false); - } - } -}; - -function makeCellBlockHandler(key: string) { - return { - key, - format: ["table-cell-block"], - collapsed: true, - handler(range: Range, context: Context) { - const [line] = this.quill.getLine(range.index); - const { offset, suffix } = context; - if (offset === 0 && !line.prev) return false; - const blotName = line.prev?.statics.blotName; - if ( - offset === 0 && - (blotName === ListContainer.blotName || - blotName === TableCellBlock.blotName || - blotName === TableHeader.blotName) - ) { - return removeLine.call(this, line, range); - } - // Delete isn't from the end - if (offset !== 0 && !suffix && key === "Delete") { - return false; - } - return true; - } - }; -} - -// Prevent table default up and down keyboard events. -// Implemented by the makeTableArrowVerticalHandler function. -function makeTableArrowHandler(up: boolean) { - return { - key: up ? "ArrowUp" : "ArrowDown", - collapsed: true, - format: ["table-cell"], - handler() { - return false; - } - }; -} - -function makeTableHeaderHandler(key: string) { - return { - key, - format: ["table-header"], - collapsed: true, - empty: true, - handler(range: Range, _context: Context) { - const [line] = this.quill.getLine(range.index); - if (line.prev) { - return removeLine.call(this, line, range); - } else { - const cellId = getCellId(line.formats()[line.statics.blotName]); - line.replaceWith(TableCellBlock.blotName, cellId); - } - } - }; -} - -function makeTableListHandler(key: string) { - return { - key, - format: ["table-list"], - collapsed: true, - empty: true, - handler(range: Range, _context: Context) { - const [line] = this.quill.getLine(range.index); - const cellId = getCellId(line.parent.formats()[line.parent.statics.blotName]); - line.replaceWith(TableCellBlock.blotName, cellId); - } - }; -} - -function removeLine(line: Line, range: Range) { - const tableModule = this.quill.getModule("table-better"); - line.remove(); - tableModule?.tableMenus.updateMenus(); - this.quill.setSelection(range.index - 1, Quill.sources.SILENT); - return false; -} - -Table.keyboardBindings = keyboardBindings; - -export default Table; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/types.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/types.ts deleted file mode 100644 index 715219c1b6..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/types.ts +++ /dev/null @@ -1,85 +0,0 @@ -import Quill from "quill"; -import TableHeader from "./formats/header"; -import TableList, { ListContainer } from "./formats/list"; -import { TableBody, TableCell, TableCellBlock, TableColgroup, TableContainer, TableRow } from "./formats/table"; -import QuillTableBetter from "./quill-table-better"; -import CellSelection from "./ui/cell-selection"; -import TableMenus from "./ui/table-menus"; - -export interface CorrectBound { - left: number; - top: number; - right: number; - bottom: number; - width?: number; - height?: number; -} - -export interface Props { - [propName: string]: string; -} - -export interface Range { - index: number; - length: number; -} - -export type InsertTableHandler = (rows: number, columns: number) => void; - -export type TableCellAllowedChildren = TableCellBlock | TableHeader | ListContainer; -export type TableCellChildren = TableCellAllowedChildren | TableList; -export type TableCellMap = Map; - -export type UseLanguageHandler = (name: string) => string; - -interface BindingObject extends Partial> { - key: number | string | string[]; - shortKey?: boolean | null; - shiftKey?: boolean | null; - altKey?: boolean | null; - metaKey?: boolean | null; - ctrlKey?: boolean | null; - prefix?: RegExp; - suffix?: RegExp; - format?: Record | string[]; - handler?: ( - this: { quill: Quill }, - range: Range, - // eslint-disable-next-line no-use-before-define - curContext: Context, - // eslint-disable-next-line no-use-before-define - binding: NormalizedBinding - ) => boolean | void; -} - -interface Context { - collapsed: boolean; - empty: boolean; - offset: number; - prefix: string; - suffix: string; - format: Record; - event: KeyboardEvent; - line: TableCellChildren; -} - -interface NormalizedBinding extends Omit { - key: string | number; -} - -export type { - BindingObject, - CellSelection, - Context, - ListContainer, - QuillTableBetter, - TableBody, - TableCell, - TableCellBlock, - TableColgroup, - TableContainer, - TableHeader, - TableList, - TableMenus, - TableRow -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/cell-selection.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/cell-selection.ts deleted file mode 100644 index a2c9399a7e..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/cell-selection.ts +++ /dev/null @@ -1,718 +0,0 @@ -// @ts-nocheck -import { BlockBlot, ContainerBlot, EmbedBlot } from "parchment"; -import Quill from "quill"; -import type { AttributeMap, Op } from "quill-delta"; -import Delta from "quill-delta"; -import { DEVIATION } from "../config"; -import { TableCell, TableCellBlock } from "../formats/table"; -import type { - Props, - QuillTableBetter, - TableBody, - TableCellAllowedChildren, - TableCellChildren, - TableContainer, - TableRow -} from "../types"; -import { getComputeBounds, getComputeSelectedTds, getCopyTd, getCorrectBounds, getCorrectCellBlot } from "../utils"; -import { applyFormat } from "../utils/clipboard-matchers"; - -const WHITE_LIST = [ - "bold", - "italic", - "underline", - "strike", - "size", - "color", - "background", - "font", - "list", - "header", - "align", - "link", - "image" -]; - -// Only supports formatting for a single cell. -const SINGLE_WHITE_LIST = ["link", "image"]; - -function isLine(blot: unknown): blot is BlockBlot | EmbedBlot { - return blot instanceof BlockBlot || blot instanceof EmbedBlot; -} - -class CellSelection { - quill: Quill; - selectedTds: Element[]; - startTd: Element; - endTd: Element; - disabledList: Array; - singleList: Array; - tableBetter: QuillTableBetter; - constructor(quill: Quill, tableBetter: QuillTableBetter) { - this.quill = quill; - this.selectedTds = []; - this.startTd = null; - this.endTd = null; - this.disabledList = []; - this.singleList = []; - this.tableBetter = tableBetter; - if (!this.quill.options.readOnly) { - this.quill.root.addEventListener("click", this.handleClick.bind(this)); - } - this.initDocumentListener(); - this.initWhiteList(); - } - - attach(input: HTMLElement) { - let format = Array.from(input.classList).find(className => { - return className.indexOf("ql-") === 0; - }); - if (!format) return; - const [whiteList, singleWhiteList] = this.getButtonsWhiteList(); - const correctDisabled = this.getCorrectDisabled(input, format); - format = format.slice("ql-".length); - if (!whiteList.includes(format)) { - this.disabledList.push(...correctDisabled); - } - if (singleWhiteList.includes(format)) { - this.singleList.push(...correctDisabled); - } - } - - clearSelected() { - for (const td of this.selectedTds) { - td.classList && td.classList.remove("ql-cell-focused", "ql-cell-selected"); - } - this.selectedTds = []; - this.startTd = null; - this.endTd = null; - } - - exitTableFocus(block: TableCellChildren, up: boolean) { - const cell = getCorrectCellBlot(block); - const table = cell.table(); - const offset = up ? -1 : table.length(); - const index = table.offset(this.quill.scroll) + offset; - this.tableBetter.hideTools(); - this.quill.setSelection(index, 0, Quill.sources.USER); - } - - getButtonsWhiteList(): [string[], string[]] { - const { options = {} } = this.tableBetter; - const { toolbarButtons = {} } = options; - const { whiteList = WHITE_LIST, singleWhiteList = SINGLE_WHITE_LIST } = toolbarButtons; - return [whiteList, singleWhiteList]; - } - - getCopyColumns(container: Element) { - const tr = container.querySelector("tr"); - const children = Array.from(tr.querySelectorAll("td")); - return children.reduce((sum: number, td: HTMLTableCellElement) => { - const colspan = ~~td.getAttribute("colspan") || 1; - return (sum += colspan); - }, 0); - } - - getCopyData() { - const tableBlot = (Quill.find(this.selectedTds[0]) as TableCell).table(); - const tableCells = tableBlot.descendants(TableCell); - if (tableCells.length === this.selectedTds.length) { - const html = tableBlot.getCopyTable(); - const text = this.getText(html); - return { html, text }; - } - let html = ""; - const map: { [propName: string]: Element[] } = {}; - for (const td of this.selectedTds) { - const rowId = td.getAttribute("data-row"); - if (!map[rowId]) { - map[rowId] = []; - } - map[rowId].push(td); - } - for (const tds of Object.values(map)) { - let res = ""; - for (const td of tds) { - res += getCopyTd(td.outerHTML); - } - res = `${res}`; - html += res; - } - html = `${html}
    `; - const text = this.getText(html); - return { html, text }; - } - - getCorrectDisabled(input: HTMLElement, format: string) { - if (input.tagName !== "SELECT") return [input]; - const parentElement = input.closest("span.ql-formats"); - if (!parentElement) return [input]; - const child = parentElement.querySelectorAll(`span.${format}.ql-picker`); - return [...child, input]; - } - - getCorrectRow(td: Element, key: string) { - const offset = key === "next" ? 0 : -1; - let rowspan = (~~td.getAttribute("rowspan") || 1) + offset || 1; - const cell = Quill.find(td) as TableCell; - let row = cell.parent; - while (row && rowspan) { - // @ts-expect-error - row = row[key]; - rowspan--; - } - return row?.domNode; - } - - getCorrectValue(format: string, value: boolean | string) { - for (const td of this.selectedTds) { - const blot = Quill.find(td) as TableCell; - const html = blot.html() || td.outerHTML; - const delta = this.quill.clipboard.convert({ - html, - text: "\n" - }); - for (const op of delta.ops) { - if (this.isContinue(op)) continue; - value = this.getListCorrectValue(format, value, op?.attributes); - const val = (op?.attributes && op?.attributes[format]) || false; - if (value != val) return value; - } - } - return !value; - } - - getListCorrectValue(format: string, value: boolean | string, formats: AttributeMap = {}) { - if (format !== "list") return value; - if (value === "check") { - if (formats[format] === "checked" || formats[format] === "unchecked") { - return false; - } else { - return "unchecked"; - } - } - return value; - } - - getPasteComputeBounds(startTd: Element, rightTd: Element, row: TableRow) { - const startTdBounds = startTd.getBoundingClientRect(); - const rightTdBounds = rightTd.getBoundingClientRect(); - const rowBounds = row.domNode.getBoundingClientRect(); - const containerBounds = this.quill.container.getBoundingClientRect(); - const scrollLeft = this.quill.container.scrollLeft; - const scrollTop = this.quill.container.scrollTop; - const left = startTdBounds.left - containerBounds.left - scrollLeft; - const right = rightTdBounds.right - containerBounds.left - scrollLeft; - const top = startTdBounds.top - containerBounds.top - scrollTop; - const bottom = rowBounds.bottom - containerBounds.top - scrollTop; - return { - left, - right, - top, - bottom - }; - } - - getPasteInfo(td: Element, copyColumns: number, rowspan: number) { - let clospan = 0; - let cloTd = null; - let rowTd = null; - let row: Element = td.parentElement; - while (td) { - const colspan = ~~td.getAttribute("colspan") || 1; - clospan += colspan; - if (clospan >= copyColumns) { - clospan = copyColumns; - cloTd = td; - break; - } - td = td.nextElementSibling; - } - while (--rowspan) { - if (!row.nextElementSibling) { - rowTd = row.firstElementChild; - break; - } - row = row.nextElementSibling; - } - return [ - { clospan: Math.abs(copyColumns - clospan), cloTd }, - { rowspan, rowTd } - ]; - } - - getPasteLastRow(row: TableRow, len: number) { - while (--len && row) { - row = row.next; - } - return row; - } - - getPasteTds(computeSelectedTds: Element[]) { - const map: { [propName: string]: Element[] } = {}; - for (const td of computeSelectedTds) { - const id = td.getAttribute("data-row"); - if (!map[id]) map[id] = []; - map[id].push(td); - } - return Object.values(map); - } - - getText(html: string): string { - const delta: Delta = this.quill.clipboard.convert({ html }); - return delta - .filter(op => typeof op.insert === "string") - .map(op => op.insert) - .join(""); - } - - handleClick(e: MouseEvent) { - if (e.detail < 3 || !this.selectedTds.length) return; - // Multiple clicks result in cell being selected - // Cell are deleted when deleting - const { index, length } = this.quill.getSelection(true); - this.quill.setSelection(index, length - 1, Quill.sources.SILENT); - this.quill.scrollSelectionIntoView(); - } - - handleDeleteKeyup(e: KeyboardEvent) { - if (this.selectedTds?.length < 2) return; - if (e.key === "Backspace" || e.key === "Delete") { - if (e.ctrlKey) { - this.tableBetter.tableMenus.deleteColumn(true); - this.tableBetter.tableMenus.deleteRow(true); - } else { - this.removeSelectedTdsContent(); - } - } - } - - handleKeyup(e: KeyboardEvent) { - switch (e.key) { - case "ArrowLeft": - case "ArrowRight": - this.makeTableArrowLevelHandler(e.key); - break; - case "ArrowUp": - case "ArrowDown": - this.makeTableArrowVerticalHandler(e.key); - break; - default: - break; - } - } - - handleMousedown(e: MouseEvent) { - this.clearSelected(); - const table = (e.target as Element).closest("table.ql-table-better"); - if (!table) return; - this.tableBetter.tableMenus.destroyTablePropertiesForm(); - const startTd = (e.target as Element).closest("td"); - this.startTd = startTd; - this.endTd = startTd; - this.selectedTds = [startTd]; - startTd.classList.add("ql-cell-focused"); - - const handleMouseMove = (e: MouseEvent) => { - const endTd = (e.target as Element).closest("td"); - if (!endTd) return; - const isEqualNode = startTd.isEqualNode(endTd); - if (isEqualNode) return; - this.clearSelected(); - this.startTd = startTd; - this.endTd = endTd; - const startCorrectBounds = getCorrectBounds(startTd, this.quill.container); - const endCorrectBounds = getCorrectBounds(endTd, this.quill.container); - const computeBounds = getComputeBounds(startCorrectBounds, endCorrectBounds); - this.selectedTds = getComputeSelectedTds(computeBounds, table, this.quill.container); - for (const td of this.selectedTds) { - td.classList && td.classList.add("ql-cell-selected"); - } - if (!isEqualNode) this.quill.blur(); - }; - - const handleMouseup = (e: MouseEvent) => { - this.setSingleDisabled(); - this.setCorrectPositionTds(this.startTd, this.endTd, this.selectedTds); - this.quill.root.removeEventListener("mousemove", handleMouseMove); - this.quill.root.removeEventListener("mouseup", handleMouseup); - }; - - this.quill.root.addEventListener("mousemove", handleMouseMove); - this.quill.root.addEventListener("mouseup", handleMouseup); - } - - initDocumentListener() { - this.quill.root.addEventListener("copy", (e: ClipboardEvent) => this.onCaptureCopy(e, false)); - this.quill.root.addEventListener("cut", (e: ClipboardEvent) => this.onCaptureCopy(e, true)); - this.quill.root.addEventListener("keyup", this.handleDeleteKeyup.bind(this)); - this.quill.root.addEventListener("paste", this.onCapturePaste.bind(this)); - } - - initWhiteList() { - const toolbar = this.quill.getModule("toolbar"); - // @ts-expect-error - Array.from(toolbar.container.querySelectorAll("button, select")).forEach(input => { - // @ts-ignore - this.attach(input); - }); - } - - insertColumnCell(table: TableContainer, offset: number) { - const tbody = table.tbody() as TableBody; - if (!tbody) return; - tbody.children.forEach((row: TableRow) => { - const id = row.children.tail.domNode.getAttribute("data-row"); - for (let i = 0; i < offset; i++) { - table.insertColumnCell(row, id, null); - } - }); - } - - insertRow(table: TableContainer, offset: number, td: Element) { - const index = (Quill.find(td) as TableCell).rowOffset(); - while (offset--) { - table.insertRow(index + 1, 1); - } - } - - insertWith(insert: string | Record) { - if (typeof insert !== "string") return false; - return insert.startsWith("\n") && insert.endsWith("\n"); - } - - isContinue(op: Op) { - if ( - this.insertWith(op.insert) && - (!op.attributes || op.attributes["table-list"] || op.attributes["table-header"]) - ) { - return true; - } - return false; - } - - lines(blot: TableCell) { - const getLines = (blot: TableCell | TableCellAllowedChildren) => { - let lines: (BlockBlot | EmbedBlot)[] = []; - blot.children.forEach((child: TableCellAllowedChildren) => { - if (child instanceof ContainerBlot) { - lines = lines.concat(getLines(child)); - } else if (isLine(child)) { - lines.push(child); - } - }); - return lines; - }; - return getLines(blot); - } - - makeTableArrowLevelHandler(key: string) { - const td = key === "ArrowLeft" ? this.startTd : this.endTd; - const range = this.quill.getSelection(); - if (!range) return; - const [block] = this.quill.getLine(range.index); - // @ts-expect-error - const cell = getCorrectCellBlot(block); - if (!cell) return this.tableBetter.hideTools(); - if (cell && (!td || !td.isEqualNode(cell.domNode))) { - this.setSelected(cell.domNode, false); - this.tableBetter.showTools(false); - } - } - - makeTableArrowVerticalHandler(key: string) { - const up = key === "ArrowUp" ? true : false; - const range = this.quill.getSelection(); - if (!range) return; - const [block, offset] = this.quill.getLine(range.index); - const _key = up ? "prev" : "next"; - if (block[_key] && this.selectedTds.length) { - const index = block[_key].offset(this.quill.scroll) + Math.min(offset, block[_key].length() - 1); - this.quill.setSelection(index, 0, Quill.sources.USER); - } else { - if (!this.selectedTds.length) { - // @ts-expect-error - const cellBlot = getCorrectCellBlot(block); - if (!cellBlot) return; - this.tableArrowSelection(up, cellBlot); - this.tableBetter.showTools(false); - return; - } - const td = up ? this.startTd : this.endTd; - const cell = Quill.find(td) as TableCell; - const targetRow = cell.parent[_key]; - const { left: _left, right: _right } = td.getBoundingClientRect(); - if (targetRow) { - let cellBlot = null; - let row = targetRow; - while (row && !cellBlot) { - let ref = row.children.head; - while (ref) { - const { left, right } = ref.domNode.getBoundingClientRect(); - if (Math.abs(left - _left) <= DEVIATION) { - cellBlot = ref; - break; - } else if (Math.abs(right - _right) <= DEVIATION) { - cellBlot = ref; - break; - } - ref = ref.next; - } - row = row[_key]; - } - if (!cellBlot) { - // @ts-expect-error - this.exitTableFocus(block, up); - } else { - this.tableArrowSelection(up, cellBlot); - } - } else { - // @ts-expect-error - this.exitTableFocus(block, up); - } - } - } - - onCaptureCopy(e: ClipboardEvent, isCut = false) { - if (this.selectedTds?.length < 2) return; - if (e.defaultPrevented) return; - e.preventDefault(); - const { html, text } = this.getCopyData(); - e.clipboardData?.setData("text/plain", text); - e.clipboardData?.setData("text/html", html); - if (isCut) this.removeSelectedTdsContent(); - } - - onCapturePaste(e: ClipboardEvent) { - if (!this.selectedTds?.length) return; - e.preventDefault(); - const html = e.clipboardData?.getData("text/html"); - const text = e.clipboardData?.getData("text/plain"); - const container = this.quill.root.ownerDocument.createElement("div"); - container.innerHTML = html; - const copyRows = Array.from(container.querySelectorAll("tr")); - if (!copyRows.length) return; - const cell = Quill.find(this.startTd) as TableCell; - const row = cell.row(); - const table = cell.table(); - this.quill.history.cutoff(); - const copyColumns = this.getCopyColumns(container); - const [cloInfo, rowInfo] = this.getPasteInfo(this.startTd, copyColumns, copyRows.length); - const { clospan, cloTd } = cloInfo; - const { rowspan, rowTd } = rowInfo; - if (clospan) this.insertColumnCell(table, clospan); - if (rowspan) this.insertRow(table, rowspan, rowTd); - const rightTd = clospan ? row.children.tail.domNode : cloTd; - const pasteLastRow = this.getPasteLastRow(row, copyRows.length); - const computeBounds = this.getPasteComputeBounds(this.startTd, rightTd, pasteLastRow); - const pasteTds = this.getPasteTds(getComputeSelectedTds(computeBounds, table.domNode, this.quill.container)); - const copyTds = copyRows.reduce((copyTds: HTMLElement[][], row: HTMLTableRowElement) => { - copyTds.push(Array.from(row.querySelectorAll("td"))); - return copyTds; - }, []); - const selectedTds: HTMLElement[] = []; - while (copyTds.length) { - const copyTs = copyTds.shift(); - const pasteTs = pasteTds.shift(); - let prevPasteTd = null; - let cell: TableCell = null; - while (copyTs.length) { - const copyTd = copyTs.shift(); - const pasteTd = pasteTs.shift(); - if (!pasteTd) { - const id = prevPasteTd.getAttribute("data-row"); - const ref = Quill.find(prevPasteTd) as TableCell; - cell = table.insertColumnCell(ref.parent, id, ref.next); - cell = this.pasteSelectedTd(cell.domNode, copyTd); - prevPasteTd = cell.domNode; - } else { - prevPasteTd = pasteTd; - cell = this.pasteSelectedTd(pasteTd, copyTd); - } - cell && selectedTds.push(cell.domNode); - } - while (pasteTs.length) { - const pasteTd = pasteTs.shift(); - pasteTd.remove(); - } - } - this.quill.blur(); - this.setSelectedTds(selectedTds); - this.tableBetter.tableMenus.updateMenus(); - this.quill.scrollSelectionIntoView(); - } - - pasteSelectedTd(selectedTd: Element, copyTd: Element) { - const id = selectedTd.getAttribute("data-row"); - const copyFormats = TableCell.formats(copyTd); - Object.assign(copyFormats, { "data-row": id }); - const cell = Quill.find(selectedTd) as TableCell; - const _cell = cell.replaceWith(cell.statics.blotName, copyFormats) as TableCell; - this.quill.setSelection(_cell.offset(this.quill.scroll) + _cell.length() - 1, 0, Quill.sources.USER); - const range = this.quill.getSelection(true); - const formats = this.quill.getFormat(range.index) as Props; - const html = copyTd.innerHTML; - const text = this.getText(html); - const pastedDelta = this.quill.clipboard.convert({ text, html }); - const delta = new Delta().retain(range.index).delete(range.length).concat(applyFormat(pastedDelta, formats)); - this.quill.updateContents(delta, Quill.sources.USER); - return _cell; - } - - removeCursor() { - const range = this.quill.getSelection(true); - if (range && range.length === 0) { - // The attach function of the toolbar module generated extra cursor - // when clicked, which needs to be removed. - this.quill.selection.cursor.remove(); - this.quill.blur(); - } - } - - removeSelectedTdContent(td: Element) { - const tdBlot = Quill.find(td) as TableCell; - let head = tdBlot.children.head as TableCellBlock; - const cellId = head.formats()[TableCellBlock.blotName]; - const cellBlock = this.quill.scroll.create(TableCellBlock.blotName, cellId); - tdBlot.insertBefore(cellBlock, head); - while (head) { - head.remove(); - head = head.next; - } - } - - removeSelectedTdsContent() { - if (this.selectedTds.length < 2) return; - for (const td of this.selectedTds) { - this.removeSelectedTdContent(td); - } - this.tableBetter.tableMenus.updateMenus(); - } - - setCorrectPositionTds(startTd: Element, endTd: Element, selectedTds: Element[]) { - if (!startTd || !endTd || selectedTds.length < 2) return; - const firstTd = selectedTds[0]; - const lastTd = selectedTds[selectedTds.length - 1]; - const tds = [...new Set([startTd, endTd, firstTd, lastTd])]; - tds.sort((prev: Element, next: Element) => { - const prevBounds = prev.getBoundingClientRect(); - const nextBounds = next.getBoundingClientRect(); - if ( - (prevBounds.top <= nextBounds.top || prevBounds.bottom <= nextBounds.bottom) && - (prevBounds.left <= nextBounds.left || prevBounds.right <= nextBounds.right) - ) { - return -1; - } - return 1; - }); - this.startTd = tds[0]; - this.endTd = tds[tds.length - 1]; - } - - setDisabled(disabled: boolean) { - for (const input of this.disabledList) { - if (disabled) { - input.classList.add("ql-table-button-disabled"); - } else { - input.classList.remove("ql-table-button-disabled"); - } - } - this.setSingleDisabled(); - } - - setSelected(target: Element, force: boolean = true) { - const cell = Quill.find(target) as TableCell; - this.clearSelected(); - this.startTd = target; - this.endTd = target; - this.selectedTds = [target]; - target.classList.add("ql-cell-focused"); - force && this.quill.setSelection(cell.offset(this.quill.scroll) + cell.length() - 1, 0, Quill.sources.USER); - } - - setSelectedTds(selectedTds: Element[]) { - this.clearSelected(); - this.startTd = selectedTds[0]; - this.endTd = selectedTds[selectedTds.length - 1]; - this.selectedTds = selectedTds; - for (const td of this.selectedTds) { - td.classList && td.classList.add("ql-cell-selected"); - } - } - - setSelectedTdsFormat(format: string, value: boolean | string) { - const selectedTds = []; - const toolbar = this.quill.getModule("toolbar"); - for (const td of this.selectedTds) { - // @ts-expect-error - if (toolbar.handlers[format] != null) { - const cellBlot = Quill.find(td) as TableCell; - const lines = this.lines(cellBlot); - // @ts-expect-error - const blot = toolbar.handlers[format].call(toolbar, value, lines); - blot && selectedTds.push(getCorrectCellBlot(blot).domNode); - } else { - const selection = window.getSelection(); - selection.selectAllChildren(td); - this.quill.format(format, value, Quill.sources.USER); - selection.removeAllRanges(); - } - } - this.quill.blur(); - selectedTds.length && this.setSelectedTds(selectedTds); - } - - setSingleDisabled() { - for (const input of this.singleList) { - if (this.selectedTds.length > 1) { - input.classList.add("ql-table-button-disabled"); - } else { - input.classList.remove("ql-table-button-disabled"); - } - } - } - - tableArrowSelection(up: boolean, cellBlot: TableCell) { - const key = up ? "tail" : "head"; - const offset = up ? cellBlot.children[key].length() - 1 : 0; - this.setSelected(cellBlot.domNode, false); - const index = cellBlot.children[key].offset(this.quill.scroll) + offset; - this.quill.setSelection(index, 0, Quill.sources.USER); - } - - updateSelected(type: string) { - switch (type) { - case "column": - { - const target = this.endTd.nextElementSibling || this.startTd.previousElementSibling; - if (!target) return; - this.setSelected(target); - } - break; - case "row": - { - const row = this.getCorrectRow(this.endTd, "next") || this.getCorrectRow(this.startTd, "prev"); - if (!row) return; - const startCorrectBounds = getCorrectBounds(this.startTd, this.quill.container); - let child = row.firstElementChild; - while (child) { - const childCorrectBounds = getCorrectBounds(child, this.quill.container); - if ( - childCorrectBounds.left + DEVIATION >= startCorrectBounds.left || - childCorrectBounds.right - DEVIATION >= startCorrectBounds.left - ) { - this.setSelected(child); - return; - } - child = child.nextElementSibling; - } - this.setSelected(row.firstElementChild); - } - break; - default: - break; - } - } -} - -export default CellSelection; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/operate-line.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/operate-line.ts deleted file mode 100644 index 925334d5d1..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/operate-line.ts +++ /dev/null @@ -1,446 +0,0 @@ -// @ts-nocheck -import Quill from "quill"; -import type { QuillTableBetter, TableCell, TableColgroup } from "../types"; -import { setElementAttribute, setElementProperty, updateTableWidth } from "../utils"; - -interface Options { - tableNode: HTMLElement; - cellNode: HTMLElement; - mousePosition: { - clientX: number; - clientY: number; - }; -} - -const DRAG_BLOCK_HEIGHT = 8; -const DRAG_BLOCK_WIDTH = 8; -const LINE_CONTAINER_HEIGHT = 5; -const LINE_CONTAINER_WIDTH = 5; - -class OperateLine { - quill: Quill; - options: Options | null; - drag: boolean; - line: HTMLElement | null; - dragBlock: HTMLElement | null; - dragTable: HTMLElement | null; - direction: string | null; - tableBetter: QuillTableBetter; - constructor(quill: Quill, tableBetter?: QuillTableBetter) { - this.quill = quill; - this.options = null; - this.drag = false; - this.line = null; - this.dragBlock = null; - this.dragTable = null; - this.direction = null; // 1.level 2.vertical - this.tableBetter = tableBetter; - if (!this.quill.options.readOnly) { - this.quill.root.addEventListener("mousemove", this.handleMouseMove.bind(this)); - } - } - - createDragBlock() { - const dragBlock = this.quill.root.ownerDocument.createElement("div"); - dragBlock.classList.add("ql-operate-block"); - const { dragBlockProps } = this.getProperty(this.options); - setElementProperty(dragBlock, dragBlockProps); - this.dragBlock = dragBlock; - this.quill.container.appendChild(dragBlock); - this.updateCell(dragBlock); - } - - createDragTable(table: Element) { - const dragTable = this.quill.root.ownerDocument.createElement("div"); - const properties = this.getDragTableProperty(table); - dragTable.classList.add("ql-operate-drag-table"); - setElementProperty(dragTable, properties); - this.dragTable = dragTable; - this.quill.container.appendChild(dragTable); - } - - createOperateLine() { - const container = this.quill.root.ownerDocument.createElement("div"); - const line = this.quill.root.ownerDocument.createElement("div"); - container.classList.add("ql-operate-line-container"); - const { containerProps, lineProps } = this.getProperty(this.options); - setElementProperty(container, containerProps); - setElementProperty(line, lineProps); - container.appendChild(line); - this.quill.container.appendChild(container); - this.line = container; - this.updateCell(container); - } - - getCorrectCol(colgroup: TableColgroup, sum: number) { - let child = colgroup.children.head; - while (child && --sum) { - child = child.next; - } - return child; - } - - getDragTableProperty(table: Element) { - const { left, top, width, height } = table.getBoundingClientRect(); - const containerRect = this.quill.container.getBoundingClientRect(); - return { - left: `${left - containerRect.left}px`, - top: `${top - containerRect.top}px`, - width: `${width}px`, - height: `${height}px`, - display: "block" - }; - } - - getLevelColSum(cell: Element) { - let previousNode = cell; - let sum = 0; - while (previousNode) { - const colspan = ~~previousNode.getAttribute("colspan") || 1; - sum += colspan; - // @ts-ignore - previousNode = previousNode.previousSibling; - } - return sum; - } - - getMaxColNum(cell: Element) { - const cells = cell.parentElement.children; - let nums = 0; - for (const cell of cells) { - const colspan = ~~cell.getAttribute("colspan") || 1; - nums += colspan; - } - return nums; - } - - getProperty(options: Options) { - const containerRect = this.quill.container.getBoundingClientRect(); - const { tableNode, cellNode, mousePosition } = options; - const { clientX, clientY } = mousePosition; - const tableRect = tableNode.getBoundingClientRect(); - const cellRect = cellNode.getBoundingClientRect(); - const x = cellRect.left + cellRect.width; - const y = cellRect.top + cellRect.height; - const dragBlockProps = { - width: `${DRAG_BLOCK_WIDTH}px`, - height: `${DRAG_BLOCK_HEIGHT}px`, - top: `${tableRect.bottom - containerRect.top}px`, - left: `${tableRect.right - containerRect.left}px`, - display: tableRect.bottom > containerRect.bottom ? "none" : "block" - }; - if (Math.abs(x - clientX) <= 5) { - this.direction = "level"; - return { - dragBlockProps, - containerProps: { - width: `${LINE_CONTAINER_WIDTH}px`, - height: `${containerRect.height}px`, - top: "0", - left: `${x - containerRect.left - LINE_CONTAINER_WIDTH / 2}px`, - display: "flex", - cursor: "col-resize" - }, - lineProps: { - width: "1px", - height: "100%" - } - }; - } else if (Math.abs(y - clientY) <= 5) { - this.direction = "vertical"; - return { - dragBlockProps, - containerProps: { - width: `${containerRect.width}px`, - height: `${LINE_CONTAINER_HEIGHT}px`, - top: `${y - containerRect.top - LINE_CONTAINER_HEIGHT / 2}px`, - left: "0", - display: "flex", - cursor: "row-resize" - }, - lineProps: { - width: "100%", - height: "1px" - } - }; - } else { - this.hideLine(); - } - return { dragBlockProps }; - } - - getVerticalCells(cell: Element, rowspan: number) { - let row = cell.parentElement; - while (rowspan > 1 && row) { - // @ts-ignore - row = row.nextSibling; - rowspan--; - } - return row.children; - } - - handleMouseMove(e: MouseEvent) { - const tableNode = (e.target as Element).closest("table.ql-table-better"); - if (!tableNode) return; - const cellNode = (e.target as Element).closest("td"); - const mousePosition = { - clientX: e.clientX, - clientY: e.clientY - }; - if (!tableNode || !cellNode) { - if (this.line && !this.drag) { - this.hideLine(); - this.hideDragBlock(); - } - return; - } - const options = { tableNode, cellNode, mousePosition }; - if (!this.line) { - this.options = options; - this.createOperateLine(); - this.createDragBlock(); - } else { - if (this.drag || !cellNode) return; - this.updateProperty(options); - } - } - - hideDragBlock() { - this.dragBlock && setElementProperty(this.dragBlock, { display: "none" }); - } - - hideDragTable() { - this.dragTable && setElementProperty(this.dragTable, { display: "none" }); - } - - hideLine() { - this.line && setElementProperty(this.line, { display: "none" }); - } - - isLine(node: Element) { - return node.classList.contains("ql-operate-line-container"); - } - - setCellLevelRect(cell: Element, clientX: number) { - const { right } = cell.getBoundingClientRect(); - const change = ~~(clientX - right); - const colSum = this.getLevelColSum(cell); - const quillCell = Quill.find(cell) as TableCell; - const tableBlot = quillCell?.table(); - const colgroup = tableBlot.colgroup() as TableColgroup; - const bounds = tableBlot.domNode.getBoundingClientRect(); - if (colgroup) { - const col = this.getCorrectCol(colgroup, colSum); - const nextCol = col.next; - const formats = col.formats()[col.statics.blotName]; - col.domNode.setAttribute("width", `${parseFloat(formats["width"]) + change}`); - if (nextCol) { - const nextFormats = nextCol.formats()[nextCol.statics.blotName]; - nextCol.domNode.setAttribute("width", `${parseFloat(nextFormats["width"]) - change}`); - } - } else { - const isLastCell = cell.nextElementSibling == null; - const rows = cell.parentElement.parentElement.children; - const preNodes: [Element, string][] = []; - for (const row of rows) { - const cells = row.children; - if (isLastCell) { - const cell = cells[cells.length - 1]; - const { width } = cell.getBoundingClientRect(); - preNodes.push([cell, `${~~(width + change)}`]); - continue; - } - let sum = 0; - for (const cell of cells) { - const colspan = ~~cell.getAttribute("colspan") || 1; - sum += colspan; - if (sum > colSum) break; - if (sum === colSum) { - const { width } = cell.getBoundingClientRect(); - const nextCell = cell.nextElementSibling; - if (!nextCell) continue; - const { width: nextWidth } = nextCell.getBoundingClientRect(); - preNodes.push([cell, `${~~(width + change)}`], [nextCell, `${~~(nextWidth - change)}`]); - } - } - } - for (const [node, width] of preNodes) { - setElementAttribute(node, { width }); - setElementProperty(node as HTMLElement, { width: `${width}px` }); - } - } - if (cell.nextElementSibling == null) { - updateTableWidth(tableBlot.domNode, bounds, change); - } - } - - setCellRect(cell: Element, clientX: number, clientY: number) { - if (this.direction === "level") { - this.setCellLevelRect(cell, clientX); - } else if (this.direction === "vertical") { - this.setCellVerticalRect(cell, clientY); - } - } - - setCellsRect(cell: Element, changeX: number, changeY: number) { - const rows = cell.parentElement.parentElement.children; - const maxColNum = this.getMaxColNum(cell); - const averageX = changeX / maxColNum; - const averageY = changeY / rows.length; - const preNodes: [Element, string, string][] = []; - const tableBlot = (Quill.find(cell) as TableCell).table(); - const colgroup = tableBlot.colgroup() as TableColgroup; - const bounds = tableBlot.domNode.getBoundingClientRect(); - for (const row of rows) { - const cells = row.children; - for (const cell of cells) { - const colspan = ~~cell.getAttribute("colspan") || 1; - const { width, height } = cell.getBoundingClientRect(); - preNodes.push([cell, `${Math.ceil(width + averageX * colspan)}`, `${Math.ceil(height + averageY)}`]); - } - } - if (colgroup) { - let col = colgroup.children.head; - for (const [node, , height] of preNodes) { - setElementAttribute(node, { height }); - setElementProperty(node as HTMLElement, { height: `${height}px` }); - } - while (col) { - const { width } = col.domNode.getBoundingClientRect(); - setElementAttribute(col.domNode, { width: `${Math.ceil(width + averageX)}` }); - col = col.next; - } - } else { - for (const [node, width, height] of preNodes) { - setElementAttribute(node, { width, height }); - setElementProperty(node as HTMLElement, { - width: `${width}px`, - height: `${height}px` - }); - } - } - updateTableWidth(tableBlot.domNode, bounds, changeX); - } - - setCellVerticalRect(cell: Element, clientY: number) { - const rowspan = ~~cell.getAttribute("rowspan") || 1; - const cells = rowspan > 1 ? this.getVerticalCells(cell, rowspan) : cell.parentElement.children; - for (const cell of cells) { - const { top } = cell.getBoundingClientRect(); - const height = `${~~(clientY - top)}`; - setElementAttribute(cell, { height }); - setElementProperty(cell as HTMLElement, { height: `${height}px` }); - } - } - - toggleLineChildClass(isAdd: boolean) { - const node = this.line.firstElementChild; - if (isAdd) { - node.classList.add("ql-operate-line"); - } else { - node.classList.remove("ql-operate-line"); - } - } - - updateCell(node: Element) { - if (!node) return; - const isLine = this.isLine(node); - const handleDrag = (e: MouseEvent) => { - e.preventDefault(); - if (this.drag) { - if (isLine) { - this.updateDragLine(e.clientX, e.clientY); - this.hideDragBlock(); - } else { - this.updateDragBlock(e.clientX, e.clientY); - this.hideLine(); - } - } - }; - - const handleMouseup = (e: MouseEvent) => { - e.preventDefault(); - const { cellNode, tableNode } = this.options; - if (isLine) { - this.setCellRect(cellNode, e.clientX, e.clientY); - this.toggleLineChildClass(false); - } else { - const { right, bottom } = tableNode.getBoundingClientRect(); - const changeX = e.clientX - right; - const changeY = e.clientY - bottom; - this.setCellsRect(cellNode, changeX, changeY); - this.dragBlock.classList.remove("ql-operate-block-move"); - this.hideDragBlock(); - this.hideDragTable(); - } - this.drag = false; - window.document.removeEventListener("mousemove", handleDrag, false); - window.document.removeEventListener("mouseup", handleMouseup, false); - this.tableBetter.tableMenus.updateMenus(tableNode); - }; - - const handleMousedown = (e: MouseEvent) => { - e.preventDefault(); - const { tableNode } = this.options; - if (isLine) { - this.toggleLineChildClass(true); - } else { - if (this.dragTable) { - const properties = this.getDragTableProperty(tableNode); - setElementProperty(this.dragTable, properties); - } else { - this.createDragTable(tableNode); - } - } - this.drag = true; - window.document.addEventListener("mousemove", handleDrag); - window.document.addEventListener("mouseup", handleMouseup); - }; - node.addEventListener("mousedown", handleMousedown); - } - - updateDragBlock(clientX: number, clientY: number) { - const containerRect = this.quill.container.getBoundingClientRect(); - this.dragBlock.classList.add("ql-operate-block-move"); - setElementProperty(this.dragBlock, { - top: `${~~(clientY - containerRect.top - DRAG_BLOCK_HEIGHT / 2)}px`, - left: `${~~(clientX - containerRect.left - DRAG_BLOCK_WIDTH / 2)}px` - }); - this.updateDragTable(clientX, clientY); - } - - updateDragLine(clientX: number, clientY: number) { - const containerRect = this.quill.container.getBoundingClientRect(); - if (this.direction === "level") { - setElementProperty(this.line, { - left: `${~~(clientX - containerRect.left - LINE_CONTAINER_WIDTH / 2)}px` - }); - } else if (this.direction === "vertical") { - setElementProperty(this.line, { - top: `${~~clientY - containerRect.top - LINE_CONTAINER_HEIGHT / 2}px` - }); - } - } - - updateDragTable(clientX: number, clientY: number) { - const { top, left } = this.dragTable.getBoundingClientRect(); - const width = clientX - left; - const height = clientY - top; - setElementProperty(this.dragTable, { - width: `${width}px`, - height: `${height}px`, - display: "block" - }); - } - - updateProperty(options: Options) { - const { containerProps, lineProps, dragBlockProps } = this.getProperty(options); - if (!containerProps || !lineProps) return; - this.options = options; - setElementProperty(this.line, containerProps); - setElementProperty(this.line.firstChild as HTMLElement, lineProps); - setElementProperty(this.dragBlock, dragBlockProps); - } -} - -export default OperateLine; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/table-menus.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/table-menus.ts deleted file mode 100644 index 1d20679621..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/table-menus.ts +++ /dev/null @@ -1,854 +0,0 @@ -// @ts-nocheck -import type { LinkedList } from "parchment"; -import Quill from "quill"; -import Delta from "quill-delta"; -import { CELL_DEFAULT_VALUES, CELL_DEFAULT_WIDTH, CELL_PROPERTIES, DEVIATION, TABLE_PROPERTIES } from "../config"; -import { TableCell, tableId } from "../formats/table"; -import type { - CorrectBound, - Props, - QuillTableBetter, - TableCellMap, - TableColgroup, - TableContainer, - TableRow, - UseLanguageHandler -} from "../types"; -import { - createTooltip, - getAlign, - getCellFormats, - getComputeBounds, - getComputeSelectedCols, - getComputeSelectedTds, - getCorrectBounds, - getElementStyle, - setElementProperty, - updateTableWidth -} from "../utils"; -import TablePropertiesForm from "./table-properties-form"; - -interface Children { - [propName: string]: { - content: string; - handler: () => void; - }; -} - -interface MenusDefaults { - [propName: string]: { - content: string; - icon: string; - handler: (list: HTMLUListElement, tooltip: HTMLDivElement) => void; - children?: Children; - }; -} - -enum Alignment { - left = "margin-left", - right = "margin-right" -} - -function getMenusConfig(useLanguage: UseLanguageHandler, menus?: string[]): MenusDefaults { - const DEFAULT: MenusDefaults = { - column: { - content: useLanguage("col"), - icon: "icons icon-Column", - handler(list: HTMLUListElement, tooltip: HTMLDivElement) { - this.toggleAttribute(list, tooltip); - }, - children: { - left: { - content: useLanguage("insColL"), - handler() { - const { leftTd } = this.getSelectedTdsInfo(); - const bounds = this.table.getBoundingClientRect(); - this.insertColumn(leftTd, 0); - updateTableWidth(this.table, bounds, CELL_DEFAULT_WIDTH); - this.updateMenus(); - } - }, - right: { - content: useLanguage("insColR"), - handler() { - const { rightTd } = this.getSelectedTdsInfo(); - const bounds = this.table.getBoundingClientRect(); - this.insertColumn(rightTd, 1); - updateTableWidth(this.table, bounds, CELL_DEFAULT_WIDTH); - this.updateMenus(); - } - }, - delete: { - content: useLanguage("delCol"), - handler() { - this.deleteColumn(); - } - } - } - }, - row: { - content: useLanguage("row"), - icon: "icons icon-Row", - handler(list: HTMLUListElement, tooltip: HTMLDivElement) { - this.toggleAttribute(list, tooltip); - }, - children: { - above: { - content: useLanguage("insRowAbv"), - handler() { - const { leftTd } = this.getSelectedTdsInfo(); - this.insertRow(leftTd, 0); - this.updateMenus(); - } - }, - below: { - content: useLanguage("insRowBlw"), - handler() { - const { rightTd } = this.getSelectedTdsInfo(); - this.insertRow(rightTd, 1); - this.updateMenus(); - } - }, - delete: { - content: useLanguage("delRow"), - handler() { - this.deleteRow(); - } - } - } - }, - merge: { - content: useLanguage("mCells"), - icon: "icons icon-Merge", - handler(list: HTMLUListElement, tooltip: HTMLDivElement) { - this.toggleAttribute(list, tooltip); - }, - children: { - merge: { - content: useLanguage("mCells"), - handler() { - this.mergeCells(); - this.updateMenus(); - } - }, - split: { - content: useLanguage("sCell"), - handler() { - this.splitCell(); - this.updateMenus(); - } - } - } - }, - table: { - content: useLanguage("tblProps"), - icon: "icons icon-Table", - handler(list: HTMLUListElement, tooltip: HTMLDivElement) { - const attribute = { - ...getElementStyle(this.table, TABLE_PROPERTIES), - align: this.getTableAlignment(this.table) - }; - this.toggleAttribute(list, tooltip); - this.tablePropertiesForm = new TablePropertiesForm(this, { attribute, type: "table" }); - this.hideMenus(); - } - }, - cell: { - content: useLanguage("cellProps"), - icon: "icons icon-Cell", - handler(list: HTMLUListElement, tooltip: HTMLDivElement) { - const { selectedTds } = this.tableBetter.cellSelection; - const isGridShown = this.isGridShown(); - if (isGridShown) { - this.showGrid(false); - } - const attribute = - selectedTds.length > 1 - ? this.getSelectedTdsAttrs(selectedTds) - : this.getSelectedTdAttrs(selectedTds[0]); - this.toggleAttribute(list, tooltip); - this.tablePropertiesForm = new TablePropertiesForm(this, { attribute, type: "cell" }); - this.hideMenus(); - if (isGridShown) { - this.showGrid(true); - } - } - }, - wrap: { - content: useLanguage("insParaOTbl"), - icon: "icons icon-Wrap", - handler(list: HTMLUListElement, tooltip: HTMLDivElement) { - this.toggleAttribute(list, tooltip); - }, - children: { - before: { - content: useLanguage("insB4"), - handler() { - this.insertParagraph(-1); - } - }, - after: { - content: useLanguage("insAft"), - handler() { - this.insertParagraph(1); - } - } - } - }, - delete: { - content: useLanguage("delTable"), - icon: "icons icon-Delete", - handler() { - this.deleteTable(); - } - } - }; - const EXTRA: MenusDefaults = { - copy: { - content: useLanguage("copyTable"), - icon: "icons icon-Copy", - handler() { - this.copyTable(); - } - }, - grid: { - content: useLanguage("showGrid"), - icon: "icons icon-Table grid-toggle", - handler() { - this.showGrid(); - } - } - }; - if (menus?.length) { - return Object.values(menus).reduce((config: MenusDefaults, key: string) => { - config[key] = Object.assign({}, DEFAULT, EXTRA)[key]; - return config; - }, {}); - } - return DEFAULT; -} - -class TableMenus { - quill: Quill; - table: HTMLElement | null; - root: HTMLElement; - prevList: HTMLUListElement | null; - prevTooltip: HTMLDivElement | null; - scroll: boolean; - tableBetter: QuillTableBetter; - tablePropertiesForm: TablePropertiesForm; - constructor(quill: Quill, tableBetter?: QuillTableBetter) { - this.quill = quill; - this.table = null; - this.prevList = null; - this.prevTooltip = null; - this.scroll = false; - this.tableBetter = tableBetter; - this.tablePropertiesForm = null; - if (!this.quill.options.readOnly) { - this.quill.root.addEventListener("click", this.handleClick.bind(this)); - } - this.root = this.createMenus(); - } - - async copyTable() { - if (!this.table) return; - const tableBlot = Quill.find(this.table) as TableContainer; - if (!tableBlot) return; - const html = "


    " + tableBlot.getCopyTable(); - const text = this.tableBetter.cellSelection.getText(html); - const clipboardItem = new ClipboardItem({ - "text/html": new Blob([html], { type: "text/html" }), - "text/plain": new Blob([text], { type: "text/plain" }) - }); - try { - await navigator.clipboard.write([clipboardItem]); - const index = this.quill.getIndex(tableBlot); - const length = tableBlot.length(); - this.quill.setSelection(index + length, Quill.sources.SILENT); - this.tableBetter.hideTools(); - this.quill.scrollSelectionIntoView(); - } catch (error) { - console.error("Failed to copy table:", error); - } - } - - isGridShown() { - return this.table?.classList.contains("ql-table-grid"); - } - - showGrid(isShow?: boolean) { - const tableGridHelperClass = "ql-table-grid"; - if (isShow === undefined) { - this.table?.classList.toggle(tableGridHelperClass); - this.root.classList.toggle(tableGridHelperClass); - } else if (isShow) { - this.table?.classList.add(tableGridHelperClass); - this.root.classList.add(tableGridHelperClass); - } else { - this.table?.classList.remove(tableGridHelperClass); - this.root.classList.remove(tableGridHelperClass); - } - } - - createList(children: Children) { - if (!children) return null; - const container = this.quill.root.ownerDocument.createElement("ul"); - for (const [, child] of Object.entries(children)) { - const { content, handler } = child; - const list = this.quill.root.ownerDocument.createElement("li"); - list.innerText = content; - list.addEventListener("click", handler.bind(this)); - container.appendChild(list); - } - container.classList.add("ql-table-dropdown-list", "ql-hidden"); - return container; - } - - createMenu(left: string, right: string, isDropDown: boolean) { - const container = this.quill.root.ownerDocument.createElement("div"); - container.classList.add("ql-table-dropdown"); - const dropDown = this.quill.root.ownerDocument.createElement("span"); - dropDown.classList.add("ql-table-tooltip-hover"); - const classes = left.split(" "); - const icon = this.quill.root.ownerDocument.createElement("span"); - icon.classList.add(...classes); - dropDown.appendChild(icon); - if (isDropDown) { - const classes = right.split(" "); - const dropDownIcon = this.quill.root.ownerDocument.createElement("span"); - dropDownIcon.classList.add(...classes); - dropDown.appendChild(dropDownIcon); - } - container.appendChild(dropDown); - return container; - } - - createMenus() { - const { language, options = {} } = this.tableBetter; - const { menus } = options; - const useLanguage = language.useLanguage.bind(language); - const container = this.quill.root.ownerDocument.createElement("div"); - container.classList.add("ql-table-menus-container", "ql-hidden"); - for (const [, val] of Object.entries(getMenusConfig(useLanguage, menus))) { - const { content, icon, children, handler } = val; - const list = this.createList(children); - const tooltip = createTooltip(content); - const menu = this.createMenu(icon, "icons icon-Arrow-down", !!children); - menu.appendChild(tooltip); - list && menu.appendChild(list); - container.appendChild(menu); - menu.addEventListener("click", handler.bind(this, list, tooltip)); - } - this.quill.container.appendChild(container); - return container; - } - - deleteColumn(isKeyboard: boolean = false) { - const { computeBounds, leftTd, rightTd } = this.getSelectedTdsInfo(); - const bounds = this.table.getBoundingClientRect(); - const deleteTds = getComputeSelectedTds(computeBounds, this.table, this.quill.container, "column"); - const deleteCols = getComputeSelectedCols(computeBounds, this.table, this.quill.container); - const tableBlot = (Quill.find(leftTd) as TableCell).table(); - const { changeTds, delTds } = this.getCorrectTds(deleteTds, computeBounds, leftTd, rightTd); - if (isKeyboard && delTds.length !== this.tableBetter.cellSelection.selectedTds.length) return; - this.tableBetter.cellSelection.updateSelected("column"); - tableBlot.deleteColumn(changeTds, delTds, this.deleteTable.bind(this), deleteCols); - updateTableWidth(this.table, bounds, computeBounds.left - computeBounds.right); - this.updateMenus(); - } - - deleteRow(isKeyboard: boolean = false) { - const selectedTds = this.tableBetter.cellSelection.selectedTds; - const map: { [propName: string]: TableRow } = {}; - for (const td of selectedTds) { - let rowspan = ~~td.getAttribute("rowspan") || 1; - let row = Quill.find(td.parentElement) as TableRow; - if (rowspan > 1) { - while (row && rowspan) { - const id = row.children.head.domNode.getAttribute("data-row"); - if (!map[id]) map[id] = row; - row = row.next; - rowspan--; - } - } else { - const id = td.getAttribute("data-row"); - if (!map[id]) map[id] = row; - } - } - const rows: TableRow[] = Object.values(map); - if (isKeyboard) { - const sum = rows.reduce((sum: number, row: TableRow) => { - return (sum += row.children.length); - }, 0); - if (sum !== selectedTds.length) return; - } - this.tableBetter.cellSelection.updateSelected("row"); - const tableBlot = (Quill.find(selectedTds[0]) as TableCell).table(); - tableBlot.deleteRow(rows, this.deleteTable.bind(this)); - this.updateMenus(); - } - - deleteTable() { - const tableBlot = Quill.find(this.table) as TableContainer; - if (!tableBlot) return; - const offset = tableBlot.offset(this.quill.scroll); - tableBlot.remove(); - this.tableBetter.hideTools(); - this.quill.setSelection(offset - 1, 0, Quill.sources.USER); - } - - destroyTablePropertiesForm() { - if (!this.tablePropertiesForm) return; - this.tablePropertiesForm.removePropertiesForm(); - this.tablePropertiesForm = null; - } - - getCellsOffset(computeBounds: CorrectBound, bounds: CorrectBound, leftColspan: number, rightColspan: number) { - const tableBlot = Quill.find(this.table) as TableContainer; - const cells = tableBlot.descendants(TableCell); - const _left = Math.max(bounds.left, computeBounds.left); - const _right = Math.min(bounds.right, computeBounds.right); - const map: TableCellMap = new Map(); - const leftMap: TableCellMap = new Map(); - const rightMap: TableCellMap = new Map(); - for (const cell of cells) { - const { left, right } = getCorrectBounds(cell.domNode, this.quill.container); - if (left + DEVIATION >= _left && right <= _right + DEVIATION) { - this.setCellsMap(cell, map); - } else if (left + DEVIATION >= computeBounds.left && right <= bounds.left + DEVIATION) { - this.setCellsMap(cell, leftMap); - } else if (left + DEVIATION >= bounds.right && right <= computeBounds.right + DEVIATION) { - this.setCellsMap(cell, rightMap); - } - } - return ( - this.getDiffOffset(map) || - this.getDiffOffset(leftMap, leftColspan) + this.getDiffOffset(rightMap, rightColspan) - ); - } - - getColsOffset(colgroup: TableColgroup, computeBounds: CorrectBound, bounds: CorrectBound) { - let col = colgroup.children.head; - const _left = Math.max(bounds.left, computeBounds.left); - const _right = Math.min(bounds.right, computeBounds.right); - let colLeft = null; - let colRight = null; - let offset = 0; - while (col) { - const { width } = col.domNode.getBoundingClientRect(); - if (!colLeft && !colRight) { - const colBounds = getCorrectBounds(col.domNode, this.quill.container); - colLeft = colBounds.left; - colRight = colLeft + width; - } else { - colLeft = colRight; - colRight += width; - } - if (colLeft > _right) break; - if (colLeft >= _left && colRight <= _right) { - offset--; - } - col = col.next; - } - return offset; - } - - getCorrectBounds(table: HTMLElement): CorrectBound[] { - const bounds = this.quill.container.getBoundingClientRect(); - const tableBounds = getCorrectBounds(table, this.quill.container); - return tableBounds.width >= bounds.width - ? [{ ...tableBounds, left: 0, right: bounds.width }, bounds] - : [tableBounds, bounds]; - } - - getCorrectTds(deleteTds: Element[], computeBounds: CorrectBound, leftTd: Element, rightTd: Element) { - const changeTds: [Element, number][] = []; - const delTds = []; - const colgroup = (Quill.find(leftTd) as TableCell).table().colgroup() as TableColgroup; - const leftColspan = ~~leftTd.getAttribute("colspan") || 1; - const rightColspan = ~~rightTd.getAttribute("colspan") || 1; - if (colgroup) { - for (const td of deleteTds) { - const bounds = getCorrectBounds(td, this.quill.container); - if (bounds.left + DEVIATION >= computeBounds.left && bounds.right <= computeBounds.right + DEVIATION) { - delTds.push(td); - } else { - const offset = this.getColsOffset(colgroup, computeBounds, bounds); - changeTds.push([td, offset]); - } - } - } else { - for (const td of deleteTds) { - const bounds = getCorrectBounds(td, this.quill.container); - if (bounds.left + DEVIATION >= computeBounds.left && bounds.right <= computeBounds.right + DEVIATION) { - delTds.push(td); - } else { - const offset = this.getCellsOffset(computeBounds, bounds, leftColspan, rightColspan); - changeTds.push([td, offset]); - } - } - } - return { changeTds, delTds }; - } - - getDiffOffset(map: TableCellMap, colspan?: number) { - let offset = 0; - const tds = this.getTdsFromMap(map); - if (tds.length) { - if (colspan) { - for (const td of tds) { - offset += ~~td.getAttribute("colspan") || 1; - } - offset -= colspan; - } else { - for (const td of tds) { - offset -= ~~td.getAttribute("colspan") || 1; - } - } - } - return offset; - } - - getRefInfo(row: TableRow, right: number) { - let ref = null; - if (!row) return { id: tableId(), ref }; - let td = row.children.head; - const id = td.domNode.getAttribute("data-row"); - while (td) { - const { left } = td.domNode.getBoundingClientRect(); - if (Math.abs(left - right) <= DEVIATION) { - return { id, ref: td }; - // The nearest cell of a multi-row cell - } else if (Math.abs(left - right) >= DEVIATION && !ref) { - ref = td; - } - td = td.next; - } - return { id, ref }; - } - - getSelectedTdAttrs(td: HTMLElement) { - const cellBlot = Quill.find(td) as TableCell; - const align = getAlign(cellBlot); - - const attr: Props = align - ? { ...getElementStyle(td, CELL_PROPERTIES), "text-align": align } - : getElementStyle(td, CELL_PROPERTIES); - return attr; - } - - getSelectedTdsAttrs(selectedTds: HTMLElement[]) { - const map = new Map(); - let attribute = null; - for (const td of selectedTds) { - const attr = this.getSelectedTdAttrs(td); - if (!attribute) { - attribute = attr; - continue; - } - for (const key of Object.keys(attribute)) { - if (map.has(key)) continue; - if (attr[key] !== attribute[key]) { - map.set(key, false); - } - } - } - for (const key of Object.keys(attribute)) { - if (map.has(key)) { - attribute[key] = CELL_DEFAULT_VALUES[key]; - } - } - return attribute; - } - - getSelectedTdsInfo() { - const { startTd, endTd } = this.tableBetter.cellSelection; - const startCorrectBounds = getCorrectBounds(startTd, this.quill.container); - const endCorrectBounds = getCorrectBounds(endTd, this.quill.container); - const computeBounds = getComputeBounds(startCorrectBounds, endCorrectBounds); - if (startCorrectBounds.left <= endCorrectBounds.left && startCorrectBounds.top <= endCorrectBounds.top) { - return { - computeBounds, - leftTd: startTd, - rightTd: endTd - }; - } - return { - computeBounds, - leftTd: endTd, - rightTd: startTd - }; - } - - getTableAlignment(table: HTMLTableElement) { - const align = table.getAttribute("align"); - if (!align) { - const { [Alignment.left]: left, [Alignment.right]: right } = getElementStyle(table, [ - Alignment.left, - Alignment.right - ]); - if (left === "auto") { - if (right === "auto") return "center"; - return "right"; - } - return "left"; - } - return align || "center"; - } - - getTdsFromMap(map: TableCellMap) { - return Object.values(Object.fromEntries(map)).reduce( - (tds: HTMLTableCellElement[], item: HTMLTableCellElement[]) => { - return tds.length > item.length ? tds : item; - }, - [] - ); - } - - handleClick(e: MouseEvent) { - const table = (e.target as Element).closest("table.ql-table-better"); - if (!table) return; - this.prevList && this.prevList.classList.add("ql-hidden"); - this.prevTooltip && this.prevTooltip.classList.remove("ql-table-tooltip-hidden"); - this.prevList = null; - this.prevTooltip = null; - if (!table && !this.tableBetter.cellSelection.selectedTds.length) { - this.hideMenus(); - this.destroyTablePropertiesForm(); - return; - } else { - if (this.tablePropertiesForm) return; - this.showMenus(); - this.updateMenus(table); - if ((table && !table.isEqualNode(this.table)) || this.scroll) { - this.updateScroll(false); - } - this.table = table; - } - } - - hideMenus() { - this.root.classList.add("ql-hidden"); - } - - insertColumn(td: HTMLTableColElement, offset: number) { - const { left, right, width } = td.getBoundingClientRect(); - const tdBlot = Quill.find(td) as TableCell; - const tableBlot = tdBlot.table(); - const isLast = td.parentElement.lastChild.isEqualNode(td); - const position = offset > 0 ? right : left; - tableBlot.insertColumn(position, isLast, width, offset); - this.quill.scrollSelectionIntoView(); - } - - insertParagraph(offset: number) { - const blot = Quill.find(this.table) as TableContainer; - const index = this.quill.getIndex(blot); - const length = offset > 0 ? blot.length() : 0; - const delta = new Delta().retain(index + length).insert("\n"); - this.quill.updateContents(delta, Quill.sources.USER); - this.quill.setSelection(index + length, Quill.sources.SILENT); - this.tableBetter.hideTools(); - this.quill.scrollSelectionIntoView(); - } - - insertRow(td: HTMLTableColElement, offset: number) { - const tdBlot = Quill.find(td) as TableCell; - const index = tdBlot.rowOffset(); - const tableBlot = tdBlot.table(); - if (offset > 0) { - const rowspan = ~~td.getAttribute("rowspan") || 1; - tableBlot.insertRow(index + offset + rowspan - 1, offset); - } else { - tableBlot.insertRow(index + offset, offset); - } - this.quill.scrollSelectionIntoView(); - } - - mergeCells() { - const { selectedTds } = this.tableBetter.cellSelection; - const { computeBounds, leftTd } = this.getSelectedTdsInfo(); - const leftTdBlot = Quill.find(leftTd) as TableCell; - const [formats, cellId] = getCellFormats(leftTdBlot); - const head = leftTdBlot.children.head; - const tableBlot = leftTdBlot.table(); - const rows = tableBlot.tbody().children as LinkedList; - const row = leftTdBlot.row(); - const colspan = row.children.reduce((colspan: number, td: TableCell) => { - const tdCorrectBounds = getCorrectBounds(td.domNode, this.quill.container); - if (tdCorrectBounds.left >= computeBounds.left && tdCorrectBounds.right <= computeBounds.right) { - colspan += ~~td.domNode.getAttribute("colspan") || 1; - } - return colspan; - }, 0); - const rowspan = rows.reduce((rowspan: number, row: TableRow) => { - const rowCorrectBounds = getCorrectBounds(row.domNode, this.quill.container); - if (rowCorrectBounds.top >= computeBounds.top && rowCorrectBounds.bottom <= computeBounds.bottom) { - let minRowspan = Number.MAX_VALUE; - row.children.forEach((td: TableCell) => { - const rowspan = ~~td.domNode.getAttribute("rowspan") || 1; - minRowspan = Math.min(minRowspan, rowspan); - }); - rowspan += minRowspan; - } - return rowspan; - }, 0); - let offset = 0; - for (const td of selectedTds) { - if (leftTd.isEqualNode(td)) continue; - const blot = Quill.find(td) as TableCell; - blot.moveChildren(leftTdBlot); - blot.remove(); - if (!blot.parent?.children?.length) offset++; - } - if (offset) { - // Subtract the number of rows deleted by the merge - row.children.forEach((child: TableCell) => { - if (child.domNode.isEqualNode(leftTd)) return; - const rowspan = child.domNode.getAttribute("rowspan"); - const [formats] = getCellFormats(child); - // @ts-expect-error - child.replaceWith(child.statics.blotName, { ...formats, rowspan: rowspan - offset }); - }); - } - leftTdBlot.setChildrenId(cellId); - // @ts-expect-error - head.format(leftTdBlot.statics.blotName, { ...formats, colspan, rowspan: rowspan - offset }); - this.tableBetter.cellSelection.setSelected(head.parent.domNode); - this.quill.scrollSelectionIntoView(); - } - - setCellsMap(cell: TableCell, map: TableCellMap) { - const key: string = cell.domNode.getAttribute("data-row"); - if (map.has(key)) { - map.set(key, [...map.get(key), cell.domNode]); - } else { - map.set(key, [cell.domNode]); - } - } - - showMenus() { - if (this.table?.classList.contains("ql-table-grid")) { - this.root.classList.add("ql-table-grid"); - } else { - this.root.classList.remove("ql-table-grid"); - } - this.root.classList.remove("ql-hidden"); - } - - splitCell() { - const { selectedTds } = this.tableBetter.cellSelection; - const { leftTd } = this.getSelectedTdsInfo(); - const leftTdBlot = Quill.find(leftTd) as TableCell; - const head = leftTdBlot.children.head; - for (const td of selectedTds) { - const colspan = ~~td.getAttribute("colspan") || 1; - const rowspan = ~~td.getAttribute("rowspan") || 1; - if (colspan === 1 && rowspan === 1) continue; - const columnCells: [TableRow, string, TableCell | null][] = []; - const { width, right } = td.getBoundingClientRect(); - const blot = Quill.find(td) as TableCell; - const tableBlot = blot.table(); - const nextBlot = blot.next; - const rowBlot = blot.row(); - if (rowspan > 1) { - if (colspan > 1) { - let nextRowBlot = rowBlot.next; - for (let i = 1; i < rowspan; i++) { - const { ref, id } = this.getRefInfo(nextRowBlot, right); - for (let j = 0; j < colspan; j++) { - columnCells.push([nextRowBlot, id, ref]); - } - nextRowBlot && (nextRowBlot = nextRowBlot.next); - } - } else { - let nextRowBlot = rowBlot.next; - for (let i = 1; i < rowspan; i++) { - const { ref, id } = this.getRefInfo(nextRowBlot, right); - columnCells.push([nextRowBlot, id, ref]); - nextRowBlot && (nextRowBlot = nextRowBlot.next); - } - } - } - if (colspan > 1) { - const id = td.getAttribute("data-row"); - for (let i = 1; i < colspan; i++) { - columnCells.push([rowBlot, id, nextBlot]); - } - } - for (const [row, id, ref] of columnCells) { - tableBlot.insertColumnCell(row, id, ref); - } - const [formats] = getCellFormats(blot); - blot.replaceWith(blot.statics.blotName, { - ...formats, - width: ~~(width / colspan), - colspan: null, - rowspan: null - }); - } - this.tableBetter.cellSelection.setSelected(head.parent.domNode); - this.quill.scrollSelectionIntoView(); - } - - toggleAttribute(list: HTMLUListElement, tooltip: HTMLDivElement) { - if (this.prevList && !this.prevList.isEqualNode(list)) { - this.prevList.classList.add("ql-hidden"); - this.prevTooltip.classList.remove("ql-table-tooltip-hidden"); - } - if (!list) return; - list.classList.toggle("ql-hidden"); - tooltip.classList.toggle("ql-table-tooltip-hidden"); - this.prevList = list; - this.prevTooltip = tooltip; - } - - updateMenus(table: HTMLElement = this.table) { - if (!table) return; - requestAnimationFrame(() => { - this.root.classList.remove("ql-table-triangle-none"); - const [tableBounds, containerBounds] = this.getCorrectBounds(table); - const { left, right, top, bottom } = tableBounds; - const { height, width } = this.root.getBoundingClientRect(); - const toolbar = this.quill.getModule("toolbar"); - // @ts-expect-error - const computedStyle = getComputedStyle(toolbar.container); - let correctTop = top - height - 10; - let correctLeft = (left + right - width) >> 1; - if (correctTop > -parseInt(computedStyle.paddingBottom)) { - this.root.classList.add("ql-table-triangle-up"); - this.root.classList.remove("ql-table-triangle-down"); - } else { - if (bottom > containerBounds.height) { - correctTop = containerBounds.height + 10; - } else { - correctTop = bottom + 10; - } - this.root.classList.add("ql-table-triangle-down"); - this.root.classList.remove("ql-table-triangle-up"); - } - if (correctLeft < containerBounds.left) { - correctLeft = 0; - this.root.classList.add("ql-table-triangle-none"); - } else if (correctLeft + width > containerBounds.right) { - correctLeft = containerBounds.right - width; - this.root.classList.add("ql-table-triangle-none"); - } - setElementProperty(this.root, { - left: `${correctLeft}px`, - top: `${correctTop}px` - }); - }); - } - - updateScroll(scroll: boolean) { - this.scroll = scroll; - } - - updateTable(table: HTMLElement) { - this.table = table; - } -} - -export default TableMenus; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/table-properties-form.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/table-properties-form.ts deleted file mode 100644 index 7e92cbfdef..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/table-properties-form.ts +++ /dev/null @@ -1,606 +0,0 @@ -import { computePosition, flip, offset, shift } from "@floating-ui/react"; -import Coloris from "@melloware/coloris"; -import "@melloware/coloris/dist/coloris.css"; -import Quill from "quill"; -import { getProperties } from "../config"; -import { ListContainer } from "../formats/list"; -import type { Props, TableCell, TableCellBlock, TableContainer, TableHeader, TableList, TableMenus } from "../types"; -import { - addDimensionsUnit, - createTooltip, - getClosestElement, - getComputeSelectedCols, - isDimensions, - setElementAttribute, - setElementProperty -} from "../utils"; - -interface Child { - category: string; - propertyName: string; - value?: string; - attribute?: Props; - options?: string[]; - tooltip?: string; - menus?: Menus[]; - valid?: (value?: string) => boolean; - message?: string; -} - -interface Menus { - icon: string; - describe: string; - align: string; -} - -interface Properties { - content: string; - children: Child[]; -} - -interface Options { - type: "table" | "cell"; - attribute: Props; -} - -interface ColorList { - value: string; - describe: string; -} - -const ACTION_LIST = [ - { icon: "icons icon-Save", label: "save" }, - { icon: "icons icon-Close", label: "cancel" } -]; - -const COLOR_LIST: ColorList[] = [ - { value: "#000000", describe: "black" }, - { value: "#4d4d4d", describe: "dimGrey" }, - { value: "#808080", describe: "grey" }, - { value: "#e6e6e6", describe: "lightGrey" }, - { value: "#ffffff", describe: "white" }, - { value: "#ff0000", describe: "red" }, - { value: "#ffa500", describe: "orange" }, - { value: "#ffff00", describe: "yellow" }, - { value: "#99e64d", describe: "lightGreen" }, - { value: "#008000", describe: "green" }, - { value: "#7fffd4", describe: "aquamarine" }, - { value: "#40e0d0", describe: "turquoise" }, - { value: "#4d99e6", describe: "lightBlue" }, - { value: "#0000ff", describe: "blue" }, - { value: "#800080", describe: "purple" } -]; - -class TablePropertiesForm { - tableMenus: TableMenus; - options: Options; - attrs: Props; - borderForm: HTMLElement[]; - saveButton: HTMLButtonElement | null; - form: HTMLDivElement; - constructor(tableMenus: TableMenus, options: Options) { - this.tableMenus = tableMenus; - this.options = options; - this.attrs = { ...options.attribute }; - this.borderForm = []; - this.saveButton = null; - this.form = this.createPropertiesForm(options); - } - - checkBtnsAction(status: string) { - if (status === "save") { - this.saveAction(this.options.type); - } - this.removePropertiesForm(); - this.tableMenus.showMenus(); - this.tableMenus.updateMenus(); - } - - createActionBtns(listener: EventListener, showLabel: boolean) { - const useLanguage = this.getUseLanguage(); - const ownerDocument = this.tableMenus.quill.root.ownerDocument; - const container = ownerDocument.createElement("div"); - const fragment = ownerDocument.createDocumentFragment(); - container.classList.add("properties-form-action-row"); - for (const { icon, label } of ACTION_LIST) { - const button = ownerDocument.createElement("button"); - const iconContainer = ownerDocument.createElement("span"); - const iconClasses = icon.split(" "); - iconContainer.classList.add(...iconClasses); - button.appendChild(iconContainer); - button.setAttribute("label", label); - if (showLabel) { - const labelContainer = ownerDocument.createElement("span"); - labelContainer.innerText = useLanguage(label); - button.appendChild(labelContainer); - } - fragment.appendChild(button); - } - container.addEventListener("click", e => listener(e)); - container.appendChild(fragment); - return container; - } - - createCheckBtns(child: Child) { - const { menus, propertyName } = child; - const ownerDocument = this.tableMenus.quill.root.ownerDocument; - const container = ownerDocument.createElement("div"); - const fragment = ownerDocument.createDocumentFragment(); - for (const { icon, describe, align } of menus ?? []) { - const container = ownerDocument.createElement("span"); - const iconContainer = ownerDocument.createElement("span"); - const iconClasses = icon.split(" "); - iconContainer.classList.add(...iconClasses); - container.appendChild(iconContainer); - container.setAttribute("data-align", align); - container.classList.add("ql-table-tooltip-hover"); - if (this.options.attribute[propertyName] === align) { - container.classList.add("ql-table-btns-checked"); - } - const tooltip = createTooltip(describe); - container.appendChild(tooltip); - fragment.appendChild(container); - } - container.classList.add("ql-table-check-container"); - container.appendChild(fragment); - container.addEventListener("click", e => { - const target: HTMLSpanElement | null = (e.target as HTMLElement).closest("span.ql-table-tooltip-hover"); - const value = target?.getAttribute("data-align"); - this.switchButton(container, target!); - this.setAttribute(propertyName, value ?? ""); - }); - return container; - } - - createColorContainer(child: Child) { - const container = this.tableMenus.quill.root.ownerDocument.createElement("div"); - container.classList.add("ql-table-color-container"); - const input = this.createColorInput(child); - const inputEl = input.querySelector("input"); - if (inputEl) { - this.createColorPicker(inputEl); - } - container.appendChild(input); - return container; - } - - createColorInput(child: Child) { - const { attribute = {}, propertyName, value } = child; - const ownerDocument = this.tableMenus.quill.root.ownerDocument; - const placeholder = attribute?.placeholder ?? ""; - const container = ownerDocument.createElement("div"); - container.classList.add("label-field-view", "label-field-view-color"); - const label = ownerDocument.createElement("label"); - label.innerText = placeholder; - const input = ownerDocument.createElement("input"); - const attributes = { - ...attribute, - class: "property-input", - value: value ?? "", - "data-property": propertyName - }; - setElementAttribute(input, attributes); - - container.appendChild(input); - container.appendChild(label); - return container; - } - - createColorPicker(input: HTMLInputElement) { - Coloris.init(); - Coloris({ - el: input, - clearButton: true, - closeButton: true, - onChange: (color: string, input: HTMLElement): void => { - const propertyName = input.getAttribute("data-property") ?? ""; - this.setAttribute(propertyName, color, input); - }, - swatches: COLOR_LIST.map(({ value }) => value), - theme: "polaroid" - }); - } - - createDropdown(value: string) { - const ownerDocument = this.tableMenus.quill.root.ownerDocument; - const dropdown = ownerDocument.createElement("div"); - const dropIcon = ownerDocument.createElement("span"); - const dropText = ownerDocument.createElement("span"); - dropIcon.classList.add("icons", "icon-Arrow-down", "ql-table-dropdown-icon"); - value && (dropText.innerText = value); - dropText.classList.add("ql-table-dropdown-text"); - dropdown.classList.add("ql-table-dropdown-properties"); - dropdown.appendChild(dropText); - dropdown.appendChild(dropIcon); - return { dropdown, dropText }; - } - - createInput(child: Child) { - const { attribute = {}, message, propertyName, valid, value } = child; - const ownerDocument = this.tableMenus.quill.root.ownerDocument; - const placeholder = attribute?.placeholder ?? ""; - const container = ownerDocument.createElement("div"); - const wrapper = ownerDocument.createElement("div"); - const label = ownerDocument.createElement("label"); - const input = ownerDocument.createElement("input"); - const status = ownerDocument.createElement("div"); - container.classList.add("label-field-view"); - wrapper.classList.add("label-field-view-input-wrapper"); - label.innerText = placeholder; - const attributes = { - ...attribute, - class: "property-input", - value: value ?? "" - }; - setElementAttribute(input, attributes); - input.addEventListener("input", e => { - const value = (e.target as HTMLInputElement).value; - valid && this.switchHidden(status, valid(value)); - this.updateInputStatus(wrapper, !valid?.(value)); - this.setAttribute(propertyName, value, container); - }); - status.classList.add("label-field-view-status", "ql-hidden"); - message && (status.innerText = message); - wrapper.appendChild(input); - wrapper.appendChild(label); - container.appendChild(wrapper); - valid && container.appendChild(status); - return container; - } - - createList(child: Child, dropText?: HTMLSpanElement) { - const { options, propertyName } = child; - if (!options?.length) return null; - const ownerDocument = this.tableMenus.quill.root.ownerDocument; - const container = ownerDocument.createElement("ul"); - for (const option of options) { - const list = ownerDocument.createElement("li"); - list.innerText = option; - container.appendChild(list); - } - container.classList.add("ql-table-dropdown-list", "ql-hidden"); - container.addEventListener("click", e => { - const value = (e.target as HTMLLIElement).innerText; - if (dropText) { - dropText.innerText = value; - } - this.toggleBorderDisabled(value); - this.setAttribute(propertyName, value); - }); - return container; - } - - createProperty(property: Properties) { - const { content, children } = property; - const ownerDocument = this.tableMenus.quill.root.ownerDocument; - const useLanguage = this.getUseLanguage(); - const container = ownerDocument.createElement("div"); - const label = ownerDocument.createElement("label"); - label.innerText = content; - label.classList.add("ql-table-dropdown-label"); - container.classList.add("properties-form-row"); - if (children.length === 1) { - container.classList.add("properties-form-row-full"); - } - container.appendChild(label); - for (const child of children) { - const node = this.createPropertyChild(child); - node && container.appendChild(node); - if (node && content === useLanguage("border")) { - this.borderForm.push(node); - } - } - return container; - } - - createPropertyChild(child: Child) { - const { category, value } = child; - switch (category) { - case "dropdown": - const { dropdown, dropText } = this.createDropdown(value!); - const list = this.createList(child, dropText); - dropdown.appendChild(list!); - dropdown.addEventListener("click", () => { - this.toggleHidden(list!); - this.updateSelectedStatus(dropdown, dropText.innerText, "dropdown"); - }); - return dropdown; - case "color": - return this.createColorContainer(child); - case "menus": - return this.createCheckBtns(child); - case "input": - return this.createInput(child); - default: - break; - } - } - - createPropertiesForm(options: Options) { - const useLanguage = this.getUseLanguage(); - const { title, properties } = getProperties(options, useLanguage); - const ownerDocument = this.tableMenus.quill.root.ownerDocument; - const container = ownerDocument.createElement("div"); - container.classList.add("ql-table-properties-form"); - const header = ownerDocument.createElement("h2"); - const actions = this.createActionBtns((e: MouseEvent) => { - const target = (e.target as HTMLElement).closest("button"); - const label = target?.getAttribute("label") ?? ""; - target && this.checkBtnsAction(label); - }, true); - header.innerText = title; - header.classList.add("properties-form-header"); - container.appendChild(header); - for (const property of properties) { - const node = this.createProperty(property); - container.appendChild(node); - } - container.appendChild(actions); - this.setBorderDisabled(); - this.tableMenus.quill.container.appendChild(container); - this.updatePropertiesForm(container, options.type); - this.setSaveButton(actions); - container.addEventListener("click", (e: MouseEvent) => { - const target = e.target as HTMLElement; - this.hiddenSelectList(target); - }); - return container; - } - - getCellStyle(td: Element, attrs: Props) { - const style = (td.getAttribute("style") || "") - .split(";") - .filter((value: string) => value.trim()) - .reduce((style: Props, value: string) => { - const arr = value.split(":"); - return { ...style, [arr[0].trim()]: arr[1].trim() }; - }, {}); - Object.assign(style, attrs); - return Object.keys(style).reduce((value: string, key: string) => { - return (value += `${key}: ${style[key]}; `); - }, ""); - } - - getColorClosest(container: HTMLElement) { - return getClosestElement(container, ".ql-table-color-container"); - } - - getDiffProperties() { - const change = this.attrs; - const old = this.options.attribute; - return Object.keys(change).reduce((attrs: Props, key) => { - if (change[key] !== old[key] || key.startsWith("border")) { - attrs[key] = isDimensions(key) ? addDimensionsUnit(change[key]) : change[key]; - } - return attrs; - }, {}); - } - - getUseLanguage() { - const { language } = this.tableMenus.tableBetter; - const useLanguage = language.useLanguage.bind(language); - return useLanguage; - } - - hiddenSelectList(element: HTMLElement) { - const listClassName = ".ql-table-dropdown-properties"; - const colorClassName = ".color-picker"; - const list = this.form.querySelectorAll(".ql-table-dropdown-list"); - const colorPicker = this.form.querySelectorAll(".color-picker-select"); - for (const node of [...Array.from(list), ...Array.from(colorPicker)]) { - if ( - node.closest(listClassName)?.isEqualNode(element.closest(listClassName)) || - node.closest(colorClassName)?.isEqualNode(element.closest(colorClassName)) - ) { - continue; - } - node.classList.add("ql-hidden"); - } - } - - removePropertiesForm() { - this.form.remove(); - this.borderForm = []; - } - - saveAction(type: string) { - switch (type) { - case "table": - this.saveTableAction(); - break; - default: - this.saveCellAction(); - break; - } - } - - saveCellAction() { - const { selectedTds } = this.tableMenus.tableBetter.cellSelection; - const { quill, table } = this.tableMenus; - const colgroup = (Quill.find(table as Node) as TableContainer)?.colgroup(); - const attrs = this.getDiffProperties(); - const width = parseFloat(attrs["width"]); - const align = attrs["text-align"]; - align && delete attrs["text-align"]; - const newSelectedTds = []; - if (colgroup && width) { - delete attrs["width"]; - const { computeBounds } = this.tableMenus.getSelectedTdsInfo(); - const cols = getComputeSelectedCols(computeBounds, table as Element, quill.container); - for (const col of cols) { - col.setAttribute("width", `${width}`); - } - } - for (const td of selectedTds) { - const tdBlot = Quill.find(td) as TableCell; - const blotName = tdBlot.statics.blotName; - const formats = tdBlot.formats()[blotName]; - const style = this.getCellStyle(td, attrs); - if (align) { - const _align = align === "left" ? "" : align; - tdBlot.children.forEach((child: TableCellBlock | ListContainer | TableHeader) => { - if (child.statics.blotName === ListContainer.blotName) { - child.children.forEach((ch: TableList) => { - ch.format && ch.format("align", _align); - }); - } else { - child.format("align", _align); - } - }); - } - const parent = tdBlot.replaceWith(blotName, { ...formats, style }) as TableCell; - newSelectedTds.push(parent.domNode); - } - this.tableMenus.tableBetter.cellSelection.setSelectedTds(newSelectedTds); - } - - saveTableAction() { - const { table, tableBetter } = this.tableMenus; - const temporary = (Quill.find(table as Node) as TableContainer).temporary()?.domNode; - const td = table?.querySelector("td"); - const attrs = this.getDiffProperties(); - const align = attrs["align"]; - delete attrs["align"]; - switch (align) { - case "center": - Object.assign(attrs, { margin: "0 auto" }); - break; - case "left": - Object.assign(attrs, { margin: "" }); - break; - case "right": - Object.assign(attrs, { "margin-left": "auto", "margin-right": "" }); - break; - default: - break; - } - const element = temporary || table; - setElementProperty(element!, attrs); - tableBetter.cellSelection.setSelected(td as Element); - } - - setAttribute(propertyName: string, value: string, container?: HTMLElement) { - this.attrs[propertyName] = value; - if (propertyName.includes("-color") && container) { - this.updateSelectColor(this.getColorClosest(container)!, value); - } - } - - setBorderDisabled() { - const [borderContainer] = this.borderForm; - // @ts-ignore - const borderStyle = borderContainer.querySelector(".ql-table-dropdown-text").innerText; - this.toggleBorderDisabled(borderStyle); - } - - setSaveButton(container: HTMLDivElement) { - const saveButton: HTMLButtonElement | null = container.querySelector('button[label="save"]'); - this.saveButton = saveButton; - } - - setSaveButtonDisabled(disabled: boolean) { - if (!this.saveButton) return; - if (disabled) { - this.saveButton.setAttribute("disabled", "true"); - } else { - this.saveButton.removeAttribute("disabled"); - } - } - - switchButton(container: HTMLDivElement, target: HTMLSpanElement) { - const children = container.querySelectorAll("span.ql-table-tooltip-hover"); - for (const child of Array.from(children)) { - child.classList.remove("ql-table-btns-checked"); - } - target.classList.add("ql-table-btns-checked"); - } - - switchHidden(container: HTMLElement, valid: boolean) { - if (!valid) { - container.classList.remove("ql-hidden"); - } else { - container.classList.add("ql-hidden"); - } - } - - toggleBorderDisabled(value: string) { - const [, colorContainer, widthContainer] = this.borderForm; - if (value === "none" || !value) { - this.attrs["border-color"] = ""; - this.attrs["border-width"] = ""; - this.updateSelectColor(colorContainer, ""); - this.updateInputValue(widthContainer, ""); - colorContainer.classList.add("ql-table-disabled"); - widthContainer.classList.add("ql-table-disabled"); - } else { - colorContainer.classList.remove("ql-table-disabled"); - widthContainer.classList.remove("ql-table-disabled"); - } - } - - toggleHidden(container: HTMLElement) { - container.classList.toggle("ql-hidden"); - } - - updateInputValue(element: Element, value: string) { - const input: HTMLInputElement | null = element.querySelector(".property-input"); - if (input) { - input.value = value; - } - } - - updateInputStatus(container: HTMLElement, status: boolean, isColor?: boolean) { - const closestContainer = isColor - ? this.getColorClosest(container) - : getClosestElement(container, ".label-field-view"); - const wrapper = closestContainer?.querySelector(".label-field-view-input-wrapper"); - if (status) { - wrapper?.classList.add("label-field-view-error"); - this.setSaveButtonDisabled(true); - } else { - wrapper?.classList.remove("label-field-view-error"); - const wrappers = this.form.querySelectorAll(".label-field-view-error"); - if (!wrappers.length) this.setSaveButtonDisabled(false); - } - } - - updatePropertiesForm(container: HTMLElement, type: string) { - const target = type === "table" ? this.tableMenus.table! : this.tableMenus.getSelectedTdsInfo().leftTd; - - computePosition(target, container, { - middleware: [offset(4), flip(), shift({ padding: 10 })], - placement: "bottom", - strategy: "fixed" - }).then(({ x, y }) => { - setElementProperty(container, { - left: `${x}px`, - top: `${y}px` - }); - }); - } - - updateSelectColor(element: Element, value: string) { - const input: HTMLInputElement | null = element.querySelector(".property-input"); - - if (input) { - input.value = value; - } - } - - updateSelectedStatus(container: HTMLDivElement, value: string, type: string) { - const selectors = type === "color" ? ".color-list" : ".ql-table-dropdown-list"; - const list = container.querySelector(selectors); - if (!list) return; - const lists = Array.from(list.querySelectorAll("li")); - for (const list of lists) { - list.classList.remove(`ql-table-${type}-selected`); - } - const selected = lists.find(li => { - const data = type === "color" ? li.getAttribute("data-color") : li.innerText; - return data === value; - }); - selected && selected.classList.add(`ql-table-${type}-selected`); - } -} - -export default TablePropertiesForm; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/toolbar-table.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/toolbar-table.ts deleted file mode 100644 index 2993dbf743..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/ui/toolbar-table.ts +++ /dev/null @@ -1,123 +0,0 @@ -// @ts-nocheck -import type { InlineBlot } from "parchment"; -import QuillInline from "quill/blots/inline"; -import type { InsertTableHandler } from "../types"; - -const Inline = QuillInline as typeof InlineBlot; -const SUM = 10; - -class ToolbarTable extends Inline {} - -class TableSelect { - computeChildren: Element[]; - root: HTMLDivElement; - constructor() { - this.computeChildren = []; - this.root = this.createContainer(); - } - - clearSelected(children: NodeListOf | Element[]) { - for (const child of children) { - child.classList && child.classList.remove("ql-cell-selected"); - } - this.computeChildren = []; - this.root && this.setLabelContent(this.root.lastElementChild, null); - } - - createContainer() { - const container = document.createElement("div"); - const list = document.createElement("div"); - const label = document.createElement("div"); - const fragment = document.createDocumentFragment(); - for (let row = 1; row <= SUM; row++) { - for (let column = 1; column <= SUM; column++) { - const child = document.createElement("span"); - child.setAttribute("row", `${row}`); - child.setAttribute("column", `${column}`); - fragment.appendChild(child); - } - } - label.innerHTML = "0 × 0"; - container.classList.add("ql-table-select-container", "ql-hidden"); - list.classList.add("ql-table-select-list"); - label.classList.add("ql-table-select-label"); - list.appendChild(fragment); - container.appendChild(list); - container.appendChild(label); - container.addEventListener("mousemove", e => this.handleMouseMove(e, container)); - return container; - } - - getComputeChildren(children: HTMLCollection, e: MouseEvent): Element[] { - const computeChildren = []; - const { clientX, clientY } = e; - for (const child of children) { - const { left, top } = child.getBoundingClientRect(); - if (clientX >= left && clientY >= top) { - computeChildren.push(child); - } - } - return computeChildren; - } - - getSelectAttrs(element: Element) { - const row = ~~element.getAttribute("row"); - const column = ~~element.getAttribute("column"); - return [row, column]; - } - - handleClick(e: MouseEvent, insertTable: InsertTableHandler) { - this.toggle(this.root); - const span = (e.target as Element).closest("span[row]"); - if (!span) { - // Click between two spans - const child = this.computeChildren[this.computeChildren.length - 1]; - if (child) this.insertTable(child, insertTable); - return; - } - this.insertTable(span, insertTable); - } - - handleMouseMove(e: MouseEvent, container: Element) { - const children = container.firstElementChild.children; - this.clearSelected(this.computeChildren); - const computeChildren = this.getComputeChildren(children, e); - for (const child of computeChildren) { - child.classList && child.classList.add("ql-cell-selected"); - } - this.computeChildren = computeChildren; - this.setLabelContent(container.lastElementChild, computeChildren[computeChildren.length - 1]); - } - - hide(element: Element) { - this.clearSelected(this.computeChildren); - element && element.classList.add("ql-hidden"); - } - - insertTable(child: Element, insertTable: InsertTableHandler) { - const [row, column] = this.getSelectAttrs(child); - insertTable(row, column); - this.hide(this.root); - } - - setLabelContent(label: Element, child: Element) { - if (!child) { - label.innerHTML = "0 × 0"; - } else { - const [row, column] = this.getSelectAttrs(child); - label.innerHTML = `${row} × ${column}`; - } - } - - show(element: Element) { - this.clearSelected(this.computeChildren); - element && element.classList.remove("ql-hidden"); - } - - toggle(element: Element) { - this.clearSelected(this.computeChildren); - element && element.classList.toggle("ql-hidden"); - } -} - -export { ToolbarTable as default, TableSelect }; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/utils/clipboard-matchers.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/utils/clipboard-matchers.ts deleted file mode 100644 index eb850f4049..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/utils/clipboard-matchers.ts +++ /dev/null @@ -1,90 +0,0 @@ -// @ts-nocheck -import merge from "lodash.merge"; -import Delta from "quill-delta"; -import { TableCell } from "../formats/table"; -import type { Props } from "../types"; -import { filterWordStyle } from "./"; - -const TABLE_ATTRIBUTE = ["border", "cellspacing", "style", "class"]; - -function applyFormat(delta: Delta, format: Props | string, value?: any): Delta { - if (typeof format === "object") { - return Object.keys(format).reduce((newDelta, key) => { - return applyFormat(newDelta, key, format[key]); - }, delta); - } - return delta.reduce((newDelta, op) => { - if (op.attributes && op.attributes[format]) { - return newDelta.push(op); - } - return newDelta.insert(op.insert, merge({}, { [format]: value }, op.attributes)); - }, new Delta()); -} - -function matchTable(node: HTMLTableRowElement, delta: Delta) { - const table = (node.parentNode as HTMLElement).tagName === "TABLE" ? node.parentNode : node.parentNode.parentNode; - const rows = Array.from(table.querySelectorAll("tr")); - const row = rows.indexOf(node) + 1; - if (!node.innerHTML.replace(/\s/g, "")) return new Delta(); - return applyFormat(delta, "table-cell", row); -} - -function matchTableCell(node: HTMLTableCellElement, delta: Delta) { - const table = - (node.parentNode.parentNode as HTMLElement).tagName === "TABLE" - ? node.parentNode.parentNode - : node.parentNode.parentNode.parentNode; - const rows = Array.from(table.querySelectorAll("tr")); - const tagName = node.tagName; - const cells = Array.from(node.parentNode.querySelectorAll(tagName)); - const row = node.getAttribute("data-row") || rows.indexOf(node.parentNode as HTMLTableRowElement) + 1; - const cellId = node?.firstElementChild?.getAttribute("data-cell") || cells.indexOf(node) + 1; - if (!delta.length()) delta.insert("\n", { "table-cell": { "data-row": row } }); - delta.ops.forEach(op => { - if (op.attributes && op.attributes["table-cell"]) { - // @ts-ignore - op.attributes["table-cell"] = { ...op.attributes["table-cell"], "data-row": row }; - } - }); - return applyFormat(matchTableTh(node, delta, row), "table-cell-block", cellId); -} - -function matchTableCol(node: HTMLElement, delta: Delta) { - let span = ~~node.getAttribute("span") || 1; - const width = node.getAttribute("width"); - const newDelta = new Delta(); - while (span > 1) { - newDelta.insert("\n", { "table-col": { width } }); - span--; - } - return newDelta.concat(delta); -} - -function matchTableTemporary(node: HTMLElement, delta: Delta) { - const formats = TABLE_ATTRIBUTE.reduce((formats: Props, attr) => { - if (node.hasAttribute(attr)) { - if (attr === "class") { - formats["data-class"] = node.getAttribute(attr); - } else { - formats[attr] = filterWordStyle(node.getAttribute(attr)); - } - } - return formats; - }, {}); - return new Delta().insert("\n", { "table-temporary": formats }).concat(delta); -} - -function matchTableTh(node: HTMLTableCellElement, delta: Delta, row: string | number) { - const formats = TableCell.formats(node); - if (node.tagName === "TH") { - delta.ops.forEach(op => { - if (typeof op.insert === "string" && !op.insert.endsWith("\n")) { - op.insert += "\n"; - } - }); - return applyFormat(delta, "table-cell", { ...formats, "data-row": row }); - } - return delta; -} - -export { applyFormat, matchTable, matchTableCell, matchTableCol, matchTableTemporary }; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/utils/index.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/utils/index.ts deleted file mode 100644 index e8010713ff..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/quill-table-better/utils/index.ts +++ /dev/null @@ -1,383 +0,0 @@ -// @ts-nocheck -import Quill from "quill"; -import { COLORS, DEVIATION } from "../config"; -import TableHeader from "../formats/header"; -import TableList, { ListContainer } from "../formats/list"; -import { TableCell, TableCellBlock, TableCol } from "../formats/table"; -import type { CorrectBound, Props, TableCellChildren, TableContainer } from "../types"; - -function addDimensionsUnit(value: string) { - if (!value) return value; - const unit = value.slice(-2); // 'px' or 'em' - if (unit !== "px" && unit !== "em") { - return value + "px"; - } - return value; -} - -function convertUnitToInteger(withUnit: string) { - if (typeof withUnit !== "string" || !withUnit) return withUnit; - const unit = withUnit.slice(-2); // 'px' or 'em' - const numberPart = withUnit.slice(0, -2); - const integerPart = Math.round(parseFloat(numberPart)); - return `${integerPart}${unit}`; -} - -function createTooltip(content: string) { - const element = document.createElement("div"); - element.innerText = content; - element.classList.add("ql-table-tooltip", "ql-hidden"); - return element; -} - -function debounce(cb: Function, delay: number) { - let timer: NodeJS.Timeout = null; - return function () { - let context = this; - let args = arguments; - if (timer) clearTimeout(timer); - timer = setTimeout(function () { - cb.apply(context, args); - }, delay); - }; -} - -function filterWordStyle(s: string) { - return s.replace(/mso.*?;/g, ""); -} - -function getAlign(cellBlot: TableCell) { - const DEFAULT = "left"; - let align = null; - const blocks = cellBlot.descendants(TableCellBlock); - const lists = cellBlot.descendants(TableList); - const headers = cellBlot.descendants(TableHeader); - function getChildAlign(child: TableCellChildren): string { - for (const name of child.domNode.classList) { - if (/ql-align-/.test(name)) { - return name.split("ql-align-")[1]; - } - } - return DEFAULT; - } - function isSameValue(prev: string | null, cur: string) { - if (prev == null) return true; - return prev === cur; - } - for (const child of [...blocks, ...lists, ...headers]) { - const _align = getChildAlign(child); - if (isSameValue(align, _align)) { - align = _align; - } else { - return DEFAULT; - } - } - return align != null ? align : DEFAULT; -} - -function getCellChildBlot(cellBlot: TableCell) { - // @ts-expect-error - const [block] = cellBlot.descendant(TableCellBlock); - // @ts-expect-error - const [list] = cellBlot.descendant(ListContainer); - // @ts-expect-error - const [header] = cellBlot.descendant(TableHeader); - return block || list || header; -} - -function getCellFormats(cellBlot: TableCell): [Props, string] { - const formats = TableCell.formats(cellBlot.domNode); - const childBlot = getCellChildBlot(cellBlot); - if (!childBlot) { - const row = formats["data-row"].split("-")[1]; - return [formats, `cell-${row}`]; - } else { - const _formats = childBlot.formats()[childBlot.statics.blotName]; - const cellId = getCellId(_formats); - return [formats, cellId]; - } -} - -function getCellId(formats: string | Props) { - return formats instanceof Object ? formats["cellId"] : formats; -} - -function getClosestElement(element: HTMLElement, selector: string) { - return element.closest(selector); -} - -function getComputeBounds(startCorrectBounds: CorrectBound, endCorrectBounds: CorrectBound) { - const left = Math.min(startCorrectBounds.left, endCorrectBounds.left); - const right = Math.max(startCorrectBounds.right, endCorrectBounds.right); - const top = Math.min(startCorrectBounds.top, endCorrectBounds.top); - const bottom = Math.max(startCorrectBounds.bottom, endCorrectBounds.bottom); - return { left, right, top, bottom }; -} - -function getComputeSelectedCols(computeBounds: CorrectBound, table: Element, container: Element) { - const tableParchment = Quill.find(table) as TableContainer; - const cols = tableParchment.descendants(TableCol); - let correctLeft = 0; - return cols.reduce((selectedCols: Element[], col: TableCol) => { - const { left, width } = getCorrectBounds(col.domNode, container); - correctLeft = correctLeft ? correctLeft : left; - if (correctLeft + DEVIATION >= computeBounds.left && correctLeft - DEVIATION + width <= computeBounds.right) { - selectedCols.push(col.domNode); - } - correctLeft += width; - return selectedCols; - }, []); -} - -function getComputeSelectedTds( - computeBounds: CorrectBound, - table: Element, - container: Element, - type?: string -): Element[] { - const tableParchment = Quill.find(table) as TableContainer; - const tableCells = tableParchment.descendants(TableCell); - return tableCells.reduce((selectedTds: Element[], tableCell: TableCell) => { - const { left, top, width, height } = getCorrectBounds(tableCell.domNode, container); - switch (type) { - case "column": - if (left + DEVIATION >= computeBounds.left && left - DEVIATION + width <= computeBounds.right) { - selectedTds.push(tableCell.domNode); - } else if (left + DEVIATION < computeBounds.right && computeBounds.right < left - DEVIATION + width) { - selectedTds.push(tableCell.domNode); - } else if (computeBounds.left > left + DEVIATION && computeBounds.left < left - DEVIATION + width) { - selectedTds.push(tableCell.domNode); - } - break; - case "row": - break; - default: - if ( - left + DEVIATION >= computeBounds.left && - left - DEVIATION + width <= computeBounds.right && - top + DEVIATION >= computeBounds.top && - top - DEVIATION + height <= computeBounds.bottom - ) { - selectedTds.push(tableCell.domNode); - } - break; - } - return selectedTds; - }, []); -} - -function getCopyTd(html: string) { - return html - .replace(/data-[a-z]+="[^"]*"/g, "") - .replace(/class="[^"]*"/g, collapse => { - return collapse - .replace("ql-cell-selected", "") - .replace("ql-cell-focused", "") - .replace("ql-table-block", ""); - }) - .replace(/class="\s*"/g, ""); -} - -function getCorrectBounds(target: Element, container: Element) { - const targetBounds = target.getBoundingClientRect(); - const containerBounds = container.getBoundingClientRect(); - const left = targetBounds.left - containerBounds.left - container.scrollLeft; - const top = targetBounds.top - containerBounds.top - container.scrollTop; - const width = targetBounds.width; - const height = targetBounds.height; - return { - left, - top, - width, - height, - right: left + width, - bottom: top + height - }; -} - -function getCorrectCellBlot(blot: TableCell | TableCellChildren): TableCell | null { - while (blot) { - if (blot.statics.blotName === TableCell.blotName) { - // @ts-ignore - return blot; - } - // @ts-expect-error - blot = blot.parent; - } - return null; -} - -function getElementStyle(node: HTMLElement, rules: string[]) { - const computedStyle = getComputedStyle(node); - const style = node.style; - return rules.reduce((styles: Props, rule: string) => { - styles[rule] = rgbToHex(style.getPropertyValue(rule) || computedStyle.getPropertyValue(rule)); - return styles; - }, {}); -} - -function isDimensions(key: string) { - if (key.endsWith("width") || key.endsWith("height")) return true; - return false; -} - -function isSimpleColor(color: string) { - for (const col of COLORS) { - if (col === color) return true; - } - return false; -} - -function isValidColor(color: string) { - if (!color) return true; - const hexRegex = /^#([A-Fa-f0-9]{3,6})$/; - const rgbRegex = /^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/; - // const rgbaRegex = /^rgba\((\d{1,3}), (\d{1,3}), (\d{1,3}), (\d{1,3})\)$/; - if (hexRegex.test(color)) { - return true; - } else if (rgbRegex.test(color)) { - return true; - } - return isSimpleColor(color); -} - -function isValidDimensions(value: string) { - if (!value) return true; - const unit = value.slice(-2); // 'px' or 'em' - if (unit !== "px" && unit !== "em") { - return !/[a-z]/.test(unit) && !isNaN(parseFloat(unit)); - } - return true; -} - -function removeElementProperty(node: HTMLElement, properties: string[]) { - for (const property of properties) { - node.style.removeProperty(property); - } -} - -function rgbToHex(value: string) { - if (value.startsWith("rgba(")) return rgbaToHex(value); - if (!value.startsWith("rgb(")) return value; - value = value.replace(/^[^\d]+/, "").replace(/[^\d]+$/, ""); - const hex = value - .split(",") - .map(component => `00${parseInt(component, 10).toString(16)}`.slice(-2)) - .join(""); - return `#${hex}`; -} - -function rgbaToHex(value: string) { - value = value.replace(/^[^\d]+/, "").replace(/[^\d]+$/, ""); - const r = Math.round(+value[0]); - const g = Math.round(+value[1]); - const b = Math.round(+value[2]); - const a = Math.round(+value[3] * 255) - .toString(16) - .toUpperCase() - .padStart(2, "0"); - return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1) + a; -} - -function setElementAttribute(node: Element, attributes: Props) { - for (const attribute in attributes) { - node.setAttribute(attribute, attributes[attribute]); - } -} - -function setElementProperty(node: HTMLElement, properties: Props) { - const style = node.style; - if (!style) { - node.setAttribute("style", properties.toString()); - return; - } - for (const propertyName in properties) { - style.setProperty(propertyName, properties[propertyName]); - } -} - -function throttle(cb: Function, delay: number) { - let last = 0; - return function () { - let context = this; - let args = arguments; - let now = +new Date(); - if (now - last >= delay) { - last = now; - cb.apply(context, args); - } - }; -} - -function throttleStrong(cb: Function, delay: number) { - let last = 0, - timer: NodeJS.Timeout = null; - return function () { - let context = this; - let args = arguments; - let now = +new Date(); - if (now - last < delay) { - clearTimeout(timer); - timer = setTimeout(function () { - last = now; - cb.apply(context, args); - }, delay); - } else { - last = now; - cb.apply(context, args); - } - }; -} - -function updateTableWidth(table: HTMLElement, tableBounds: CorrectBound, change: number) { - const tableBlot = Quill.find(table) as TableContainer; - if (!tableBlot) return; - const colgroup = tableBlot.colgroup(); - const temporary = tableBlot.temporary(); - if (colgroup) { - let _width = 0; - const cols = colgroup.domNode.querySelectorAll("col"); - for (const col of cols) { - const width = ~~col.getAttribute("width"); - _width += width; - } - setElementProperty(temporary.domNode, { - width: `${_width}px` - }); - } else { - setElementProperty(temporary.domNode, { - width: `${~~(tableBounds.width + change)}px` - }); - } -} - -export { - addDimensionsUnit, - convertUnitToInteger, - createTooltip, - debounce, - filterWordStyle, - getAlign, - getCellChildBlot, - getCellFormats, - getCellId, - getClosestElement, - getComputeBounds, - getComputeSelectedCols, - getComputeSelectedTds, - getCopyTd, - getCorrectBounds, - getCorrectCellBlot, - getElementStyle, - isDimensions, - isValidColor, - isValidDimensions, - removeElementProperty, - rgbaToHex, - rgbToHex, - setElementAttribute, - setElementProperty, - throttle, - throttleStrong, - updateTableWidth -}; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/resizeModuleConfig.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/resizeModuleConfig.ts deleted file mode 100644 index c6a8bf24f7..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/resizeModuleConfig.ts +++ /dev/null @@ -1,71 +0,0 @@ -import Quill from "quill"; -import QuillResize from "quill-resize-module"; -import { ACTION_DISPATCHER } from "../helpers"; -import MxResizeToolbar from "../modules/resizeToolbar"; -import MxResize from "../modules/resize"; - -export const RESIZE_MODULE_CONFIG = { - modules: ["DisplaySize", MxResizeToolbar, MxResize, "Keyboard"], - tools: [ - { - text: "Edit Image", - className: "icons icon-Image", - verify(activeEle: HTMLElement) { - return activeEle && activeEle.tagName === "IMG"; - }, - handler( - this: { quill: Quill; resizer: typeof QuillResize }, - _evt: MouseEvent, - _button: HTMLElement, - activeEle: HTMLImageElement - ) { - const imageInfo = { - alt: activeEle.alt || "", - src: activeEle.src, - width: activeEle.width, - height: activeEle.height, - type: "image" - }; - this.resizer.handleEdit(); - this.quill.emitter.emit(ACTION_DISPATCHER, imageInfo); - } - }, - { - text: "Edit Video", - className: "icons icon-Film", - verify(activeEle: HTMLElement) { - return activeEle && activeEle.tagName === "IFRAME" && activeEle.classList.contains("ql-video"); - }, - handler( - this: typeof QuillResize.Modules.Base, - _evt: MouseEvent, - _button: HTMLElement, - activeEle: HTMLIFrameElement - ) { - const videoInfo = { - src: activeEle.src, - width: activeEle.width, - height: activeEle.height, - type: "video" - }; - this.resizer.handleEdit(); - this.quill.emitter.emit(ACTION_DISPATCHER, videoInfo); - } - } - ], - parchment: { - image: { - attribute: ["width", "height"] - }, - video: { - attribute: ["width", "height"] - } - } -}; - -export function getResizeModuleConfig(isReadOnly?: boolean): Record | undefined { - if (isReadOnly) { - return {}; - } - return { resize: RESIZE_MODULE_CONFIG }; -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/softBreak.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/softBreak.ts deleted file mode 100644 index c37942ef43..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/softBreak.ts +++ /dev/null @@ -1,17 +0,0 @@ -// import { BlockEmbed } from "quill/blots/block"; -import { EmbedBlot } from "parchment"; -/** - * custom video link handler, allowing width and height config - */ -class SoftBreak extends EmbedBlot { - static create(_value: unknown): Element { - const node = super.create() as HTMLElement; - return node; - } -} - -// SoftBreak.scope = Scope.INLINE_BLOT; -SoftBreak.blotName = "softbreak"; -SoftBreak.tagName = "BR"; - -export default SoftBreak; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/video.scss b/packages/pluggableWidgets/rich-text-web/src/utils/formats/video.scss deleted file mode 100644 index 1f7e0a8e3d..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/video.scss +++ /dev/null @@ -1,5 +0,0 @@ -.widget-rich-text { - .ql-editor .ql-video { - display: inline-block; - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/video.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/video.ts deleted file mode 100644 index db83a39b1f..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/video.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Scope } from "parchment"; -import Video from "quill/formats/video"; -import { videoConfigType } from "../formats"; -import { matchPattern } from "../videoUrlPattern"; -import "./video.scss"; - -/** - * custom video link handler, allowing width and height config - */ -class CustomVideo extends Video { - html(): string { - return this.domNode.outerHTML; - } - - length(): number { - // asume that video has child to be able to delete embedded video - return 2; - } - - static create(value: unknown): Element { - if ((value as videoConfigType)?.src !== undefined) { - const videoConfig = value as videoConfigType; - const urlPatterns = matchPattern(videoConfig.src); - const node = super.create(urlPatterns?.url || videoConfig.src) as HTMLElement; - return node; - } else { - // @ts-expect-error type mismatch expected - return super.create(value); - } - } -} - -CustomVideo.scope = Scope.INLINE_BLOT; - -export default CustomVideo; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/whiteSpace.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/whiteSpace.ts deleted file mode 100644 index 5726c3ebeb..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/whiteSpace.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Scope, StyleAttributor } from "parchment"; - -const config = { - scope: Scope.INLINE, - whitelist: [ - "normal", - "pre", - "pre-wrap", - "pre-line", - "nowrap", - "wrap", - "break-spaces", - "collapse", - "inherit", - "initial", - "revert", - "unset" - ] -}; - -export class WhiteSpaceStyleAttributor extends StyleAttributor {} -export const WhiteSpaceStyle = new WhiteSpaceStyleAttributor("whitespace", "white-space", config); diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts b/packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts index 7f60084c39..c7ed30f02d 100644 --- a/packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts +++ b/packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts @@ -1,6 +1,7 @@ import { CSSProperties } from "react"; import { RichTextContainerProps } from "typings/RichTextProps"; +export const INDENT_MAGIC_NUMBER = 3; export const ACTION_DISPATCHER = "ACTION_DISPATCHER"; function getHeightScale(height: number, heightUnit: "pixels" | "percentageOfParent" | "percentageOfView"): string { @@ -32,3 +33,44 @@ export function constructWrapperStyle(props: RichTextContainerProps): CSSPropert return wrapperStyle; } + +export function normalizeStyleAndClassAttribute(doc: Document, styleDataFormat: "inline" | "class"): void { + if (styleDataFormat === "class") { + const allIndentLeftElements = doc.querySelectorAll("[style*=padding-left]"); + const allIndentRightElements = doc.querySelectorAll("[style*=padding-right]"); + allIndentLeftElements.forEach(element => { + const paddingLeft = (element as HTMLElement).style.paddingLeft || "0em"; + const indentValue = parseInt(paddingLeft.replace("px", "").replace("em", ""), 10); + if (indentValue) { + const indentClassValue = Math.round(indentValue / INDENT_MAGIC_NUMBER); + element.classList.add(`ql-indent-${indentClassValue}`); + (element as HTMLElement).style.removeProperty("padding-left"); + } + }); + allIndentRightElements.forEach(element => { + const paddingRight = (element as HTMLElement).style.paddingRight || "0em"; + const indentValue = parseInt(paddingRight.replace("px", "").replace("em", ""), 10); + if (indentValue) { + const indentClassValue = Math.round(indentValue / INDENT_MAGIC_NUMBER); + element.classList.add(`ql-indent-${indentClassValue}`); + (element as HTMLElement).style.removeProperty("padding-right"); + } + }); + } else if (styleDataFormat === "inline") { + const allIndentsElements = doc.querySelectorAll("[class*=ql-indent-]"); + allIndentsElements.forEach(element => { + const indentClass = Array.from(element.classList).find(className => className.startsWith("ql-indent-")); + if (indentClass) { + const indentValue = parseInt(indentClass.replace("ql-indent-", ""), 10); + if (indentValue) { + if (element.classList.contains("ql-direction-rtl")) { + (element as HTMLElement).style.paddingRight = `${indentValue * INDENT_MAGIC_NUMBER}em`; + } else { + (element as HTMLElement).style.paddingLeft = `${indentValue * INDENT_MAGIC_NUMBER}em`; + } + } + element.classList.remove(indentClass); + } + }); + } +} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/clipboard.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/clipboard.ts deleted file mode 100644 index 0b98faa349..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/clipboard.ts +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Custom Clipboard module to override Quill's default clipboard behavior - * to better handle pasting from various sources. - * https://github.com/slab/quill/blob/main/packages/quill/src/modules/clipboard.ts - */ - -import { EmbedBlot, type ScrollBlot } from "parchment"; -import Quill, { Delta } from "quill"; -import Clipboard, { matchNewline } from "quill/modules/clipboard"; - -export default class CustomClipboard extends Clipboard { - constructor(quill: Quill, options: any) { - super(quill, options); - // remove default CLIPBOARD_CONFIG list matchers for ol and ul - // https://github.com/slab/quill/blob/539cbffd0a13b18e9c65eb84dd35e6596e403158/packages/quill/src/modules/clipboard.ts#L32 - this.matchers = this.matchers.filter( - matcher => matcher[0] !== "ol, ul" && matcher[0] !== Node.TEXT_NODE && matcher[0] !== "br" - ); - // adding back, we do not actually want to remove newline matching - this.matchers.unshift([Node.TEXT_NODE, matchNewline]); - // add custom text matcher to better handle spaces and newlines - this.matchers.unshift([Node.TEXT_NODE, customMatchText]); - // add custom list matchers for ol and ul to allow custom list types (lower-alpha, lower-roman, etc.) - this.addMatcher("ol, ul", matchList); - this.addMatcher("a", matchLink); - } -} - -function isLine(node: Node, scroll: ScrollBlot): any { - if (!(node instanceof Element)) return false; - const match = scroll.query(node); - // @ts-expect-error prototype not exist on match - if (match && match.prototype instanceof EmbedBlot) return false; - - return [ - "address", - "article", - "blockquote", - "canvas", - "dd", - "div", - "dl", - "dt", - "fieldset", - "figcaption", - "figure", - "footer", - "form", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "header", - "iframe", - "li", - "main", - "nav", - "ol", - "output", - "p", - "pre", - "section", - "table", - "td", - "tr", - "ul", - "video" - ].includes(node.tagName.toLowerCase()); -} - -function isBetweenInlineElements(node: HTMLElement, scroll: ScrollBlot): boolean | null { - return ( - node.previousElementSibling && - node.nextElementSibling && - !isLine(node.previousElementSibling, scroll) && - !isLine(node.nextElementSibling, scroll) - ); -} - -const preNodes = new WeakMap(); -function isPre(node: Node | null): boolean { - if (node == null) return false; - if (!preNodes.has(node)) { - // @ts-expect-error tagName not exist on Node - if (node.tagName === "PRE") { - preNodes.set(node, true); - } else { - preNodes.set(node, isPre(node.parentNode)); - } - } - return preNodes.get(node); -} - -function customMatchText(node: HTMLElement, delta: Delta, scroll: ScrollBlot): Delta { - // @ts-expect-error data not exist on node - let text = node.data as string; - // Word represents empty line with   - if (node.parentElement?.tagName === "O:P") { - return delta.insert(text.trim()); - } - if (!isPre(node)) { - if (text.trim().length === 0 && text.includes("\n") && !isBetweenInlineElements(node, scroll)) { - return delta; - } - // collapse consecutive spaces into one - text = text.replace(/ {2,}/g, " "); - if ( - (node.nextSibling == null && node.parentElement != null && isLine(node.parentElement, scroll)) || - (node.nextSibling instanceof Element && isLine(node.nextSibling, scroll)) - ) { - // block structure means we don't need trailing space - text = text.replace(/ $/, ""); - } - } - return delta.insert(text); -} - -function matchLink(node: HTMLElement, _delta: Delta): Delta { - const href = node.getAttribute("href"); - const text = node.textContent || href || ""; - const title = node.getAttribute("title"); - const target = node.getAttribute("target"); - const value = { href, text, title, target }; - return new Delta().insert(text, { link: value }); -} - -function matchList(node: HTMLElement, delta: Delta): Delta { - const format = "list"; - let list = "ordered"; - const element = node as HTMLUListElement; - const checkedAttr = element.getAttribute("data-checked"); - if (checkedAttr) { - list = checkedAttr === "true" ? "checked" : "unchecked"; - } else { - const listStyleType = element.style.listStyleType; - if (listStyleType) { - if (listStyleType === "disc") { - // disc is standard list type, convert to bullet - list = "bullet"; - } else if (listStyleType === "decimal") { - // list decimal type is dependant on indent level, convert to standard ordered list - list = "ordered"; - } else { - list = listStyleType; - } - } else { - list = element.tagName === "OL" ? "ordered" : "bullet"; - } - } - - // apply list format to delta - return delta.reduce((newDelta, op) => { - if (!op.insert) return newDelta; - if (op.attributes && op.attributes[format]) { - return newDelta.push(op); - } - const formats = list ? { [format]: list } : {}; - return newDelta.insert(op.insert, { ...formats, ...op.attributes }); - }, new Delta()); -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/keyboard.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/keyboard.ts deleted file mode 100644 index cb5706e02d..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/keyboard.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - addIndentText, - enterKeyKeyboardHandler, - exitFullscreenKeyboardHandler, - gotoStatusBarKeyboardHandler, - gotoToolbarKeyboardHandler, - moveIndent, - moveOutdent, - movePrevFocus, - shiftEnterKeyKeyboardHandler -} from "./toolbarHandlers"; -import QuillTableBetter from "../formats/quill-table-better/quill-table-better"; - -export function getKeyboardBindings(): Record { - const defaultBindings: Record = { - enter: { - key: "Enter", - handler: enterKeyKeyboardHandler - }, - shiftEnter: { - key: "Enter", - shiftKey: true, - collapsed: true, - handler: shiftEnterKeyKeyboardHandler - }, - focusTab: { - key: "F10", - altKey: true, - collapsed: true, - handler: gotoToolbarKeyboardHandler - }, - shiftTab: { - key: "Tab", - shiftKey: true, - handler: movePrevFocus - }, - outdent: { - key: "Tab", - shiftKey: true, - format: ["blockquote", "indent", "list"], - // highlight tab or tab at beginning of list, indent or blockquote - handler: moveOutdent - }, - indent: { - // highlight tab or tab at beginning of list, indent or blockquote - key: "Tab", - format: ["blockquote", "indent", "list"], - handler: moveIndent - }, - nextFocusTab: { - key: "F11", - altKey: true, - collapsed: true, - handler: gotoStatusBarKeyboardHandler - }, - escape: { - key: "Escape", - handler: exitFullscreenKeyboardHandler - }, - tab: { - key: "Tab", - handler: addIndentText - }, - ...QuillTableBetter.keyboardBindings - }; - return defaultBindings; -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/resize.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/resize.ts deleted file mode 100644 index 8c88b4ee8d..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/resize.ts +++ /dev/null @@ -1,103 +0,0 @@ -import QuillResize from "quill-resize-module"; - -type ImageSize = { - width?: number; - height?: number; -}; - -type ImageSizeWithUnit = { - width?: string; - height?: string; -}; - -type ImageSizes = ImageSize | ImageSizeWithUnit; - -type LimitConfig = { - ratio?: number; - minWidth?: number; - maxWidth?: number; - minHeight?: number; - maxHeight?: number; - unit?: string; -}; - -type CalculateSizeEvent = { clientX: number; clientY: number }; - -export default class MxResize extends QuillResize.Modules.Resize { - // modified from https://github.com/mudoo/quill-resize-module/blob/master/src/modules/Resize.js - calcSize(evt: CalculateSizeEvent, limit: LimitConfig = {}): ImageSizes { - // update size - const deltaX = evt.clientX - this.dragStartX; - const deltaY = evt.clientY - this.dragStartY; - - const size: ImageSize = {}; - let direction = 1; - - (this.blotOptions.attribute || ["width"]).forEach((key: "width" | "height") => { - size[key] = this.preDragSize[key]; - }); - - // modification to check if height attribute is exist from current image - // this is to maintain ratio by resizing width only - const allowHeight = - this.activeEle.getAttribute("height") !== undefined && this.activeEle.getAttribute("height") !== null; - if (!allowHeight) { - delete size.height; - } - - // left-side - if (this.dragBox === this.boxes[0] || this.dragBox === this.boxes[3]) { - direction = -1; - } - - if (size.width) { - size.width = Math.round(this.preDragSize.width + deltaX * direction); - } - if (size.height) { - size.height = Math.round(this.preDragSize.height + deltaY * direction); - } - - let { width, height } = size; - - // keep ratio - if (limit.ratio) { - let limitHeight; - if (limit.minWidth) width = Math.max(limit.minWidth, width!); - if (limit.maxWidth) width = Math.min(limit.maxWidth, width!); - - height = width! * limit.ratio; - - if (limit.minHeight && height < limit.minHeight) { - limitHeight = true; - height = limit.minHeight; - } - if (limit.maxHeight && height > limit.maxHeight) { - limitHeight = true; - height = limit.maxHeight; - } - - if (limitHeight) { - width = height / limit.ratio; - } - } else { - if (size.width) { - if (limit.minWidth) width = Math.max(limit.minWidth, width!); - if (limit.maxWidth) width = Math.min(limit.maxWidth, width!); - } - if (size.height) { - if (limit.minHeight) height = Math.max(limit.minHeight, height!); - if (limit.maxHeight) height = Math.min(limit.maxHeight, height!); - } - } - const res: ImageSizes = {}; - - if (limit.unit) { - if (width) res.width = width + "px"; - if (height) res.height = height + "px"; - } else { - if (width) res.width = width; - if (height) res.height = height; - } - return res; - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/resizeToolbar.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/resizeToolbar.ts deleted file mode 100644 index 54311977a8..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/resizeToolbar.ts +++ /dev/null @@ -1,38 +0,0 @@ -import QuillResize from "quill-resize-module"; - -type ToolbarTool = { - text: string; - className: string; - verify: (activeEle: HTMLElement) => boolean; - handler: ( - this: typeof QuillResize.Modules.Base, - _evt: MouseEvent, - _button: HTMLElement, - activeEle: HTMLIFrameElement - ) => void; -}; - -// eslint-disable-next-line no-unsafe-optional-chaining -export default class MxResizeToolbar extends QuillResize.Modules?.Toolbar { - _addToolbarButtons(): void { - const buttons: HTMLButtonElement[] = []; - this.options.tools.forEach((tool: ToolbarTool) => { - if (tool.verify && tool.verify.call(this, this.activeEle) === false) { - return; - } - - const button = document.createElement("button"); - button.className = tool.className; - buttons.push(button); - button.setAttribute("aria-label", tool.text); - button.setAttribute("type", "button"); - - button.addEventListener("click", evt => { - tool.handler.call(this, evt, button, this.activeEle); - // image may change position; redraw drag handles - this.requestUpdate(); - }); - this.toolbar.appendChild(button); - }); - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/scroll.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/scroll.ts deleted file mode 100644 index 475b6e5121..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/scroll.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Scroll from "quill/blots/scroll"; -import MxBlock from "../formats/block"; - -export default class MxScroll extends Scroll { - trimEnd(): void { - // remove multiple empty line placed in the end - // this is usually occurs with multi level ListContainer - if ( - this.children.tail && - MxBlock.IsEmptyBlock(this.children.tail) && - MxBlock.IsEmptyBlock(this.children.tail.prev) - ) { - this.children.tail.parent.removeChild(this.children.tail); - } - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/toolbarHandlers.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/toolbarHandlers.ts deleted file mode 100644 index 98565d9007..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/toolbarHandlers.ts +++ /dev/null @@ -1,183 +0,0 @@ -import Quill from "quill"; -import Delta from "quill-delta"; -import { MutableRefObject } from "react"; -import { Range } from "quill/core/selection"; -import Keyboard, { Context } from "quill/modules/keyboard"; -import { Scope } from "parchment"; -import { ACTION_DISPATCHER } from "../helpers"; -import { SET_FULLSCREEN_ACTION } from "../../store/store"; - -function returnWithStopPropagation(context: Context): boolean { - context.event.stopPropagation(); - context.event.preventDefault(); - return true; -} - -/** - * give custom indent handler to use our custom "indent-left" and "indent-right" formats (formats/indent.ts) - */ -export function getIndentHandler(ref: MutableRefObject): (value: any) => void { - const customIndentHandler = (value: any): void => { - const range = ref.current?.getSelection(); - // @ts-expect-error type error expected - const formats = ref.current?.getFormat(range); - if (formats) { - const indent = parseInt((formats.indent as string) || "0", 10); - if (value === "+1" || value === "-1") { - let modifier = value === "+1" ? 1 : -1; - const formatHandler = formats.list - ? "indent" - : formats.direction === "rtl" - ? "indent-right" - : "indent-left"; - if (formats.direction === "rtl") { - modifier *= -1; - } - ref.current?.format(formatHandler, indent + modifier, Quill.sources.USER); - } - } - }; - - return customIndentHandler; -} - -/** - * copied with modification from "handleEnter" function in : - * https://github.com/slab/quill/blob/main/packages/quill/src/modules/keyboard.ts - */ -export function enterKeyKeyboardHandler(this: Keyboard, range: Range, context: Context): any { - const lineFormats = Object.keys(context.format).reduce((formats: Record, format) => { - if (this.quill.scroll.query(format, Scope.BLOCK) && !Array.isArray(context.format[format])) { - formats[format] = context.format[format]; - } - return formats; - }, {}); - - const delta = new Delta().retain(range.index).delete(range.length).insert("\n", lineFormats); - this.quill.updateContents(delta, Quill.sources.USER); - this.quill.setSelection(range.index + 1, Quill.sources.SILENT); - - /** - * NOTE: Modified from default handler to maintain format on new line - * Applies previous formats on the new line. This was dropped in - * https://github.com/slab/quill/commit/ba5461634caa8e24641b687f2d1a8768abfec640 - */ - Object.keys(context.format).forEach(name => { - if (lineFormats[name] != null) { - return; - } - if (Array.isArray(context.format[name])) { - return; - } - if (name === "code" || name === "link") { - return; - } - this.quill.format(name, context.format[name], Quill.sources.USER); - }); -} - -export function shiftEnterKeyKeyboardHandler(this: Keyboard, range: Range, context: Context): any { - if (context.format.table) { - return true; - } - - if (context.suffix === "") { - // if it is on the end of block - // we need to insert two soft breaks to create a new line within the same block - // this is to override /n behavior - this.quill.insertEmbed(range.index, "softbreak", true, Quill.sources.USER); - } - this.quill.insertEmbed(range.index, "softbreak", true, Quill.sources.USER); - this.quill.setSelection(range.index + 1, Quill.sources.SILENT); - return false; -} - -export function movePrevFocus(this: Keyboard, range: Range, context: Context): any { - if (context.format.table) { - return returnWithStopPropagation(context); - } else if (context.collapsed) { - if (context.format.indent || context.format.list || context.format.blockquote) { - return returnWithStopPropagation(context); - } - } - - gotoToolbarKeyboardHandler.call(this, range, context); - return returnWithStopPropagation(context); -} - -// Copied from https://github.com/slab/quill/blob/539cbffd0a13b18e9c65eb84dd35e6596e403158/packages/quill/src/modules/keyboard.ts#L372 -// with added stopPropagation and preventDefault -export function moveOutdent(this: Keyboard, _range: Range, context: Context): any { - if (context.collapsed && context.offset !== 0) { - return returnWithStopPropagation(context); - } - this.quill.format("indent", "-1", Quill.sources.USER); - return !returnWithStopPropagation(context); -} - -// Copied from https://github.com/slab/quill/blob/539cbffd0a13b18e9c65eb84dd35e6596e403158/packages/quill/src/modules/keyboard.ts#L372 -// with added stopPropagation and preventDefault -export function moveIndent(this: Keyboard, _range: Range, context: Context): any { - if (context.collapsed && context.offset !== 0) { - return returnWithStopPropagation(context); - } - this.quill.format("indent", "+1", Quill.sources.USER); - return !returnWithStopPropagation(context); -} - -// focus to first toolbar button -export function gotoToolbarKeyboardHandler(this: Keyboard, _range: Range, context: Context): any { - if (context.format.table) { - return true; - } - - const toolbar = this.quill.container.parentElement?.parentElement?.querySelector(".widget-rich-text-toolbar"); - if (toolbar) { - (toolbar?.querySelector(".ql-formats button") as HTMLElement)?.focus(); - } else { - // "widget-rich-text form-control" - this.quill.container.parentElement?.parentElement?.parentElement?.focus(); - } -} - -// move to next element focus : status bar button (exit editor) -export function gotoStatusBarKeyboardHandler(this: Keyboard, _range: Range, context: Context): boolean | void { - if (context.format.table) { - return true; - } - - const statusBar = this.quill.container.parentElement?.parentElement?.nextElementSibling; - if (statusBar) { - (statusBar as HTMLElement)?.focus(); - } else { - // "widget-rich-text form-control" - this.quill.container.parentElement?.parentElement?.parentElement?.focus(); - } -} - -// default quill tab handler -// https://github.com/slab/quill/blob/539cbffd0a13b18e9c65eb84dd35e6596e403158/packages/quill/src/modules/keyboard.ts#L412 -// but modified to add stopPropagation and preventDefault -export function addIndentText(this: Keyboard, range: Range, context: Context): boolean | void { - if (context.format.table) { - return true; - } - if (context.collapsed && context.offset === 0) { - return moveIndent.call(this, range, context); - } else { - this.quill.history.cutoff(); - const delta = new Delta().retain(range.index).delete(range.length).insert("\t"); - this.quill.updateContents(delta, Quill.sources.USER); - this.quill.history.cutoff(); - this.quill.setSelection(range.index + 1, Quill.sources.SILENT); - return returnWithStopPropagation(context); - } -} - -/** - * Keyboard handler for exiting fullscreen mode when the Escape key is pressed - */ -export function exitFullscreenKeyboardHandler(this: Keyboard, _range: Range, _context: Context): boolean | void { - this.quill.emitter.emit(ACTION_DISPATCHER, { type: SET_FULLSCREEN_ACTION, value: false }); - return true; -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/uploader.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/uploader.ts deleted file mode 100644 index 2ff058eb8c..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/uploader.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Range } from "quill/core/selection"; -import Uploader from "quill/modules/uploader"; -import { ACTION_DISPATCHER } from "../helpers"; - -class MxUploader extends Uploader { - protected useEntityUpload: boolean = false; - - setEntityUpload(useEntityUpload: boolean): void { - this.useEntityUpload = useEntityUpload; - } - - upload(range: Range, files: FileList | File[]): void { - if (!this.quill.scroll.query("image")) { - return; - } - if (this.useEntityUpload) { - // If entity upload is enabled, the file will be handled by external widget's upload handler. - const dataTransfer = new DataTransfer(); - Array.from(files).forEach(file => { - if (file && this.options.mimetypes?.includes(file.type)) { - dataTransfer.items.add(file); - } - }); - const imageInfo = { - type: "image", - files: dataTransfer.files - }; - this.quill.emitter.emit(ACTION_DISPATCHER, imageInfo); - } else { - super.upload.call(this, range, files); - } - } -} - -export default MxUploader; diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/themes/mxTheme.ts b/packages/pluggableWidgets/rich-text-web/src/utils/themes/mxTheme.ts deleted file mode 100644 index 8eab0c31ed..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/themes/mxTheme.ts +++ /dev/null @@ -1,143 +0,0 @@ -import SnowTheme from "quill/themes/snow"; -import MxTooltip from "./mxTooltip"; -import icons from "quill/ui/icons"; -import Toolbar from "quill/modules/toolbar"; -import { Context } from "quill/modules/keyboard"; -import Picker from "quill/ui/picker"; -import { Range } from "quill"; - -/** - * Override quill's current theme. - */ -export default class MendixTheme extends SnowTheme { - fontPicker?: Picker = undefined; - fontSizePicker?: Picker = undefined; - headerPicker?: Picker = undefined; - defaultFontFamily = "helvetica"; - defaultFontSize = "14px"; - buildPickers(selects: NodeListOf, icons: Record>): void { - super.buildPickers(selects, icons); - - this.pickers.forEach(picker => { - const pickerLabel = picker.container.querySelector(".ql-picker-label"); - if (picker.container.classList.contains("ql-size")) { - picker.selectItem( - picker.container.querySelector(`[data-value="${this.defaultFontSize}"]`) as HTMLElement, - false - ); - this.fontSizePicker = picker; - } - if (picker.container.classList.contains("ql-font")) { - picker.selectItem( - picker.container.querySelector(`[data-value=${this.defaultFontFamily}]`) as HTMLElement, - false - ); - this.fontPicker = picker; - } - - if (picker.container.classList.contains("ql-header")) { - picker.selectItem( - picker.container.querySelector(`.ql-picker-item:not([data-value])`) as HTMLElement, - false - ); - this.headerPicker = picker; - } - if (pickerLabel) { - pickerLabel.setAttribute("tabindex", "-1"); - } - }); - } - - /** - * copied from https://github.com/slab/quill/blob/main/packages/quill/src/themes/snow.ts - * with modification to replace tooltip with MxTooltip - * @param toolbar - */ - extendToolbar(toolbar: Toolbar): void { - if (toolbar.container != null) { - toolbar.container.classList.add("ql-snow"); - this.buildButtons(toolbar.container.querySelectorAll("button"), icons); - this.buildPickers(toolbar.container.querySelectorAll("select"), icons); - // @ts-expect-error unknown type - this.tooltip = new MxTooltip(this.quill, this.options.bounds); - if (toolbar.container.querySelector(".ql-link")) { - this.quill.keyboard.addBinding({ key: "k", shortKey: true }, (_range: Range, context: Context) => { - toolbar.handlers.link.call(toolbar, !context.format.link); - }); - } - } - } - - /** updating font picker selected item based on current's selection font */ - updatePicker(range: Range): void { - if (this.pickers) { - const currentRange = range || { index: 0, length: 0 }; - - const format = this.quill.getFormat(currentRange.index, currentRange.length); - const font = format ? (format.font as string) : undefined; - this.updateFontPicker(font || this.defaultFontFamily); - const fontSize = format ? (format.size as string) : undefined; - this.updateFontSizePicker(fontSize || this.defaultFontSize); - const header = format ? (format.header as string) : undefined; - this.updateHeaderPicker(header); - } - } - - updateFontPicker(font: string): void { - if (this.pickers) { - if (!this.fontPicker) { - this.fontPicker = this.pickers.find(picker => picker.container.classList.contains("ql-font")); - } - - const currentOption = this.fontPicker?.container.querySelector(`[data-value=${font}]`); - if (currentOption) { - this.fontPicker?.selectItem(currentOption as HTMLElement, false); - } - } - } - - updateFontSizePicker(fontSize: string): void { - if (this.pickers) { - if (!this.fontSizePicker) { - this.fontSizePicker = this.pickers.find(picker => picker.container.classList.contains("ql-size")); - } - - const currentOption = this.fontSizePicker?.container.querySelector(`[data-value="${fontSize}"]`); - if (currentOption) { - this.fontSizePicker?.selectItem(currentOption as HTMLElement, false); - } - } - } - - updateHeaderPicker(header?: string): void { - if (this.pickers) { - if (!this.headerPicker) { - this.headerPicker = this.pickers.find(picker => picker.container.classList.contains("ql-header")); - } - if (!header) { - const currentOption = this.headerPicker?.container.querySelector(`.ql-picker-item:not([data-value])`); - this.headerPicker?.selectItem(currentOption as HTMLElement, false); - } - } - } - - updateDefaultFontFamily(fontFamily?: string): void { - if (fontFamily && fontFamily.trim().length > 0) { - this.defaultFontFamily = fontFamily; - } else { - this.defaultFontFamily = "helvetica"; - } - - this.updateFontPicker(this.defaultFontFamily); - } - - updateDefaultFontSize(fontSize?: string): void { - if (fontSize) { - this.defaultFontSize = fontSize; - } else { - this.defaultFontSize = "14px"; - } - - this.updateFontSizePicker(this.defaultFontSize); - } -} diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/themes/mxTooltip.ts b/packages/pluggableWidgets/rich-text-web/src/utils/themes/mxTooltip.ts deleted file mode 100644 index 53bd314c9b..0000000000 --- a/packages/pluggableWidgets/rich-text-web/src/utils/themes/mxTooltip.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Range } from "quill/core"; -import Emitter from "quill/core/emitter"; -import LinkBlot from "quill/formats/link"; -import { BaseTooltip } from "quill/themes/base"; -import { linkConfigType } from "../formats"; -import { ACTION_DISPATCHER } from "../helpers"; -import { computePosition, flip, shift } from "@floating-ui/dom"; - -export default class MxTooltip extends BaseTooltip { - static TEMPLATE = [ - '', - '', - '', - '' - ].join(""); - - preview = this.root.querySelector("a.ql-preview"); - linkDOMNode = this.root.querySelector("a.ql-preview"); - - listen(): void { - super.listen(); - // @ts-expect-error Fix me later - this.root.querySelector("a.ql-action").addEventListener("click", event => { - if (this.root.classList.contains("ql-editing")) { - this.save(); - } else { - // @ts-expect-error Fix me later - this.edit("link", this.preview.textContent); - } - event.preventDefault(); - }); - // @ts-expect-error Fix me later - this.root.querySelector("a.ql-remove").addEventListener("click", event => { - if (this.linkRange != null) { - const range = this.linkRange; - this.restoreFocus(); - this.quill.formatText(range, "link", false, Emitter.sources.USER); - delete this.linkRange; - } - event.preventDefault(); - this.hide(); - }); - this.quill.on(Emitter.events.SELECTION_CHANGE, (range, _oldRange, source) => { - if (range == null) { - return; - } - if (range.length === 0 && source === Emitter.sources.USER) { - const [link, offset] = this.quill.scroll.descendant(LinkBlot, range.index); - if (link != null) { - this.linkRange = new Range(range.index - offset, link.length()); - if (link.domNode && link.domNode instanceof HTMLAnchorElement) { - this.linkDOMNode = link.domNode; - } - const preview = LinkBlot.formats(link.domNode); - // @ts-expect-error Fix me later - this.preview.textContent = preview; - // @ts-expect-error Fix me later - this.preview.setAttribute("href", preview); - this.show(); - this.setPosition(link.domNode); - return; - } - } else { - delete this.linkRange; - } - this.hide(); - }); - } - - edit(mode = "link", preview: string | null = null): void { - if (mode === "link") { - if (this.linkRange) { - this.quill.setSelection(this.linkRange); - const linkConfig: linkConfigType = { - text: this.linkDOMNode?.textContent ?? undefined, - href: this.linkDOMNode?.getAttribute("href") ?? "", - title: this.linkDOMNode?.getAttribute("title") ?? undefined, - target: this.linkDOMNode?.getAttribute("target") ?? undefined - }; - this.quill.emitter.emit(ACTION_DISPATCHER, linkConfig); - } - } else { - super.edit(mode, preview); - } - } - - setPosition(domNode: HTMLElement): void { - computePosition(domNode, this.root, { - placement: "bottom-end", - middleware: [flip(), shift({ padding: 5 })] - }).then(({ x, y }) => { - Object.assign(this.root.style, { - left: `${x}px`, - top: `${y}px` - }); - }); - } - - show(): void { - super.show(); - this.root.removeAttribute("data-mode"); - } -} diff --git a/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts b/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts index c3242f3c72..632f73900c 100644 --- a/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts +++ b/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts @@ -33,9 +33,11 @@ export interface CustomFontsType { export type StatusBarContentEnum = "wordCount" | "characterCount" | "characterCountHtml"; +export type StyleDataFormatEnum = "inline" | "class"; + export type ToolbarConfigEnum = "basic" | "advanced"; -export type CtItemTypeEnum = "separator" | "undo" | "redo" | "bold" | "italic" | "underline" | "strike" | "superScript" | "subScript" | "orderedList" | "bulletList" | "lowerAlphaList" | "checkList" | "minIndent" | "plusIndent" | "direction" | "link" | "image" | "video" | "formula" | "blockquote" | "code" | "codeBlock" | "viewCode" | "align" | "centerAlign" | "rightAlign" | "font" | "size" | "color" | "background" | "header" | "fullscreen" | "clean" | "tableBetter"; +export type CtItemTypeEnum = "separator" | "undo" | "redo" | "bold" | "italic" | "underline" | "strike" | "superScript" | "subScript" | "orderedList" | "bulletList" | "lowerAlphaList" | "checkList" | "minIndent" | "plusIndent" | "direction" | "link" | "image" | "video" | "formula" | "blockquote" | "code" | "codeBlock" | "viewCode" | "leftAlign" | "centerAlign" | "rightAlign" | "justifyAlign" | "font" | "size" | "color" | "background" | "header" | "fullscreen" | "clean" | "tableBetter"; export interface AdvancedConfigType { ctItemType: CtItemTypeEnum; @@ -83,6 +85,7 @@ export interface RichTextContainerProps { imageSourceContent?: ReactNode; enableDefaultUpload: boolean; statusBarContent: StatusBarContentEnum; + styleDataFormat: StyleDataFormatEnum; toolbarConfig: ToolbarConfigEnum; history: boolean; fontStyle: boolean; @@ -133,6 +136,7 @@ export interface RichTextPreviewProps { imageSourceContent: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> }; enableDefaultUpload: boolean; statusBarContent: StatusBarContentEnum; + styleDataFormat: StyleDataFormatEnum; toolbarConfig: ToolbarConfigEnum; history: boolean; fontStyle: boolean; diff --git a/packages/pluggableWidgets/rich-text-web/typings/modules.d.ts b/packages/pluggableWidgets/rich-text-web/typings/modules.d.ts deleted file mode 100644 index 5411798145..0000000000 --- a/packages/pluggableWidgets/rich-text-web/typings/modules.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -declare module "*.css"; -declare module "*.scss"; - -// Add quill-resize-module declaration -declare module "quill-resize-module" { - import Quill from "quill"; - - interface ResizeModuleOptions { - modules?: string[]; - handleStyles?: { - backgroundColor?: string; - border?: string; - boxSizing?: string; - }; - displayStyles?: { - backgroundColor?: string; - border?: string; - color?: string; - }; - toolbarStyles?: { - backgroundColor?: string; - border?: string; - color?: string; - boxShadow?: string; - }; - overlayStyles?: { - border?: string; - boxSizing?: string; - }; - embedTags?: string[]; - tools?: Array< - | string - | { - text: string; - verify: (activeEle: HTMLElement) => boolean; - handler: (evt: MouseEvent, button: HTMLElement, activeEle: HTMLElement) => void; - } - >; - locale?: { - altTip?: string; - inputTip?: string; - floatLeft?: string; - floatRight?: string; - center?: string; - restore?: string; - }; - } - - interface ResizeModuleConstructor { - new (quill: Quill, options: ResizeModuleOptions): any; - Modules?: any; - handleEdit(): void; - } - - const ResizeModule: ResizeModuleConstructor; - export default ResizeModule; -} - -// Add the dist/resize module declaration -declare module "quill-resize-module/dist/resize" { - export * from "quill-resize-module"; - export { default } from "quill-resize-module"; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf32de8c14..2b2c9c7939 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,7 +61,7 @@ importers: version: 8.0.3 turbo: specifier: ^2.5.4 - version: 2.9.18 + version: 2.10.2 automation/e2e-mcp: dependencies: @@ -124,7 +124,7 @@ importers: devDependencies: '@axe-core/playwright': specifier: ^4.11.1 - version: 4.11.3(playwright-core@1.61.0) + version: 4.12.1(playwright-core@1.61.1) '@eslint/js': specifier: ^9.39.4 version: 9.39.4 @@ -133,7 +133,7 @@ importers: version: link:../../packages/shared/prettier-config-web-widgets '@playwright/test': specifier: ^1.60.0 - version: 1.61.0 + version: 1.61.1 '@types/node': specifier: ~24.12.0 version: 24.12.4 @@ -142,7 +142,7 @@ importers: version: 2.10.4(eslint@9.39.4(jiti@2.6.1)) globals: specifier: ^17.4.0 - version: 17.6.0 + version: 17.7.0 playwright-ctrf-json-reporter: specifier: ^0.0.27 version: 0.0.27 @@ -151,7 +151,7 @@ importers: dependencies: '@commitlint/cli': specifier: ^19.8.1 - version: 19.8.1(@types/node@24.12.4)(typescript@6.0.3) + version: 19.8.1(@types/node@24.12.4)(typescript@5.9.3) '@commitlint/config-conventional': specifier: ^19.8.1 version: 19.8.1 @@ -160,7 +160,7 @@ importers: version: link:../../packages/shared/prettier-config-web-widgets pretty-quick: specifier: ^4.1.1 - version: 4.2.2(prettier@3.8.4) + version: 4.2.2(prettier@3.9.4) automation/snapshot-generator: dependencies: @@ -176,7 +176,7 @@ importers: version: link:../../packages/shared/prettier-config-web-widgets globals: specifier: ^17.3.0 - version: 17.6.0 + version: 17.7.0 automation/utils: devDependencies: @@ -224,7 +224,7 @@ importers: version: 0.8.5 ts-node: specifier: ^10.9.1 - version: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3) + version: 10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3) zod: specifier: ^3.25.76 version: 3.25.76 @@ -310,10 +310,10 @@ importers: version: link:../../shared/prettier-config-web-widgets '@rollup/plugin-node-resolve': specifier: ^16.0.0 - version: 16.0.3(rollup@4.62.0) + version: 16.0.3(rollup@4.62.2) '@rollup/plugin-terser': specifier: ^0.4.4 - version: 0.4.4(rollup@4.62.0) + version: 0.4.4(rollup@4.62.2) concurrently: specifier: ^6.5.1 version: 6.5.1 @@ -322,7 +322,7 @@ importers: version: 0.1.8 rollup: specifier: '*' - version: 4.62.0 + version: 4.62.2 xlsx: specifier: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz version: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz @@ -372,7 +372,7 @@ importers: version: link:../../shared/prettier-config-web-widgets globals: specifier: ^17.3.0 - version: 17.6.0 + version: 17.7.0 packages/pluggableWidgets/accessibility-helper-web: dependencies: @@ -388,7 +388,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.4)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -413,7 +413,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.4)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -453,7 +453,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -481,7 +481,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -512,7 +512,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -552,7 +552,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -583,7 +583,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -620,7 +620,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -638,7 +638,7 @@ importers: version: link:../../shared/widget-plugin-platform '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.3(rollup@4.62.0) + version: 6.0.3(rollup@4.62.2) packages/pluggableWidgets/bubble-chart-web: dependencies: @@ -666,7 +666,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -700,7 +700,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -746,7 +746,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -783,7 +783,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -854,7 +854,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -900,7 +900,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -946,7 +946,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -980,7 +980,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1041,7 +1041,7 @@ importers: version: 18.0.1 '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1078,7 +1078,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1124,7 +1124,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1164,7 +1164,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1222,7 +1222,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1261,7 +1261,7 @@ importers: version: link:../../shared/widget-plugin-platform '@radix-ui/react-progress': specifier: ^1.1.7 - version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) brandi: specifier: ^5.0.0 version: 5.1.0 @@ -1289,7 +1289,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1323,7 +1323,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1372,13 +1372,13 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/rollup-web-widgets': specifier: workspace:* version: link:../../shared/rollup-web-widgets '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.3(rollup@4.62.0) + version: 6.0.3(rollup@4.62.2) packages/pluggableWidgets/dropdown-sort-web: dependencies: @@ -1394,7 +1394,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1424,7 +1424,7 @@ importers: version: 2.5.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + version: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) packages/pluggableWidgets/events-web: dependencies: @@ -1440,7 +1440,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1477,7 +1477,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1520,7 +1520,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1532,7 +1532,7 @@ importers: version: link:../../shared/widget-plugin-test-utils '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.62.0) + version: 6.1.0(rollup@4.62.2) '@types/big.js': specifier: ^6.2.2 version: 6.2.2 @@ -1584,7 +1584,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1621,7 +1621,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1655,7 +1655,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1686,7 +1686,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1701,7 +1701,7 @@ importers: version: 2.5.1 react-image-crop: specifier: ^11.0.10 - version: 11.0.10(react@18.3.1) + version: 11.1.2(react@18.3.1) devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -1711,7 +1711,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1748,7 +1748,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1782,7 +1782,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1819,7 +1819,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1871,7 +1871,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1923,7 +1923,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1975,7 +1975,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2006,7 +2006,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2037,7 +2037,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2071,7 +2071,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2105,7 +2105,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2154,7 +2154,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2188,7 +2188,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2210,12 +2210,6 @@ importers: packages/pluggableWidgets/rich-text-web: dependencies: - '@codemirror/lang-html': - specifier: ^6.4.9 - version: 6.4.11 - '@codemirror/state': - specifier: ^6.5.2 - version: 6.6.0 '@floating-ui/dom': specifier: ^1.7.4 version: 1.7.6 @@ -2225,15 +2219,87 @@ importers: '@melloware/coloris': specifier: ^0.25.0 version: 0.25.0 - '@uiw/codemirror-theme-github': - specifier: ^4.23.13 - version: 4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1) - '@uiw/react-codemirror': - specifier: ^4.23.13 - version: 4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.1)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.16 + version: 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tiptap/core': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/extension-color': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/extension-text-style@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))) + '@tiptap/extension-font-family': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/extension-text-style@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))) + '@tiptap/extension-highlight': + specifier: ^3.23.5 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-image': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-link': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-list': + specifier: ^3.23.5 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-list-item': + specifier: ^3.23.5 + version: 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-subscript': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-superscript': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-table': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-table-cell': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/extension-table@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-table-header': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/extension-table@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-table-row': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/extension-table@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-task-item': + specifier: ^3.23.5 + version: 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-task-list': + specifier: ^3.23.5 + version: 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-text-align': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-text-style': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-underline': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-youtube': + specifier: ^3.23.4 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/pm': + specifier: ^3.23.4 + version: 3.27.1 + '@tiptap/react': + specifier: ^3.23.4 + version: 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tiptap/starter-kit': + specifier: ^3.23.4 + version: 3.27.1 + '@uiw/react-color-compact': + specifier: ^2.10.1 + version: 2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 js-beautify: specifier: ^1.15.4 version: 1.15.4 @@ -2246,15 +2312,15 @@ importers: lodash.merge: specifier: ^4.6.2 version: 4.6.2 - parchment: - specifier: ^3.0.0 - version: 3.0.0 - quill: - specifier: ^2.0.3 - version: 2.0.3 - quill-resize-module: - specifier: ^2.0.4 - version: 2.1.3 + react-dropzone: + specifier: ^14.3.8 + version: 14.4.1(patch_hash=d30fd95f2a3d58218fd5d657104b52cad6924893c0ac0e173f51c8c2d8e179b6)(react@18.3.1) + react-scroll-sync: + specifier: ^1.0.2 + version: 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-simple-code-editor: + specifier: ^0.14.1 + version: 0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -2264,7 +2330,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2288,16 +2354,16 @@ importers: version: link:../../shared/widget-plugin-test-utils '@rollup/plugin-alias': specifier: ^5.1.1 - version: 5.1.1(rollup@4.62.0) + version: 5.1.1(rollup@4.62.2) '@rollup/plugin-image': specifier: ^3.0.3 - version: 3.0.3(rollup@4.62.0) + version: 3.0.3(rollup@4.62.2) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.62.0) + version: 6.1.0(rollup@4.62.2) '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.3(rollup@4.62.0) + version: 6.0.3(rollup@4.62.2) '@types/js-beautify': specifier: ^1.14.3 version: 1.14.3 @@ -2312,19 +2378,19 @@ importers: version: 7.0.3 postcss: specifier: ^8.5.6 - version: 8.5.15 + version: 8.5.16 postcss-import: specifier: ^16.1.1 - version: 16.1.1(postcss@8.5.15) + version: 16.1.1(postcss@8.5.16) postcss-url: specifier: ^10.1.3 - version: 10.1.4(postcss@8.5.15) + version: 10.1.4(postcss@8.5.16) rollup-plugin-postcss: specifier: ^4.0.2 - version: 4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + version: 4.0.2(postcss@8.5.16)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) rollup-preserve-directives: specifier: ^1.1.3 - version: 1.1.3(rollup@4.62.0) + version: 1.1.3(rollup@4.62.2) packages/pluggableWidgets/selection-helper-web: dependencies: @@ -2340,7 +2406,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2374,7 +2440,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2414,7 +2480,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2457,7 +2523,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2485,7 +2551,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2531,7 +2597,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2562,7 +2628,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2596,7 +2662,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2627,7 +2693,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2655,7 +2721,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2713,16 +2779,16 @@ importers: version: link:../widget-plugin-test-utils '@rollup/plugin-commonjs': specifier: ^28.0.3 - version: 28.0.9(rollup@4.62.0) + version: 28.0.9(rollup@4.62.2) '@rollup/plugin-node-resolve': specifier: ^16.0.0 - version: 16.0.3(rollup@4.62.0) + version: 16.0.3(rollup@4.62.2) '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.3(rollup@4.62.0) + version: 6.0.3(rollup@4.62.2) '@rollup/plugin-terser': specifier: ^0.4.4 - version: 0.4.4(rollup@4.62.0) + version: 0.4.4(rollup@4.62.2) '@types/plotly.js-dist-min': specifier: ^2.3.4 version: 2.3.4 @@ -2737,13 +2803,13 @@ importers: version: 4.4.1 rollup: specifier: '*' - version: 4.62.0 + version: 4.62.2 rollup-plugin-copy: specifier: ^3.5.0 version: 3.5.0 rollup-plugin-license: specifier: ^3.6.0 - version: 3.7.1(picomatch@4.0.4)(rollup@4.62.0) + version: 3.7.1(picomatch@4.0.4)(rollup@4.62.2) packages/shared/eslint-config-web-widgets: dependencies: @@ -2764,16 +2830,16 @@ importers: version: 5.4.0(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1)) + version: 2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-jest: specifier: ^29.15.0 - version: 29.15.2(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.3.0(@types/node@24.12.4))(typescript@6.0.3) + version: 29.15.4(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.3.0(@types/node@24.12.4))(typescript@5.9.3) eslint-plugin-package-json: specifier: ^0.89.1 version: 0.89.4(@types/estree@1.0.9)(eslint@9.39.4(jiti@2.6.1))(jsonc-eslint-parser@3.1.0) eslint-plugin-prettier: specifier: ^5.5.5 - version: 5.5.6(eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(prettier@3.8.4) + version: 5.5.6(eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(prettier@3.9.4) eslint-plugin-promise: specifier: ^7.2.1 version: 7.3.0(eslint@9.39.4(jiti@2.6.1)) @@ -2785,13 +2851,13 @@ importers: version: 7.0.1(eslint@9.39.4(jiti@2.6.1)) globals: specifier: ^17.3.0 - version: 17.6.0 + version: 17.7.0 prettier: specifier: ^3.8.1 - version: 3.8.4 + version: 3.9.4 typescript-eslint: specifier: ^8.57.0 - version: 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + version: 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) packages/shared/filter-commons: dependencies: @@ -2819,7 +2885,7 @@ importers: version: link:../widget-plugin-test-utils '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 packages/shared/prettier-config-web-widgets: dependencies: @@ -2828,25 +2894,25 @@ importers: version: 9.39.4 '@prettier/plugin-xml': specifier: '>=3.4.1' - version: 3.4.2(prettier@3.8.4) + version: 3.4.2(prettier@3.9.4) eslint: specifier: ^9.39.3 version: 9.39.4(jiti@2.6.1) globals: specifier: ^17.3.0 - version: 17.6.0 + version: 17.7.0 prettier: specifier: ^3.8.1 - version: 3.8.4 + version: 3.9.4 prettier-plugin-packagejson: specifier: ^2.5.19 - version: 2.5.22(prettier@3.8.4) + version: 2.5.22(prettier@3.9.4) packages/shared/rollup-web-widgets: devDependencies: '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) rollup-plugin-copy: specifier: ^3.5.0 version: 3.5.0 @@ -2866,10 +2932,10 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.15.43) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -2896,7 +2962,7 @@ importers: version: link:../widget-plugin-mobx-kit downshift: specifier: ^9.0.9 - version: 9.3.6(react@18.3.1) + version: 9.4.0(react@18.3.1) mendix: specifier: ^10.24.75382 version: 10.24.75382 @@ -2934,10 +3000,10 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.15.43) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 @@ -2970,7 +3036,7 @@ importers: version: link:../widget-plugin-platform downshift: specifier: ^9.0.8 - version: 9.3.6(react@18.3.1) + version: 9.4.0(react@18.3.1) mendix: specifier: 10.24.75382 version: 10.24.75382 @@ -2995,10 +3061,10 @@ importers: version: link:../widget-plugin-test-utils '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.15.43) date-fns: specifier: ^3.6.0 version: 3.6.0 @@ -3032,10 +3098,10 @@ importers: version: link:../widget-plugin-test-utils '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.15.43) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -3059,10 +3125,10 @@ importers: version: link:../widget-plugin-platform '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.15.43) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -3093,10 +3159,10 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.15.43) optionalDependencies: react: specifier: '>=18.0.0 <19.0.0' @@ -3115,10 +3181,10 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.15.43) big.js: specifier: ^6.2.1 version: 6.2.2 @@ -3180,10 +3246,10 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.15.43 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.15.43) big.js: specifier: ^6.2.2 version: 6.2.2 @@ -3199,8 +3265,8 @@ packages: '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - '@axe-core/playwright@4.11.3': - resolution: {integrity: sha512-h/kfksv4F0cVIDlKpT4700OehdRgpvuVskuQ2nb7/JmtWUXpe9ftHAPtwyXGvVSsa6SJ64A9ER7Zrzc/sIvC4w==} + '@axe-core/playwright@4.12.1': + resolution: {integrity: sha512-rMd7xriptqKpP+w5265i4Hdkv2X5kbu6uiBi/B2I7uf3hieRBM3qDCfaKPtxfiYb2mKXfF+yLODJwIx+Jv1GDw==} peerDependencies: playwright-core: '>= 1.0.0' @@ -3905,39 +3971,6 @@ packages: resolution: {integrity: sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==} hasBin: true - '@codemirror/autocomplete@6.20.3': - resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} - - '@codemirror/commands@6.10.3': - resolution: {integrity: sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==} - - '@codemirror/lang-css@6.3.1': - resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} - - '@codemirror/lang-html@6.4.11': - resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==} - - '@codemirror/lang-javascript@6.2.5': - resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==} - - '@codemirror/language@6.12.3': - resolution: {integrity: sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==} - - '@codemirror/lint@6.9.7': - resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} - - '@codemirror/search@6.7.0': - resolution: {integrity: sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==} - - '@codemirror/state@6.6.0': - resolution: {integrity: sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==} - - '@codemirror/theme-one-dark@6.1.3': - resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} - - '@codemirror/view@6.43.1': - resolution: {integrity: sha512-+BIjw/AG3tDQ4pJgTLPYdAW25eDE66YsvM4LKyVPgGzVgZ4a9Wj1SRX8kPVKgBDdPt8oHtZ15F0qx7p0oOHdHw==} - '@commitlint/cli@19.8.1': resolution: {integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==} engines: {node: '>=v18'} @@ -4375,24 +4408,6 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@lezer/common@1.5.2': - resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} - - '@lezer/css@1.3.3': - resolution: {integrity: sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==} - - '@lezer/highlight@1.2.3': - resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} - - '@lezer/html@1.3.13': - resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==} - - '@lezer/javascript@1.5.4': - resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} - - '@lezer/lr@1.4.10': - resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==} - '@mapbox/geojson-rewind@0.5.2': resolution: {integrity: sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==} hasBin: true @@ -4400,9 +4415,9 @@ packages: '@mapbox/geojson-types@1.0.2': resolution: {integrity: sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==} - '@mapbox/jsonlint-lines-primitives@2.0.2': - resolution: {integrity: sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==} - engines: {node: '>= 0.6'} + '@mapbox/jsonlint-lines-primitives@2.0.3': + resolution: {integrity: sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==} + engines: {node: '>= 22'} '@mapbox/mapbox-gl-supported@1.5.0': resolution: {integrity: sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==} @@ -4435,9 +4450,6 @@ packages: resolution: {integrity: sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==} hasBin: true - '@marijn/find-cluster-break@1.0.2': - resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} - '@melloware/coloris@0.25.0': resolution: {integrity: sha512-RBWVFLjWbup7GRkOXb9g3+ZtR9AevFtJinrRz2cYPLjZ3TCkNRGMWuNbmQWbZ5cF3VU7aQDZwUsYgIY/bGrh2g==} @@ -4456,8 +4468,8 @@ packages: '@cfworker/json-schema': optional: true - '@napi-rs/wasm-runtime@1.1.5': - resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -4580,8 +4592,8 @@ packages: resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} engines: {node: ^14.18.0 || >=16.0.0} - '@playwright/test@1.61.0': - resolution: {integrity: sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==} + '@playwright/test@1.61.1': + resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==} engines: {node: '>=18'} hasBin: true @@ -4612,6 +4624,35 @@ packages: peerDependencies: prettier: ^3.0.0 + '@radix-ui/primitive@1.1.4': + resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} + + '@radix-ui/react-arrow@1.1.11': + resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.11': + resolution: {integrity: sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-compose-refs@1.1.3': resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} peerDependencies: @@ -4630,8 +4671,139 @@ packages: '@types/react': optional: true - '@radix-ui/react-primitive@2.1.6': - resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==} + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.14': + resolution: {integrity: sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.19': + resolution: {integrity: sha512-HZccBkbK0LOi8nYKIp5jll/zIRW0cCOmG6WWyqsSpmXCU+ZlcBbTqIwlBvPCu886C5RVu6c/kHV7xSP8IgYNHw==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.11': + resolution: {integrity: sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-menu@2.1.19': + resolution: {integrity: sha512-Mht9BVd1AIsNFVQr4KG3bIK7XQn5IXF0TL/2ObsrzOdc1loaly/+kBDL5roSCYn8j8XZkvpOD0WYLz2FQtH1Eg==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.3.2': + resolution: {integrity: sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} + peerDependencies: + '@types/react': '>=18.2.36' + '@types/react-dom': '*' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-progress@1.1.11': + resolution: {integrity: sha512-KqiGJcFaZDc+BvveAgU3ZhACg2MvSUDrCBx4lRR/ZVRNal0bvt8lBpvnSkep9heeOuF8Qfw3fszLDX4OpQ2NVw==} peerDependencies: '@types/react': '>=18.2.36' '@types/react-dom': '*' @@ -4643,8 +4815,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.10': - resolution: {integrity: sha512-JYzEg60lk79PwKM27WZyKd7PW8O4OM5jOaFfRPfOyeXmMw7tLJh5kSj+CEjVTehszuwml/AdCzPGMXBTGf4BBw==} + '@radix-ui/react-roving-focus@1.1.14': + resolution: {integrity: sha512-8Qcnx9447tx/aCBgw6Jenfqg4Skq+vqab9mCBmuGNipIS5YXvL275wbKEu7+ICYHIlAPgCduUMJH1XOYewKF6Q==} peerDependencies: '@types/react': '>=18.2.36' '@types/react-dom': '*' @@ -4665,6 +4837,63 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + '@rc-component/motion@1.3.3': resolution: {integrity: sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A==} peerDependencies: @@ -4928,141 +5157,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.62.0': - resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==} + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.62.0': - resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==} + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.62.0': - resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==} + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.62.0': - resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==} + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.62.0': - resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==} + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.62.0': - resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==} + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.62.0': - resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.62.0': - resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==} + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.62.0': - resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==} + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.62.0': - resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==} + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.62.0': - resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==} + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.62.0': - resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==} + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.62.0': - resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==} + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.62.0': - resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==} + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.62.0': - resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==} + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.62.0': - resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==} + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.62.0': - resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==} + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.62.0': - resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==} + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.62.0': - resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==} + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.62.0': - resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==} + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.62.0': - resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==} + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.62.0': - resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==} + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.62.0': - resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==} + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.62.0': - resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==} + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.62.0': - resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==} + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} cpu: [x64] os: [win32] @@ -5084,86 +5313,86 @@ packages: '@sinonjs/fake-timers@15.4.0': resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} - '@swc/core-darwin-arm64@1.15.41': - resolution: {integrity: sha512-kREh6J5paQFvP3i7f/4FbqRNOJREutVFVOkder4GVyCBQ39YmER55cW/y1NNjwrchzFqgYswFn0mMDCqbqKzrw==} + '@swc/core-darwin-arm64@1.15.43': + resolution: {integrity: sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.41': - resolution: {integrity: sha512-N8B56ESFazZAWZyIkecADSPCwlLEinW7QLMEeotCpv4J7VXwfH+OLkmRL8o96UZ+1355fwHxDTS6/wK7yucvkA==} + '@swc/core-darwin-x64@1.15.43': + resolution: {integrity: sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.41': - resolution: {integrity: sha512-6XrId2fyle0mS5xxON8rU84mPd2Cq1kDJRj+4BnQKTd7u+2kSA6Ww+JkOP0iTNqOqt9OXhPOEAjBHAuonWcdCg==} + '@swc/core-linux-arm-gnueabihf@1.15.43': + resolution: {integrity: sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.41': - resolution: {integrity: sha512-ynLIarxlkVnqHn1D0fKOVht6mNU5ks6lrH+MY3kkS+XFaGGgDxFZVjWKJlkYTKm3RCvBTfA8Ng5fLufXheMRKQ==} + '@swc/core-linux-arm64-gnu@1.15.43': + resolution: {integrity: sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [glibc] - '@swc/core-linux-arm64-musl@1.15.41': - resolution: {integrity: sha512-dXu/5vd4gh8symyhRF+4G7gOPkjmb4pONhh7sl+6GSiW0LOKZlfu5kXmyFbTz9smOT7jgr002qY9b1nujjXt2A==} + '@swc/core-linux-arm64-musl@1.15.43': + resolution: {integrity: sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [musl] - '@swc/core-linux-ppc64-gnu@1.15.41': - resolution: {integrity: sha512-XGO6zVPXoPE0gf/XnI4jBbafNT13AYgoh6ns0JCSdOetI/kqVf0vhpz7NuNgAzZrMVCsmieqjPoTwViDgh4mOQ==} + '@swc/core-linux-ppc64-gnu@1.15.43': + resolution: {integrity: sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==} engines: {node: '>=10'} cpu: [ppc64] os: [linux] libc: [glibc] - '@swc/core-linux-s390x-gnu@1.15.41': - resolution: {integrity: sha512-0WUglRwyZtW+iMi7J3iFdrCxreZZIKf4egTwEQfIYRsqFax69A0OrFj+NIoFSE03xBT/IFRrg+S8K6f9Ky+4hA==} + '@swc/core-linux-s390x-gnu@1.15.43': + resolution: {integrity: sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==} engines: {node: '>=10'} cpu: [s390x] os: [linux] libc: [glibc] - '@swc/core-linux-x64-gnu@1.15.41': - resolution: {integrity: sha512-VxkuQK59c0tHm6uJZCUrS3cyA2JhGGfdU6e41SZz0x/JS+4Sm7C1mIc97In14vkZJopEt7yXA2TouCqZDSygEA==} + '@swc/core-linux-x64-gnu@1.15.43': + resolution: {integrity: sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==} engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [glibc] - '@swc/core-linux-x64-musl@1.15.41': - resolution: {integrity: sha512-/0qXIu1ZxggLuovLb22vFfKHq2AA4n6Whw5UwmVCHk4pkw7KWnPIQpMCEqUMPsNkFJig7PPp/TSYFu8ZEb2rtQ==} + '@swc/core-linux-x64-musl@1.15.43': + resolution: {integrity: sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [musl] - '@swc/core-win32-arm64-msvc@1.15.41': - resolution: {integrity: sha512-Y481sMNZM6rECh9VO4+y26N1lWEDAyxnBZskUf37fl90uHE946VHfmiVQWT0uMFOhyJJFovGTRuF4W82dwewUg==} + '@swc/core-win32-arm64-msvc@1.15.43': + resolution: {integrity: sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.41': - resolution: {integrity: sha512-BAchBD5qeUzy3hiPSLJtaaoSm4blCLyYffOF1bGE4ETcV+OisqjUAwDQMJj++4bTpvMCDzwC+Bj3PmQyBCtscw==} + '@swc/core-win32-ia32-msvc@1.15.43': + resolution: {integrity: sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.41': - resolution: {integrity: sha512-WOkA+fJ/ViVBQDsSV9JC52NACTe5PhlurA6viASDZGb7HR3KS01ZG7RZ+Bg6SVQFIoq3gSbTsskQVe6EbHFAYw==} + '@swc/core-win32-x64-msvc@1.15.43': + resolution: {integrity: sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.15.41': - resolution: {integrity: sha512-03nQq/082QRJJiOvp3FGbgxTGyyxMxohPTjhk/W9bD2J0tk4ukITI7goOhOO2WbaHn/lsPmo/zf8+DIXhwpgYQ==} + '@swc/core@1.15.43': + resolution: {integrity: sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -5224,78 +5453,305 @@ packages: peerDependencies: '@testing-library/dom': ^10.4.1 - '@tootallnate/once@2.0.1': - resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} - engines: {node: '>= 10'} + '@tiptap/core@3.27.1': + resolution: {integrity: sha512-rV6Qn4wmC6BxfF+4mu6bqGWj9vA4oXXhsrpXaJL2uhjxeHAGofjwcHof2X84VYzeyXgdlsGmqKie4TAppVXZUQ==} + peerDependencies: + '@tiptap/pm': 3.27.1 - '@tsconfig/node10@1.0.12': - resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + '@tiptap/extension-blockquote@3.27.1': + resolution: {integrity: sha512-VMF7xJx6qEGiX6DTKNiL31NLqypOcd/4sNjFSe8rb41PwejBJh/nOqVIbBvWkiT6NMGFLxMhj7zJ8/zPo1hXeg==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + '@tiptap/extension-bold@3.27.1': + resolution: {integrity: sha512-TlC5bsS+pqETTrlz4CZz9RO/cKBYtELGIxwtKeivUn3eNfnOxQbbu4WDsiwIfzRFyd0OMnKl6BPM2KnYEehoEQ==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + '@tiptap/extension-bubble-menu@3.27.1': + resolution: {integrity: sha512-j/j8Qp9Z5nViade2m7zjrO/CYH/Ca80Qj7aqo0eUaei6FZQ5izlF9o4XQU5EFMAutV6mwynsPUp8FVo5sCuYfw==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@tiptap/extension-bullet-list@3.27.1': + resolution: {integrity: sha512-faCUHnRP47o9Zh9VZZX6EX/569udw9Vopm2PgEKPWuKLE2qaS5WBuUVU0iItdJmKUqaWiOZkpoW4jvnDmj0dfg==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 - '@turbo/darwin-64@2.9.18': - resolution: {integrity: sha512-9f27peFu16ur8c0v9nUFUEyBnbKuuFsUTjHFWfmwGfzySBXbHwzU44QhZon6Mznz0cHsIr3984NQj/bVrnGSRw==} - cpu: [x64] - os: [darwin] + '@tiptap/extension-code-block@3.27.1': + resolution: {integrity: sha512-pHlzmZx2OlHfyQ0yRlT5UL4mGokz947DthZuYefN1OleVqOkHpWBG+2JQwqoNq6bmzMne92zbH32rhcJUEYSjA==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@turbo/darwin-arm64@2.9.18': - resolution: {integrity: sha512-9A6TMRq/Ib+QnbhLlgkhOm+624wO4pzSQ/yQviQfWHOlFvaYxdnIAYmu2H6TS6y7kSVL0DvzNe04NbESTOzFVQ==} - cpu: [arm64] - os: [darwin] + '@tiptap/extension-code@3.27.1': + resolution: {integrity: sha512-epOUpFfEmBzjvnqvjv2qHX7NAuLo5dlOGV690lWu+sAYMjibuJBeVvAiKPyFCfRCCTUxdbDB3jbaOA1yEcEJ7w==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@turbo/linux-64@2.9.18': - resolution: {integrity: sha512-zCdIDtz69AnbYh913elJRRoF3QY5aa2HNnf+4rAkc7bQ+tWujiDkCNV7stazOUPggaDvhKIf2Z87qHftTeXSkw==} - cpu: [x64] - os: [linux] + '@tiptap/extension-color@3.27.1': + resolution: {integrity: sha512-9R/vW4dDTce0E0HU+AyfSkKhz08Hlrx7noRRj3CKrTwsCTR86wReTKFHlzxhLKZiDrJuV2Y7CtNG7KGrAArf+A==} + peerDependencies: + '@tiptap/extension-text-style': 3.27.1 - '@turbo/linux-arm64@2.9.18': - resolution: {integrity: sha512-Va1kXI04naMgYwqv/5Dfa36dTDx8015U7oaQAjrXa45ua9OoFjSV4OmvkML4EmXvUclQHCiBRbY8bvd0jV7eAg==} - cpu: [arm64] - os: [linux] + '@tiptap/extension-document@3.27.1': + resolution: {integrity: sha512-8FbBTkfnRP4iVaoj+2h3iWa+H0eGDD3yTyVCwrmue/sQTkqUNUoSuAZa3GDG4Sd41xdPwTJxl9nUWGgM1qDCnw==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@turbo/windows-64@2.9.18': - resolution: {integrity: sha512-m0kDhZANxSNz9ck1ybogFscHabriAsp4eDFNrN/1H5WrgTF7b3VlcPZnhuO3v2+E2KnCbeAc+UUT10BZZHdDKw==} - cpu: [x64] - os: [win32] + '@tiptap/extension-dropcursor@3.27.1': + resolution: {integrity: sha512-blFf9x9RG0Qr7P3FoAH/033ffa+mMLZn34trVs8Vi0Ppk6FmJAg5HpYFOtmYoeREdNDJ5rHJKV7SoACbOHgskQ==} + peerDependencies: + '@tiptap/extensions': 3.27.1 - '@turbo/windows-arm64@2.9.18': - resolution: {integrity: sha512-nUdR8WqoomUys9iIQmG45TMiizJ+5BV8egSeLLZba/AWblyp3fVBcIH1kSE58OtK4g2YzbMJEth6Ttv9w5rqMA==} - cpu: [arm64] - os: [win32] + '@tiptap/extension-floating-menu@3.27.1': + resolution: {integrity: sha512-BmJF1VqB7dSJkgAalrpVFj88WLhxKjcWPuWHOqf2ITrUU2832BhKLXKmxjWUy1gqV8PfNNVWtGfIERy7I0y0+Q==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@turf/area@7.3.5': - resolution: {integrity: sha512-sSn80wPT7XfBIDN3vurCPxhk9W4U8ozS/XImSqeLN8qveTICOxzZkhsGDMp0CuncaN+plWut4a2TdNM7mzZB6Q==} + '@tiptap/extension-font-family@3.27.1': + resolution: {integrity: sha512-sxPae35pbJSaF3IZRGDglB5z/7nzS9dmxLLjiQNWYmgUIs8+/EYcrsN/woYUS0nsgHswgBmg7DwBabunKLSGhQ==} + peerDependencies: + '@tiptap/extension-text-style': 3.27.1 - '@turf/bbox@7.3.5': - resolution: {integrity: sha512-oG1ya/HtBjAIg4TimbWx+nOYPbY0bCvt82Bq8tm6sBw3qqtbOyRSfDz79Sq90TnH7DXJprJ1qnVGKNtZ6jemfw==} + '@tiptap/extension-gapcursor@3.27.1': + resolution: {integrity: sha512-QoezN0wdvXIwLQ4ee2ccWDaX3RG0lzgQpIMpMz55oPDhpUVax1+19ApsS53LkcktpS4EbnPL4xO4DaJk0Vp7PQ==} + peerDependencies: + '@tiptap/extensions': 3.27.1 - '@turf/centroid@7.3.5': - resolution: {integrity: sha512-hkWaqwGFdOn6Tf0EWfn2yn1XZ1FWE1h2C5ZWstDMu/FxYO5DB+YjlmOFPl4K6SmSOEgdV07eK2vDCyPeTHqKGA==} + '@tiptap/extension-hard-break@3.27.1': + resolution: {integrity: sha512-iv/m9hzl6jfSj9Q8UEjAxONvCoUDaP7M9SRCPx3PaLNxA230TTD6RE0Ye4zFJ8ze7ZVoJJMAqg9Qpq1iYg2JOQ==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@turf/helpers@7.3.5': - resolution: {integrity: sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg==} + '@tiptap/extension-heading@3.27.1': + resolution: {integrity: sha512-SrC4l1kEIyv9ZXFaI/8LQqU2MyMmjczw7XXsWUQOTN4YXv0JyVgMNR3cI/wz0d2xsTfBdZ1N85Tdng+Ga1t0Sg==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@turf/meta@7.3.5': - resolution: {integrity: sha512-r+ohqxoyqeigFB0oFrQx/YEHIkOKqcKpCjvZkvZs7Tkv+IFco5MezAd2zd4rzK+0DfFgDP3KpJc7HqrYjvEjhg==} + '@tiptap/extension-highlight@3.27.1': + resolution: {integrity: sha512-IeMIUKbW4oyRkgn92BPYQ0fifkbTwVigKwa107nGDHMokz+pHQPFHy8xG3U7gtUOra/8OUo57bFgNGuP0TaH4A==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tiptap/extension-horizontal-rule@3.27.1': + resolution: {integrity: sha512-QlKE7qn5qMnIGVGhXQlvYedvLtNJ9z0dmit5w8vPb8tKzW4Spk6M7N2kruprrDA8GBwHfeR5wmF+njfUm34qxg==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 - '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@tiptap/extension-image@3.27.1': + resolution: {integrity: sha512-+JTahgQT+NxiGjduaB3qJVyhU/wh4m3pVkht1Earioku2bm/apj5Lb8rSowa/NJYP3B+oQgV/V4YLw5dtDgBoA==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + '@tiptap/extension-italic@3.27.1': + resolution: {integrity: sha512-jGGeyn9uRUnNjSTHpbqhiGsp6KaYTSbV09jDXPJI9cDwfV9hpugLvpaCZd0BMBbhU1B1W6kOfX0BE15qX/HQfA==} + peerDependencies: + '@tiptap/core': 3.27.1 - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + '@tiptap/extension-link@3.27.1': + resolution: {integrity: sha512-/2jBfsxBZUDGJmpZifqRQPz7f1E5qpS1BckTZ39TADzUJX+feKy7RJ3DtQ02+8y6SSMzvP9loGVjrk6zEMTk4g==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-list-item@3.27.1': + resolution: {integrity: sha512-zwRl01ETfCkWUvtvK5fw9bXtAajMPkvlkE3Cq6JvH3LF7XXJwDtNj5Tj7exacMpCaSZmlNc43vFb2rAYnrnwMA==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-list-keymap@3.27.1': + resolution: {integrity: sha512-OIMZNlzPSO8WRd4ic73Fxckzl4N1tesjjLL2XApaNA/uMpO0LoF6WSRPAWv+Z24Wp92ARRJAnRP7iZoI5+Jxig==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-list@3.27.1': + resolution: {integrity: sha512-c2Upru7lj0/ZV/Ibww6cNz6sUS8m6Dp/9uygFhYcZOd3X8M0xBIEk42c6m6SQehkPziVA8QOgNJz7sMqsbz1OQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-ordered-list@3.27.1': + resolution: {integrity: sha512-GYrKqD//9nHJ2r80uXqbDMzRnFpGzbaEQRTSGaO/SH7DvXWFMow8evkOdjQ7PCQO07jNjJo75+A85Jwu3Ov3AA==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-paragraph@3.27.1': + resolution: {integrity: sha512-7K7eo1gruOgAsnbK+GCV23AUVUI0cL1bTig8HaPneoFMVbig7vddk8jNLKBWO8TXVbG7TuHdnDN4F98vdtwh5Q==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-strike@3.27.1': + resolution: {integrity: sha512-Y3DW1jlSlCNCyMGHP3+3qBNNPS83wuFz4RTYGjZtvRRTCRh7apZme9XRWMq1rN5mJ2Cr7fKocA2/5Bs13KgN6Q==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-subscript@3.27.1': + resolution: {integrity: sha512-A8bokr7c7QBndZcMX0F4AK1y4xEYd5MvOczojFusaiTfvAnH3D8PwFbOPQs3ZLB8W9K3bn4g/XH55P1IAPFGyg==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-superscript@3.27.1': + resolution: {integrity: sha512-8021uld246dqhtzZdzjvnWcfiJwElPQ50zZe7Fc3QGFiNRDVWQ2ATXGJFmXEopgCU5GuZaytPMtAq4UVDXc6xw==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-table-cell@3.27.1': + resolution: {integrity: sha512-aopweoHugP+ZR5WTgb7mccXXWmjoNv7PBjtQDl8rvS4IWsJE1w23B+rkj7VuoncO8WK5bU3SS9O3l5aObql68w==} + peerDependencies: + '@tiptap/extension-table': 3.27.1 + + '@tiptap/extension-table-header@3.27.1': + resolution: {integrity: sha512-+J9EyKP6RMInfMJbIoNemqWzHxZe9XZv/7OjpsvtVZO1cxMTd5W1xIg0Ntr7YMdG9eKBC8Vwx5jjFbRm2tbM3A==} + peerDependencies: + '@tiptap/extension-table': 3.27.1 + + '@tiptap/extension-table-row@3.27.1': + resolution: {integrity: sha512-peEcbNRrJJ9Qc/WNKQNFbkwBrJvBvq22Dp5WNqEygn/ZJez1qD9ctd10+D6f3b9GtSyx12sfDgn8yBufWa+Iug==} + peerDependencies: + '@tiptap/extension-table': 3.27.1 + + '@tiptap/extension-table@3.27.1': + resolution: {integrity: sha512-tNB8kjxo0+XPremnWkd6NUikpeJ+JQrss7HntL8GgIxX4t0gkdnLn9yUWb0JzcaYP7Y8iTZZHdkPs0P154DG8Q==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-task-item@3.27.1': + resolution: {integrity: sha512-HxS85Xqlf9voWpqW6yJNExjnqy9AcQbvL7K41gzKUIaXZdNxAyEkUg7wve7WAd7AUPI9yrS9WONplhmq7pWuJw==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-task-list@3.27.1': + resolution: {integrity: sha512-5LaJU3q/O6S1aNe9uj/VbZ7uS8G9mJMvJGNm/wRGyR3HcZtHAcFsPh9+woylQvUThD0qO9OMeGXkgqRSE+ayuA==} + peerDependencies: + '@tiptap/extension-list': 3.27.1 + + '@tiptap/extension-text-align@3.27.1': + resolution: {integrity: sha512-EXawuJBO55wd8WcTbHTMoPhv0CGQxza4yCCPB5Hqz4ZPQwahIr3ej+8yp/kimIl0xokabwZ0/Fu8STQ4AkZv5g==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-text-style@3.27.1': + resolution: {integrity: sha512-J48WIl+6YDYTFPhWXUBQk+u7+AKVUqTdvrZOQyPYCGuQMgHrYzgWrI5+HeEifUgXJ5rMIWWP3qytp7KhVVqpDQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-text@3.27.1': + resolution: {integrity: sha512-6ZwaZwSrDh+KFFv6V1J79oO37yPs7y1bFxvk1/9Ih2rn3Xr5AWz+eMS+n8RpH3djBVVAQpdIAeYQgcn+VCSsTg==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-underline@3.27.1': + resolution: {integrity: sha512-N889J4nXN/TPfVt8uF9N1A0SY82E90zwc1y26lqOcw6KWNLmQrlhMh/9OD4ikLDbekmFpOBq/UicpHf/6S8hbQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extension-youtube@3.27.1': + resolution: {integrity: sha512-3ThjdJ2+oQAq/t1aXDR8fGjRA+L6jJxZoNrOkw/KrysDmoflFd4Vn+7ZctKtop4fkC9GjX8NX6NzkfQoe4lCrQ==} + peerDependencies: + '@tiptap/core': 3.27.1 + + '@tiptap/extensions@3.27.1': + resolution: {integrity: sha512-1Tdx9faw8k0/83V6X+xCDVhV8yElGt95JxeW3YMkKQJI56QdlPz0xOdJPlMiSGJKinPyVier+x9LJD/YZUZIaw==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + + '@tiptap/pm@3.27.1': + resolution: {integrity: sha512-Ffjx+vimmBU7zH/KrpXzJid3+pziCe/VL2aexSTP63cyQwKQ65LkFkCKaIsSpFdQQuakVZBGWjCA5RoBV852pw==} + + '@tiptap/react@3.27.1': + resolution: {integrity: sha512-/Wn2fc9zMtX08MXYScDFsm4wJ8lzfhfPEdbtls7WCDlbtrop48PWlkHDBBJrywARfAQTB2mFs9KiFy9yrQm5Lg==} + peerDependencies: + '@tiptap/core': 3.27.1 + '@tiptap/pm': 3.27.1 + '@types/react': '>=18.2.36' + '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + + '@tiptap/starter-kit@3.27.1': + resolution: {integrity: sha512-vfxRsqW8rCc0k4pzo0ilU3wobVi2wqVj88VZI2SlgZlNnUAkrDGDIAph7CTa9k9fshV+O1ivpEgPC5yC046jow==} + + '@tootallnate/once@2.0.1': + resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} + engines: {node: '>= 10'} + + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@turbo/darwin-64@2.10.2': + resolution: {integrity: sha512-wBM3ObqOWnKUDmg7QfUFDkDHPFUAJmrYlYqmEM8jMPAPA/I6wRJIbWimeQUqhOiQ8xPKhzyWM+xaiUP0wz8FEQ==} + cpu: [x64] + os: [darwin] + + '@turbo/darwin-arm64@2.10.2': + resolution: {integrity: sha512-/Cq0joWnuMjDPfhjbFP4sv+C/7gkQ415zlaO4XUzD5EZxbtrKgXKvuuydMvogG8GeUnN1aDltW71RlmEfpjbyw==} + cpu: [arm64] + os: [darwin] + + '@turbo/linux-64@2.10.2': + resolution: {integrity: sha512-mMsf5IIhiKuceEXNstd25IbadjBXZ0amxzFOqliEzJX6HyeeHdBQPVSY583PWqYDyqM/FB8d5ZjkthfBSeuH3Q==} + cpu: [x64] + os: [linux] + + '@turbo/linux-arm64@2.10.2': + resolution: {integrity: sha512-Wcng1i2kaKmXutmwxT9MUoYZvdaIekXAdlGr4+0TpgbhGLw7nDuEcRBFrxb5BbRoX1d1q8SpdRxLc45TvDZIdQ==} + cpu: [arm64] + os: [linux] + + '@turbo/windows-64@2.10.2': + resolution: {integrity: sha512-SsNhM7Ho7EpAdwtrJKBOic9Hso23vu6Dp0gAfLOvUFjPzurr/sGQlXZEvr6z89ne4RDOypTwz5CBDrixpMKtXw==} + cpu: [x64] + os: [win32] + + '@turbo/windows-arm64@2.10.2': + resolution: {integrity: sha512-Gf+S7ICAdimT/n02bOuVWKvhHnct/HYjZg3oBNIz5hZ9ZyWHbQim9J3P5Qip8WpX0ksxF7eaBVziJCuLnjhqDg==} + cpu: [arm64] + os: [win32] + + '@turf/area@7.3.5': + resolution: {integrity: sha512-sSn80wPT7XfBIDN3vurCPxhk9W4U8ozS/XImSqeLN8qveTICOxzZkhsGDMp0CuncaN+plWut4a2TdNM7mzZB6Q==} + + '@turf/bbox@7.3.5': + resolution: {integrity: sha512-oG1ya/HtBjAIg4TimbWx+nOYPbY0bCvt82Bq8tm6sBw3qqtbOyRSfDz79Sq90TnH7DXJprJ1qnVGKNtZ6jemfw==} + + '@turf/centroid@7.3.5': + resolution: {integrity: sha512-hkWaqwGFdOn6Tf0EWfn2yn1XZ1FWE1h2C5ZWstDMu/FxYO5DB+YjlmOFPl4K6SmSOEgdV07eK2vDCyPeTHqKGA==} + + '@turf/helpers@7.3.5': + resolution: {integrity: sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg==} + + '@turf/meta@7.3.5': + resolution: {integrity: sha512-r+ohqxoyqeigFB0oFrQx/YEHIkOKqcKpCjvZkvZs7Tkv+IFco5MezAd2zd4rzK+0DfFgDP3KpJc7HqrYjvEjhg==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} @@ -5339,8 +5795,8 @@ packages: '@types/glob@7.2.0': resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - '@types/google.maps@3.65.1': - resolution: {integrity: sha512-O9monmoXfyWsuyR4Wz3TZU26qai9y7jUV7DSRySluae6O5tQt3Obw5ETt0HKfNsjctnlF/yx/Tfn3WQNmKRXZA==} + '@types/google.maps@3.65.2': + resolution: {integrity: sha512-e52bmOhGCQSNabFpL48iQlwJybq6rfns8NUVJ20MR7CdPlHQ2RmSCnPbJfrUYJfogrE4OiHQTZ4LXpop+eer1w==} '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -5480,6 +5936,9 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@types/warning@3.0.4': resolution: {integrity: sha512-CqN8MnISMwQbLJXO3doBAV4Yw9hx9/Pyr2rZ78+NfaCnhyRA/nKrpyk6E7mKw17ZOaQdLpK9GiUjrqLzBlN3sg==} @@ -5492,99 +5951,100 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.61.1': - resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} + '@typescript-eslint/eslint-plugin@8.62.1': + resolution: {integrity: sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.61.1 + '@typescript-eslint/parser': ^8.62.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/parser@8.61.1': - resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} + '@typescript-eslint/parser@8.62.1': + resolution: {integrity: sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/project-service@8.61.1': - resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + '@typescript-eslint/project-service@8.62.1': + resolution: {integrity: sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/scope-manager@8.61.1': - resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + '@typescript-eslint/scope-manager@8.62.1': + resolution: {integrity: sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.61.1': - resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + '@typescript-eslint/tsconfig-utils@8.62.1': + resolution: {integrity: sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/type-utils@8.61.1': - resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} + '@typescript-eslint/type-utils@8.62.1': + resolution: {integrity: sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/types@8.61.1': - resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} + '@typescript-eslint/types@8.62.1': + resolution: {integrity: sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.61.1': - resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} + '@typescript-eslint/typescript-estree@8.62.1': + resolution: {integrity: sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/utils@8.61.1': - resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} + '@typescript-eslint/utils@8.62.1': + resolution: {integrity: sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/visitor-keys@8.61.1': - resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} + '@typescript-eslint/visitor-keys@8.62.1': + resolution: {integrity: sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@uiw/codemirror-extensions-basic-setup@4.25.10': - resolution: {integrity: sha512-P3vytLlpE62KYSWrMUnwDCv2lvaQDuDZzyj03mHntuHo5bSl34fRZpjTY3kQTPGuXHxkGSYpoPFFj+hMTqaaMQ==} + '@uiw/color-convert@2.10.3': + resolution: {integrity: sha512-5tIjb4CZzGR7K3Sshswsuuc6FOAFNFwjtF0hkhKH3f+CMauC4Akv7LPq6o9v68S7dIAeKvfj8qWg5Tc2I1TVSA==} peerDependencies: - '@codemirror/autocomplete': '>=6.0.0' - '@codemirror/commands': '>=6.0.0' - '@codemirror/language': '>=6.0.0' - '@codemirror/lint': '>=6.0.0' - '@codemirror/search': '>=6.0.0' - '@codemirror/state': ^6.5.2 - '@codemirror/view': ^6.38.1 + '@babel/runtime': '>=7.19.0' - '@uiw/codemirror-theme-github@4.25.10': - resolution: {integrity: sha512-iMM2QT4FaebJMO4W7lXmxNkRPIjKzgY26wL0QG0Ugy0gzsnxoNz4zgNeFIblPA8rvrN3vOIhNNh4nk9UOlFKxA==} + '@uiw/react-color-compact@2.10.3': + resolution: {integrity: sha512-iXIG7idhaZ3XNFanKbVfFsL4w2GU5yWRUR+J5L1SzkXar1LTCjx7IDScweEBKnMdpPzDGiS2kTIn5anAif4ZDg==} + peerDependencies: + '@babel/runtime': '>=7.19.0' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' - '@uiw/codemirror-themes@4.25.10': - resolution: {integrity: sha512-Fqiz1HIuDlDftcL+/O53V333UOH6MqQ84VbiQB5egn6u+uDwAqACp1FrdAoi4wgpR3b3TGW4Gr0wIYcrJSSz1A==} + '@uiw/react-color-editable-input-rgba@2.10.3': + resolution: {integrity: sha512-n0kyNTNicRKASJNWYJexfwwfbbwvOJEYGQO8tX4PivmZYQVuYZq/vqsgYG5y+VGrBYZsHFfY+0LtPS4AXlz+Aw==} peerDependencies: - '@codemirror/language': '>=6.0.0' - '@codemirror/state': ^6.5.2 - '@codemirror/view': ^6.38.1 + '@babel/runtime': '>=7.19.0' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + + '@uiw/react-color-editable-input@2.10.3': + resolution: {integrity: sha512-QnYNpFI0p8pssYbWxIJwfmUdPwzzX0IXOAl4mU8q1HQNYoXFBGFJ4rXkJSOdauumAfOr4C8RKZlTB4/83EBBPQ==} + peerDependencies: + '@babel/runtime': '>=7.19.0' + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' - '@uiw/react-codemirror@4.25.10': - resolution: {integrity: sha512-DzgSMwM5qzB7v1FIb4gEeriYt67iiay756/HIOM9mAbeOVK0MO7rqefHf0O5c0269pJKMW7AH9FjclExD23V9w==} + '@uiw/react-color-swatch@2.10.3': + resolution: {integrity: sha512-7Q/h9RTeloFnrIo/k6U5g+D/abSIT62FiAQZLaEC34O2eOtu1LQReTXAgx39b1HSQ8piCgBYaQc7oM9WhRTPow==} peerDependencies: - '@babel/runtime': '>=7.11.0' - '@codemirror/state': ^6.5.2 - '@codemirror/theme-one-dark': '>=6.0.0' - '@codemirror/view': ^6.38.1 - codemirror: '>=6.0.0' + '@babel/runtime': '>=7.19.0' react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@ungap/structured-clone@1.3.1': - resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + '@ungap/structured-clone@1.3.2': + resolution: {integrity: sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==} '@unrs/resolver-binding-android-arm-eabi@1.12.2': resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} @@ -5833,6 +6293,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -5934,8 +6398,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.11.4: - resolution: {integrity: sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==} + axe-core@4.12.1: + resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} engines: {node: '>=4'} babel-jest@29.7.0: @@ -6026,8 +6490,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.37: - resolution: {integrity: sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==} + baseline-browser-mapping@2.10.40: + resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==} engines: {node: '>=6.0.0'} hasBin: true @@ -6069,8 +6533,8 @@ packages: brace-expansion@2.1.1: resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - brace-expansion@5.0.6: - resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -6086,8 +6550,8 @@ packages: brandi@5.1.0: resolution: {integrity: sha512-wGAIaC/pj/SMRCc7RdEhawT83YcbuxSViRAWp0d5cWOCjpGqAzCJo8NeiL/5rUbAPL4zlQ45ciz5eMnARMGygA==} - browserslist@4.28.2: - resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + browserslist@4.28.4: + resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -6135,8 +6599,8 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001799: - resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + caniuse-lite@1.0.30001800: + resolution: {integrity: sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==} canvas@3.2.3: resolution: {integrity: sha512-PzE5nJZPz72YUAfo8oTp0u3fqqY7IzlTubneAihqDYAUcBk7ryeCmBbdJBEdaH0bptSOe2VT2Zwcb3UaFyaSWw==} @@ -6239,9 +6703,6 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - codemirror@6.0.2: - resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} - collect-v8-coverage@1.0.3: resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} @@ -6432,9 +6893,6 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - crelt@1.0.6: - resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} - cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -6752,6 +7210,9 @@ packages: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6833,8 +7294,8 @@ packages: peerDependencies: react: '>=18.0.0 <19.0.0' - downshift@9.3.6: - resolution: {integrity: sha512-xEKP1vbt/k7Siu481TKibmj8EyL6iyBwaRJgb6gCsFyLeiyZ1KEJnApS9R1U71hTdK5ym0R99AOYRROcTz1PeQ==} + downshift@9.4.0: + resolution: {integrity: sha512-yIwyzRYzycKwMZhvOahsn4BmRr4Ffi0JymdWOtTDhR+LGDEXCZHTcwgUbNBC/oZLT6YrR46iwJe/BrQUS5ClQQ==} peerDependencies: react: '>=18.0.0 <19.0.0' @@ -6858,8 +7319,8 @@ packages: earcut@2.2.4: resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} - earcut@3.0.2: - resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==} + earcut@3.2.0: + resolution: {integrity: sha512-qy0wTgkOKsKC0cHVd4cKFfaO5u7egzs0F2bQ/9l5Pvm0EJM4XDdUMFoCJrxmWc2Fc3sRFa8l6gds8tx+6BcN8g==} eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -6872,8 +7333,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.373: - resolution: {integrity: sha512-G2Hym8JIf/QreuseqkDibgH8Ci8KfJzqGDKdakbhSx9UltwRBH2cBLAWU/lBX0sCdv0TlhyxQyDCnSfxgMWsjA==} + electron-to-chromium@1.5.383: + resolution: {integrity: sha512-I2484/KkAvl8lm9VyjH2JnbOIV0d/UCqT7gbzs6l+o6Vmn9wgB66uVcKX+Vk6HrXtY6fbWTOEXuv8waDTuFNCw==} elementary-circuits-directed-graph@1.3.1: resolution: {integrity: sha512-ZEiB5qkn2adYmpXGnJKkxT8uJHlW/mxmBpmeqawEHzPxh9HkLD4/1mFYX5l0On+f6rcPIt8/EWlRU2Vo3fX6dQ==} @@ -6967,8 +7428,8 @@ packages: resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.1: - resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==} + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} engines: {node: '>= 0.4'} es5-ext@0.10.64: @@ -7061,8 +7522,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-jest@29.15.2: - resolution: {integrity: sha512-kEN4r9RZl1xcsb4arGq89LrcVdOUFII/JSCwtTPJyv16mDwmPrcuEQwpxqZHeINvcsd7oK5O/rhdGlxFRaZwvQ==} + eslint-plugin-jest@29.15.4: + resolution: {integrity: sha512-6ln5i9Nkrb27X4w91ZPt/xHDsVQnvxTS2ntgq6r32u+8gymdUrp88TdcBXSveZW0Dl+M5v2H6K75kJhMvUGhjg==} engines: {node: ^20.12.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0 @@ -7209,9 +7670,6 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -7282,6 +7740,10 @@ packages: fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + fast-equals@5.4.0: + resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} + engines: {node: '>=6.0.0'} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -7295,8 +7757,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.1.2: - resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-uri@3.1.3: + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} fast-xml-parser@4.5.6: resolution: {integrity: sha512-Yd4vkROfJf8AuJrDIVMVmYfULKmIJszVsMv7Vo71aocsKgFxpdlpSHXSaInvyYfgw2PRuObQSW2GFpVMUjxu9A==} @@ -7382,8 +7844,12 @@ packages: flow-enums-runtime@0.0.6: resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==} - flow-parser@0.318.0: - resolution: {integrity: sha512-66vPPqpjOcroUke2vbiyRNS87lbTi7ib80CM9lsn45qymGHPL4nrdY9FKo0TvtrFFqHQErfB/BJeqVkEnWeK/g==} + flow-estree@0.321.0: + resolution: {integrity: sha512-rQY7YKoo+PKpAHQjEP2cxyIefk04OCEKUlbtV4y6LAUG3Ly7guQX9YH8th6drSmYcNkMgpqWMaHRhhYaAF69CA==} + engines: {node: '>=18'} + + flow-parser@0.321.0: + resolution: {integrity: sha512-9LNK2rp/NWWbwdEK6mxC54XfQRyD2lVV0iPVBYf+5o5vvqJl7ietkR1hX3/dZ39j0GAbL4PIFoeekDViOW0x/Q==} engines: {node: '>=0.4.0'} font-atlas@2.1.0: @@ -7426,8 +7892,8 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.3.5: - resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} + fs-extra@11.3.6: + resolution: {integrity: sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==} engines: {node: '>=14.14'} fs-extra@8.1.0: @@ -7485,6 +7951,10 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} @@ -7511,7 +7981,7 @@ packages: git-raw-commits@4.0.0: resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} engines: {node: '>=16'} - deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. + deprecated: Deprecated and no longer maintained. Use @conventional-changelog/git-client instead. hasBin: true github-from-package@0.0.0: @@ -7572,8 +8042,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@17.6.0: - resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} + globals@17.7.0: + resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} engines: {node: '>=18'} globalthis@1.0.4: @@ -7706,11 +8176,15 @@ packages: hermes-parser@0.36.0: resolution: {integrity: sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w==} + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + hoist-non-react-statics@2.5.5: resolution: {integrity: sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==} - hono@4.12.25: - resolution: {integrity: sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==} + hono@4.12.27: + resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.3: @@ -7804,8 +8278,8 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@5.1.6: - resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} import-cwd@3.0.0: resolution: {integrity: sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==} @@ -8471,12 +8945,12 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + js-yaml@3.15.0: + resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} hasBin: true - js-yaml@4.2.0: - resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} hasBin: true jsbarcode@3.12.3: @@ -8665,16 +9139,9 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. - lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} @@ -8827,8 +9294,8 @@ packages: mendix@10.24.75382: resolution: {integrity: sha512-ICMxqkWUejsc3KeFD9BJYvC+T4soi/NB2iapwWPC7oN0lCrFx36upzwI4rU77oMdRHsrVSsFVYLBy7sJJOABHw==} - mendix@11.10.0: - resolution: {integrity: sha512-OsLdgNJfhwG4/TcOIMMk0gu0ewHq3Xlv2CNW1YJy2ujqD19ngFvBVB9YTjYxmf+RMn1EG0FQYrn6zCSFzmbL4A==} + mendix@11.12.0: + resolution: {integrity: sha512-p6CF7Uqz4NvL0ylaOug16BxEhvV3g0MP7HzG6xr6PYKi3ADOQVy5secDlSj08qFTsjWZjqS463So2Fj9/QMv+g==} meow@12.1.1: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} @@ -9053,8 +9520,8 @@ packages: resolution: {integrity: sha512-Jd0fILWG44a9luj8v5kED4WI+zfkkgwKyRQKItTtlPfEsh7Lznfi1kr8/iZ+XAIss4Qq5GqRB0qtWbaz9ceO/A==} engines: {node: ^18.0.0 || >=20.0.0} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -9090,8 +9557,8 @@ packages: nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - node-abi@3.92.0: - resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} + node-abi@3.93.0: + resolution: {integrity: sha512-Cu6yUpX5Iavugm8BeX7c0wgU9CvOqfd1yM6A1d2q2ZMjym7GjpASv2GdRcTq3Fx+Sb5OgBkEEpw4VnAbY6Y5RA==} engines: {node: '>=10'} node-addon-api@7.1.1: @@ -9102,8 +9569,8 @@ packages: engines: {node: '>=10.5.0'} deprecated: Use your platform's native DOMException instead - node-exports-info@1.6.0: - resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + node-exports-info@1.6.2: + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} engines: {node: '>= 0.4'} node-fetch@2.7.0: @@ -9122,8 +9589,8 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.47: - resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + node-releases@2.0.50: + resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==} engines: {node: '>=18'} nopt@7.2.1: @@ -9243,6 +9710,9 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} @@ -9305,9 +9775,6 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parchment@3.0.0: - resolution: {integrity: sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -9439,16 +9906,16 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - playwright-core@1.61.0: - resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==} + playwright-core@1.61.1: + resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} engines: {node: '>=18'} hasBin: true playwright-ctrf-json-reporter@0.0.27: resolution: {integrity: sha512-FZ8KadoHJc7xhf5XM0R9F8XBsTSm4vywa5/fhmeo2nZhN31UmapYwRfxaBsGk6AbsvGmft5G+MVmkBjTJZic/Q==} - playwright@1.61.0: - resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==} + playwright@1.61.1: + resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==} engines: {node: '>=18'} hasBin: true @@ -9700,8 +10167,8 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + postcss@8.5.16: + resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} potpack@1.0.2: @@ -9732,8 +10199,8 @@ packages: prettier: optional: true - prettier@3.8.4: - resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} + prettier@3.9.4: + resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} engines: {node: '>=14'} hasBin: true @@ -9792,6 +10259,45 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prosemirror-changeset@2.4.1: + resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==} + + prosemirror-commands@1.7.1: + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} + + prosemirror-dropcursor@1.8.2: + resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==} + + prosemirror-gapcursor@1.4.1: + resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==} + + prosemirror-history@1.5.0: + resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==} + + prosemirror-inputrules@1.5.1: + resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-model@1.25.9: + resolution: {integrity: sha512-pRTklkDDMMRopyoAcrr9wV/8g/RYgrLHBuJAb5hlEuYZRdm5yqmPjWId83fpBwPpSFqEdja0H7Dfd7z1X/npcA==} + + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + + prosemirror-state@1.4.4: + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + + prosemirror-tables@1.8.5: + resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==} + + prosemirror-transform@1.12.0: + resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==} + + prosemirror-view@1.41.9: + resolution: {integrity: sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==} + proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -9833,8 +10339,8 @@ packages: peerDependencies: react: '>=18.0.0 <19.0.0' - qs@6.15.2: - resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} querystringify@2.2.0: @@ -9852,17 +10358,6 @@ packages: quickselect@3.0.0: resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==} - quill-delta@5.1.0: - resolution: {integrity: sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==} - engines: {node: '>= 12.0.0'} - - quill-resize-module@2.1.3: - resolution: {integrity: sha512-Hrs/pwKqmnEkY8Z5zbp4LxdLSHmQBXGAR5yzEFDeSfWTZIMpi6yII/dqIeXfwQBKAxWAVSxEeWqY7DMZ2GLTaQ==} - - quill@2.0.3: - resolution: {integrity: sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==} - engines: {npm: '>=8.2.3'} - raf@3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} @@ -9873,6 +10368,10 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} + range-parser@1.3.0: + resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} + engines: {node: '>= 0.6'} + raw-body@3.0.2: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} @@ -9932,8 +10431,8 @@ packages: peerDependencies: react: '>=18.0.0 <19.0.0' - react-image-crop@11.0.10: - resolution: {integrity: sha512-+5FfDXUgYLLqBh1Y/uQhIycpHCbXkI50a+nbfkB1C0xXXUTwkisHDo2QCB1SQJyHCqIuia4FeyReqXuMDKWQTQ==} + react-image-crop@11.1.2: + resolution: {integrity: sha512-+0Pc2fxpwKL4u4oLmdKBw8XSwUceFbXbKEHvFOlsl/MGB1OVNic4uBlAPmEHGXYgoJIq+b63xHbc/aJMG0AVkA==} peerDependencies: react: '>=18.0.0 <19.0.0' @@ -10016,6 +10515,48 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + react-scroll-sync@1.0.2: + resolution: {integrity: sha512-uQTyayj/DbkejXFwsQI7o+5+pNC2uXJVsYpiGVOMKTF6NzIoE6cvEsw4NjTEKAX21iSgTJexWIE2WWyD3oBtsA==} + peerDependencies: + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + + react-simple-code-editor@0.14.1: + resolution: {integrity: sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==} + peerDependencies: + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react-test-renderer@19.2.7: resolution: {integrity: sha512-U4TyPDJ9MsC8rFimXuJum8w40aPc9kbOZYO8Pc2/4A884i8hwJsMNA/JNyuOc/f2/37wHvk7HjpVl1V4re7Dig==} peerDependencies: @@ -10051,8 +10592,8 @@ packages: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} - recast@0.23.11: - resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + recast@0.23.12: + resolution: {integrity: sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==} engines: {node: '>= 4'} rechoir@0.6.2: @@ -10220,11 +10761,14 @@ packages: peerDependencies: rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 - rollup@4.62.0: - resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -10293,8 +10837,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -10313,8 +10857,8 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serialize-javascript@7.0.5: - resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} + serialize-javascript@7.0.7: + resolution: {integrity: sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==} engines: {node: '>=20.0.0'} serve-static@1.16.3: @@ -10369,8 +10913,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.4: - resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} + shell-quote@1.9.0: + resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} engines: {node: '>= 0.4'} shelljs@0.10.0: @@ -10637,9 +11181,6 @@ packages: style-inject@0.3.0: resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==} - style-mod@4.1.3: - resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} - stylehacks@5.1.1: resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} engines: {node: ^10 || ^12 || >=14.0} @@ -10693,11 +11234,11 @@ packages: resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} engines: {node: ^14.18.0 || >=16.0.0} - tabbable@6.4.0: - resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tabbable@6.5.0: + resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==} - tar-fs@2.1.4: - resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + tar-fs@2.1.5: + resolution: {integrity: sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==} tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} @@ -10869,8 +11410,8 @@ packages: tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - turbo@2.9.18: - resolution: {integrity: sha512-bwabv6PupzeavybzEoArBAkwq5fnzwf8OFnRtpHwnviFWuwJPFxtyH+aVp36TmIqK3aYYgtTJ3J0m2ysxxSzQg==} + turbo@2.10.2: + resolution: {integrity: sha512-wTExrNrRjB8qzIcg+ZLm0A3GFNLDsWNwdS/RBXB0FPrBDyzk3i96Yx+TxWZC7a0k1SIreFB8ciUbxjmEqTH8IQ==} hasBin: true tweetnacl@1.0.3: @@ -10925,8 +11466,8 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript-eslint@8.61.1: - resolution: {integrity: sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==} + typescript-eslint@8.62.1: + resolution: {integrity: sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -10937,11 +11478,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} - hasBin: true - uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} @@ -11024,6 +11560,26 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '>=18.2.36' + react: '>=18.0.0 <19.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: @@ -11036,8 +11592,8 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} - uuid@14.0.0: - resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true v8-compile-cache-lib@3.0.1: @@ -11292,12 +11848,12 @@ packages: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + yargs@16.2.2: + resolution: {integrity: sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==} engines: {node: '>=10'} - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} engines: {node: '>=12'} yn@3.1.1: @@ -11312,8 +11868,8 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} - zip-a-folder@6.1.1: - resolution: {integrity: sha512-8hjtUn4YQpj8HZvDwtGHhol27oDf+D1x70ldKwA3Bwru6gup62fDVrBTd+BC90/8REgjdCa5ep7EsBiGHudSdA==} + zip-a-folder@6.1.2: + resolution: {integrity: sha512-M2MC7HTJTFux2UrSmjBamaoLr71g9rkr3n6TaAvArvkgaxuuaZCMrWhYyA2SNYp+NISUave9xuQw3kWS7c25fw==} hasBin: true zod-to-json-schema@3.25.2: @@ -11344,10 +11900,10 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 - '@axe-core/playwright@4.11.3(playwright-core@1.61.0)': + '@axe-core/playwright@4.12.1(playwright-core@1.61.1)': dependencies: - axe-core: 4.11.4 - playwright-core: 1.61.0 + axe-core: 4.12.1 + playwright-core: 1.61.1 '@babel/code-frame@7.29.7': dependencies: @@ -11401,7 +11957,7 @@ snapshots: dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 - browserslist: 4.28.2 + browserslist: 4.28.4 lru-cache: 5.1.1 semver: 6.3.1 @@ -12233,98 +12789,15 @@ snapshots: dependencies: commander: 2.20.3 - '@codemirror/autocomplete@6.20.3': - dependencies: - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 - - '@codemirror/commands@6.10.3': - dependencies: - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 - - '@codemirror/lang-css@6.3.1': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@lezer/common': 1.5.2 - '@lezer/css': 1.3.3 - - '@codemirror/lang-html@6.4.11': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/lang-css': 6.3.1 - '@codemirror/lang-javascript': 6.2.5 - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 - '@lezer/css': 1.3.3 - '@lezer/html': 1.3.13 - - '@codemirror/lang-javascript@6.2.5': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/language': 6.12.3 - '@codemirror/lint': 6.9.7 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 - '@lezer/javascript': 1.5.4 - - '@codemirror/language@6.12.3': - dependencies: - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - style-mod: 4.1.3 - - '@codemirror/lint@6.9.7': - dependencies: - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - crelt: 1.0.6 - - '@codemirror/search@6.7.0': - dependencies: - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - crelt: 1.0.6 - - '@codemirror/state@6.6.0': - dependencies: - '@marijn/find-cluster-break': 1.0.2 - - '@codemirror/theme-one-dark@6.1.3': - dependencies: - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/highlight': 1.2.3 - - '@codemirror/view@6.43.1': - dependencies: - '@codemirror/state': 6.6.0 - crelt: 1.0.6 - style-mod: 4.1.3 - w3c-keyname: 2.2.8 - - '@commitlint/cli@19.8.1(@types/node@24.12.4)(typescript@6.0.3)': + '@commitlint/cli@19.8.1(@types/node@24.12.4)(typescript@5.9.3)': dependencies: '@commitlint/format': 19.8.1 '@commitlint/lint': 19.8.1 - '@commitlint/load': 19.8.1(@types/node@24.12.4)(typescript@6.0.3) + '@commitlint/load': 19.8.1(@types/node@24.12.4)(typescript@5.9.3) '@commitlint/read': 19.8.1 '@commitlint/types': 19.8.1 tinyexec: 1.2.4 - yargs: 17.7.2 + yargs: 17.7.3 transitivePeerDependencies: - '@types/node' - typescript @@ -12358,7 +12831,7 @@ snapshots: '@commitlint/is-ignored@19.8.1': dependencies: '@commitlint/types': 19.8.1 - semver: 7.8.4 + semver: 7.8.5 '@commitlint/lint@19.8.1': dependencies: @@ -12367,15 +12840,15 @@ snapshots: '@commitlint/rules': 19.8.1 '@commitlint/types': 19.8.1 - '@commitlint/load@19.8.1(@types/node@24.12.4)(typescript@6.0.3)': + '@commitlint/load@19.8.1(@types/node@24.12.4)(typescript@5.9.3)': dependencies: '@commitlint/config-validator': 19.8.1 '@commitlint/execute-rule': 19.8.1 '@commitlint/resolve-extends': 19.8.1 '@commitlint/types': 19.8.1 chalk: 5.6.2 - cosmiconfig: 9.0.2(typescript@6.0.3) - cosmiconfig-typescript-loader: 6.3.0(@types/node@24.12.4)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3) + cosmiconfig: 9.0.2(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.3.0(@types/node@24.12.4)(cosmiconfig@9.0.2(typescript@5.9.3))(typescript@5.9.3) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -12497,7 +12970,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.2.0 + js-yaml: 4.3.0 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -12533,7 +13006,7 @@ snapshots: '@floating-ui/utils': 0.2.11 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tabbable: 6.4.0 + tabbable: 6.5.0 '@floating-ui/react@0.27.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -12541,7 +13014,7 @@ snapshots: '@floating-ui/utils': 0.2.11 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tabbable: 6.4.0 + tabbable: 6.5.0 '@floating-ui/utils@0.2.11': {} @@ -12558,9 +13031,9 @@ snapshots: jest-mock: 29.7.0 jest-util: 29.7.0 - '@hono/node-server@1.19.14(hono@4.12.25)': + '@hono/node-server@1.19.14(hono@4.12.27)': dependencies: - hono: 4.12.25 + hono: 4.12.27 '@humanfs/core@0.19.2': dependencies: @@ -12600,7 +13073,7 @@ snapshots: camelcase: 5.3.1 find-up: 4.1.0 get-package-type: 0.1.0 - js-yaml: 3.14.2 + js-yaml: 3.15.0 resolve-from: 5.0.0 '@istanbuljs/schema@0.1.6': {} @@ -12623,7 +13096,7 @@ snapshots: jest-util: 30.3.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -12637,7 +13110,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + jest-config: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12694,7 +13167,7 @@ snapshots: - ts-node optional: true - '@jest/core@30.3.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3))': + '@jest/core@30.3.0(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3))': dependencies: '@jest/console': 30.3.0 '@jest/pattern': 30.0.1 @@ -12709,7 +13182,7 @@ snapshots: exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.3.0 - jest-config: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest-config: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) jest-haste-map: 30.3.0 jest-message-util: 30.3.0 jest-regex-util: 30.0.1 @@ -13051,34 +13524,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@lezer/common@1.5.2': {} - - '@lezer/css@1.3.3': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - - '@lezer/highlight@1.2.3': - dependencies: - '@lezer/common': 1.5.2 - - '@lezer/html@1.3.13': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - - '@lezer/javascript@1.5.4': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - - '@lezer/lr@1.4.10': - dependencies: - '@lezer/common': 1.5.2 - '@mapbox/geojson-rewind@0.5.2': dependencies: get-stream: 6.0.1 @@ -13086,7 +13531,7 @@ snapshots: '@mapbox/geojson-types@1.0.2': {} - '@mapbox/jsonlint-lines-primitives@2.0.2': {} + '@mapbox/jsonlint-lines-primitives@2.0.3': {} '@mapbox/mapbox-gl-supported@1.5.0(mapbox-gl@1.13.3)': dependencies: @@ -13110,7 +13555,7 @@ snapshots: '@maplibre/maplibre-gl-style-spec@20.4.0': dependencies: - '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/jsonlint-lines-primitives': 2.0.3 '@mapbox/unitbezier': 0.0.1 json-stringify-pretty-compact: 4.0.0 minimist: 1.2.8 @@ -13118,11 +13563,9 @@ snapshots: rw: 1.3.3 tinyqueue: 3.0.0 - '@marijn/find-cluster-break@1.0.2': {} - '@melloware/coloris@0.25.0': {} - '@mendix/pluggable-widgets-tools@11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.4)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': + '@mendix/pluggable-widgets-tools@11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.4)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': dependencies: '@babel/core': 7.29.7 '@babel/eslint-parser': 7.29.7(@babel/core@7.29.7)(eslint@9.39.4(jiti@2.6.1)) @@ -13131,68 +13574,68 @@ snapshots: '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) '@babel/preset-env': 7.29.7(@babel/core@7.29.7) '@babel/preset-react': 7.29.7(@babel/core@7.29.7) - '@prettier/plugin-xml': 3.4.2(prettier@3.8.4) + '@prettier/plugin-xml': 3.4.2(prettier@3.9.4) '@react-native/babel-preset': 0.77.3(@babel/core@7.29.7)(@babel/preset-env@7.29.7(@babel/core@7.29.7)) - '@rollup/plugin-alias': 5.1.1(rollup@4.62.0) - '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.0) - '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.0) - '@rollup/plugin-image': 3.0.3(rollup@4.62.0) - '@rollup/plugin-json': 6.1.0(rollup@4.62.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.62.0) - '@rollup/plugin-terser': 1.0.0(rollup@4.62.0) - '@rollup/plugin-typescript': 12.3.0(rollup@4.62.0)(tslib@2.8.1)(typescript@5.9.3) - '@rollup/plugin-url': 8.0.2(rollup@4.62.0) - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/plugin-alias': 5.1.1(rollup@4.62.2) + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.2) + '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.2) + '@rollup/plugin-image': 3.0.3(rollup@4.62.2) + '@rollup/plugin-json': 6.1.0(rollup@4.62.2) + '@rollup/plugin-node-resolve': 15.3.1(rollup@4.62.2) + '@rollup/plugin-terser': 1.0.0(rollup@4.62.2) + '@rollup/plugin-typescript': 12.3.0(rollup@4.62.2)(tslib@2.8.1)(typescript@5.9.3) + '@rollup/plugin-url': 8.0.2(rollup@4.62.2) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) '@testing-library/dom': 10.4.1 '@testing-library/jest-dom': 6.9.1 '@testing-library/react': 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react-test-renderer@19.2.7(react@18.3.1))(react@18.3.1) + '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react-test-renderer@19.2.7(react@18.3.1))(react@18.3.1) '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) '@types/semver': 7.7.1 '@types/testing-library__jest-dom': 5.14.9 - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) ansi-colors: 4.1.3 babel-jest: 29.7.0(@babel/core@7.29.7) big.js: 6.2.2 core-js: 3.49.0 dotenv: 17.4.2 fast-glob: 3.3.3 - fs-extra: 11.3.5 + fs-extra: 11.3.6 identity-obj-proxy: 3.0.0 jasmine: 3.99.0 jasmine-core: 3.99.1 - jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) jest-environment-jsdom: 30.3.0(canvas@3.2.3) jest-jasmine2: 30.3.0 jest-junit: 17.0.0 make-dir: 5.1.0 - mendix: 11.10.0 + mendix: 11.12.0 mime: 4.1.0 - postcss: 8.5.15 - postcss-import: 14.1.0(postcss@8.5.15) - postcss-url: 10.1.4(postcss@8.5.15) + postcss: 8.5.16 + postcss-import: 14.1.0(postcss@8.5.16) + postcss-url: 10.1.4(postcss@8.5.16) react-test-renderer: 19.2.7(react@18.3.1) recursive-copy: 2.0.14 resolve: 1.22.12 - rollup: 4.62.0 + rollup: 4.62.2 rollup-plugin-clear: 2.0.7 rollup-plugin-command: 1.1.3 - rollup-plugin-license: 3.7.1(picomatch@4.0.4)(rollup@4.62.0) + rollup-plugin-license: 3.7.1(picomatch@4.0.4)(rollup@4.62.2) rollup-plugin-livereload: 2.0.5 - rollup-plugin-postcss: 4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + rollup-plugin-postcss: 4.0.2(postcss@8.5.16)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) rollup-plugin-re: 1.0.7 sass: 1.101.0 - semver: 7.8.4 + semver: 7.8.5 shelljs: 0.10.0 shx: 0.4.0 - ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@29.7.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3) - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3) + ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@29.7.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3) typescript: 5.9.3 xml2js: 0.6.2 - zip-a-folder: 6.1.1 + zip-a-folder: 6.1.2 transitivePeerDependencies: - '@jest/transform' - '@jest/types' @@ -13217,7 +13660,7 @@ snapshots: - tslib - utf-8-validate - '@mendix/pluggable-widgets-tools@11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': + '@mendix/pluggable-widgets-tools@11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.43)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.9.4)(react-dom@18.3.1(react@18.3.1))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': dependencies: '@babel/core': 7.29.7 '@babel/eslint-parser': 7.29.7(@babel/core@7.29.7)(eslint@9.39.4(jiti@2.6.1)) @@ -13226,68 +13669,68 @@ snapshots: '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) '@babel/preset-env': 7.29.7(@babel/core@7.29.7) '@babel/preset-react': 7.29.7(@babel/core@7.29.7) - '@prettier/plugin-xml': 3.4.2(prettier@3.8.4) + '@prettier/plugin-xml': 3.4.2(prettier@3.9.4) '@react-native/babel-preset': 0.77.3(@babel/core@7.29.7)(@babel/preset-env@7.29.7(@babel/core@7.29.7)) - '@rollup/plugin-alias': 5.1.1(rollup@4.62.0) - '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.0) - '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.0) - '@rollup/plugin-image': 3.0.3(rollup@4.62.0) - '@rollup/plugin-json': 6.1.0(rollup@4.62.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.62.0) - '@rollup/plugin-terser': 1.0.0(rollup@4.62.0) - '@rollup/plugin-typescript': 12.3.0(rollup@4.62.0)(tslib@2.8.1)(typescript@5.9.3) - '@rollup/plugin-url': 8.0.2(rollup@4.62.0) - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/plugin-alias': 5.1.1(rollup@4.62.2) + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.2) + '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.2) + '@rollup/plugin-image': 3.0.3(rollup@4.62.2) + '@rollup/plugin-json': 6.1.0(rollup@4.62.2) + '@rollup/plugin-node-resolve': 15.3.1(rollup@4.62.2) + '@rollup/plugin-terser': 1.0.0(rollup@4.62.2) + '@rollup/plugin-typescript': 12.3.0(rollup@4.62.2)(tslib@2.8.1)(typescript@5.9.3) + '@rollup/plugin-url': 8.0.2(rollup@4.62.2) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) '@testing-library/dom': 10.4.1 '@testing-library/jest-dom': 6.9.1 '@testing-library/react': 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react-test-renderer@19.2.7(react@18.3.1))(react@18.3.1) + '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react-test-renderer@19.2.7(react@18.3.1))(react@18.3.1) '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) '@types/semver': 7.7.1 '@types/testing-library__jest-dom': 5.14.9 - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) ansi-colors: 4.1.3 babel-jest: 29.7.0(@babel/core@7.29.7) big.js: 6.2.2 core-js: 3.49.0 dotenv: 17.4.2 fast-glob: 3.3.3 - fs-extra: 11.3.5 + fs-extra: 11.3.6 identity-obj-proxy: 3.0.0 jasmine: 3.99.0 jasmine-core: 3.99.1 - jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) jest-environment-jsdom: 30.3.0(canvas@3.2.3) jest-jasmine2: 30.3.0 jest-junit: 17.0.0 make-dir: 5.1.0 - mendix: 11.10.0 + mendix: 11.12.0 mime: 4.1.0 - postcss: 8.5.15 - postcss-import: 14.1.0(postcss@8.5.15) - postcss-url: 10.1.4(postcss@8.5.15) + postcss: 8.5.16 + postcss-import: 14.1.0(postcss@8.5.16) + postcss-url: 10.1.4(postcss@8.5.16) react-test-renderer: 19.2.7(react@18.3.1) recursive-copy: 2.0.14 resolve: 1.22.12 - rollup: 4.62.0 + rollup: 4.62.2 rollup-plugin-clear: 2.0.7 rollup-plugin-command: 1.1.3 - rollup-plugin-license: 3.7.1(rollup@4.62.0) + rollup-plugin-license: 3.7.1(rollup@4.62.2) rollup-plugin-livereload: 2.0.5 - rollup-plugin-postcss: 4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + rollup-plugin-postcss: 4.0.2(postcss@8.5.16)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) rollup-plugin-re: 1.0.7 sass: 1.101.0 - semver: 7.8.4 + semver: 7.8.5 shelljs: 0.10.0 shx: 0.4.0 - ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@29.7.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3) - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3) + ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@29.7.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3) typescript: 5.9.3 xml2js: 0.6.2 - zip-a-folder: 6.1.1 + zip-a-folder: 6.1.2 transitivePeerDependencies: - '@jest/transform' - '@jest/types' @@ -13314,7 +13757,7 @@ snapshots: '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.25) + '@hono/node-server': 1.19.14(hono@4.12.27) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -13324,7 +13767,7 @@ snapshots: eventsource-parser: 3.1.0 express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.25 + hono: 4.12.27 jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -13334,11 +13777,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.2 + '@tybys/wasm-util': 0.10.3 optional: true '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': @@ -13427,9 +13870,9 @@ snapshots: '@pkgr/core@0.3.6': {} - '@playwright/test@1.61.0': + '@playwright/test@1.61.1': dependencies: - playwright: 1.61.0 + playwright: 1.61.1 '@plotly/d3-sankey-circular@0.33.1': dependencies: @@ -13450,7 +13893,7 @@ snapshots: dependencies: '@mapbox/geojson-rewind': 0.5.2 '@mapbox/geojson-types': 1.0.2 - '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/jsonlint-lines-primitives': 2.0.3 '@mapbox/mapbox-gl-supported': 1.5.0(mapbox-gl@1.13.3) '@mapbox/point-geometry': 0.1.0 '@mapbox/tiny-sdf': 1.2.5 @@ -13490,10 +13933,33 @@ snapshots: '@popperjs/core@2.11.8': {} - '@prettier/plugin-xml@3.4.2(prettier@3.8.4)': + '@prettier/plugin-xml@3.4.2(prettier@3.9.4)': dependencies: '@xml-tools/parser': 1.0.11 - prettier: 3.8.4 + prettier: 3.9.4 + + '@radix-ui/primitive@1.1.4': {} + + '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collection@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@18.3.1)': dependencies: @@ -13507,7 +13973,128 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-dismissable-layer@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-dropdown-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-focus-scope@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-popper@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/rect': 1.1.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@18.3.1) react: 18.3.1 @@ -13516,10 +14103,27 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-progress@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-progress@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-roving-focus@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@18.3.1) '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: @@ -13533,6 +14137,49 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@18.3.1)': + dependencies: + '@radix-ui/rect': 1.1.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/rect@1.1.2': {} + '@rc-component/motion@1.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@rc-component/util': 1.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -13661,7 +14308,7 @@ snapshots: invariant: 2.2.4 jscodeshift: 17.3.0(@babel/preset-env@7.29.7(@babel/core@7.29.7)) nullthrows: 1.1.1 - yargs: 17.7.2 + yargs: 17.7.3 transitivePeerDependencies: - supports-color @@ -13673,7 +14320,7 @@ snapshots: invariant: 2.2.4 nullthrows: 1.1.1 tinyglobby: 0.2.17 - yargs: 17.7.2 + yargs: 17.7.3 '@react-native/community-cli-plugin@0.86.0': dependencies: @@ -13683,7 +14330,7 @@ snapshots: metro: 0.84.4 metro-config: 0.84.4 metro-core: 0.84.4 - semver: 7.8.4 + semver: 7.8.5 transitivePeerDependencies: - bufferutil - supports-color @@ -13738,24 +14385,24 @@ snapshots: dequal: 2.0.3 react: 18.3.1 - '@rollup/plugin-alias@5.1.1(rollup@4.62.0)': + '@rollup/plugin-alias@5.1.1(rollup@4.62.2)': optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.0)': + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.2)': dependencies: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) optionalDependencies: '@types/babel__core': 7.20.5 - rollup: 4.62.0 + rollup: 4.62.2 transitivePeerDependencies: - supports-color - '@rollup/plugin-commonjs@28.0.9(rollup@4.62.0)': + '@rollup/plugin-commonjs@28.0.9(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.4) @@ -13763,11 +14410,11 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.4 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-commonjs@29.0.3(rollup@4.62.0)': + '@rollup/plugin-commonjs@29.0.3(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.4) @@ -13775,162 +14422,162 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.4 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-image@3.0.3(rollup@4.62.0)': + '@rollup/plugin-image@3.0.3(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) mini-svg-data-uri: 1.4.4 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-json@6.1.0(rollup@4.62.0)': + '@rollup/plugin-json@6.1.0(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.62.0)': + '@rollup/plugin-node-resolve@15.3.1(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.12 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-node-resolve@16.0.3(rollup@4.62.0)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.12 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-replace@6.0.3(rollup@4.62.0)': + '@rollup/plugin-replace@6.0.3(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) magic-string: 0.30.21 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-terser@0.4.4(rollup@4.62.0)': + '@rollup/plugin-terser@0.4.4(rollup@4.62.2)': dependencies: serialize-javascript: 6.0.2 smob: 1.6.2 terser: 5.48.0 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-terser@1.0.0(rollup@4.62.0)': + '@rollup/plugin-terser@1.0.0(rollup@4.62.2)': dependencies: - serialize-javascript: 7.0.5 + serialize-javascript: 7.0.7 smob: 1.6.2 terser: 5.48.0 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/plugin-typescript@12.3.0(rollup@4.62.0)(tslib@2.8.1)(typescript@5.9.3)': + '@rollup/plugin-typescript@12.3.0(rollup@4.62.2)(tslib@2.8.1)(typescript@5.9.3)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) resolve: 1.22.12 typescript: 5.9.3 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 tslib: 2.8.1 - '@rollup/plugin-url@8.0.2(rollup@4.62.0)': + '@rollup/plugin-url@8.0.2(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) make-dir: 3.1.0 mime: 3.0.0 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/pluginutils@5.4.0(rollup@4.62.0)': + '@rollup/pluginutils@5.4.0(rollup@4.62.2)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.4 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.2 - '@rollup/rollup-android-arm-eabi@4.62.0': + '@rollup/rollup-android-arm-eabi@4.62.2': optional: true - '@rollup/rollup-android-arm64@4.62.0': + '@rollup/rollup-android-arm64@4.62.2': optional: true - '@rollup/rollup-darwin-arm64@4.62.0': + '@rollup/rollup-darwin-arm64@4.62.2': optional: true - '@rollup/rollup-darwin-x64@4.62.0': + '@rollup/rollup-darwin-x64@4.62.2': optional: true - '@rollup/rollup-freebsd-arm64@4.62.0': + '@rollup/rollup-freebsd-arm64@4.62.2': optional: true - '@rollup/rollup-freebsd-x64@4.62.0': + '@rollup/rollup-freebsd-x64@4.62.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.62.0': + '@rollup/rollup-linux-arm-musleabihf@4.62.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.62.0': + '@rollup/rollup-linux-arm64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.62.0': + '@rollup/rollup-linux-arm64-musl@4.62.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.62.0': + '@rollup/rollup-linux-loong64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-loong64-musl@4.62.0': + '@rollup/rollup-linux-loong64-musl@4.62.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.62.0': + '@rollup/rollup-linux-ppc64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-ppc64-musl@4.62.0': + '@rollup/rollup-linux-ppc64-musl@4.62.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.62.0': + '@rollup/rollup-linux-riscv64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.62.0': + '@rollup/rollup-linux-riscv64-musl@4.62.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.62.0': + '@rollup/rollup-linux-s390x-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.62.0': + '@rollup/rollup-linux-x64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-musl@4.62.0': + '@rollup/rollup-linux-x64-musl@4.62.2': optional: true - '@rollup/rollup-openbsd-x64@4.62.0': + '@rollup/rollup-openbsd-x64@4.62.2': optional: true - '@rollup/rollup-openharmony-arm64@4.62.0': + '@rollup/rollup-openharmony-arm64@4.62.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.62.0': + '@rollup/rollup-win32-arm64-msvc@4.62.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.62.0': + '@rollup/rollup-win32-ia32-msvc@4.62.2': optional: true - '@rollup/rollup-win32-x64-gnu@4.62.0': + '@rollup/rollup-win32-x64-gnu@4.62.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.62.0': + '@rollup/rollup-win32-x64-msvc@4.62.2': optional: true '@rtsao/scc@1.1.0': {} @@ -13951,118 +14598,353 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@swc/core-darwin-arm64@1.15.41': + '@swc/core-darwin-arm64@1.15.43': + optional: true + + '@swc/core-darwin-x64@1.15.43': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.43': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.43': optional: true - '@swc/core-darwin-x64@1.15.41': + '@swc/core-linux-arm64-musl@1.15.43': optional: true - '@swc/core-linux-arm-gnueabihf@1.15.41': + '@swc/core-linux-ppc64-gnu@1.15.43': optional: true - '@swc/core-linux-arm64-gnu@1.15.41': + '@swc/core-linux-s390x-gnu@1.15.43': optional: true - '@swc/core-linux-arm64-musl@1.15.41': - optional: true + '@swc/core-linux-x64-gnu@1.15.43': + optional: true + + '@swc/core-linux-x64-musl@1.15.43': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.43': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.43': + optional: true + + '@swc/core-win32-x64-msvc@1.15.43': + optional: true + + '@swc/core@1.15.43': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.27 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.43 + '@swc/core-darwin-x64': 1.15.43 + '@swc/core-linux-arm-gnueabihf': 1.15.43 + '@swc/core-linux-arm64-gnu': 1.15.43 + '@swc/core-linux-arm64-musl': 1.15.43 + '@swc/core-linux-ppc64-gnu': 1.15.43 + '@swc/core-linux-s390x-gnu': 1.15.43 + '@swc/core-linux-x64-gnu': 1.15.43 + '@swc/core-linux-x64-musl': 1.15.43 + '@swc/core-win32-arm64-msvc': 1.15.43 + '@swc/core-win32-ia32-msvc': 1.15.43 + '@swc/core-win32-x64-msvc': 1.15.43 + + '@swc/counter@0.1.3': {} + + '@swc/jest@0.2.39(@swc/core@1.15.43)': + dependencies: + '@jest/create-cache-key-function': 30.4.1 + '@swc/core': 1.15.43 + '@swc/counter': 0.1.3 + jsonc-parser: 3.3.1 + + '@swc/types@0.1.27': + dependencies: + '@swc/counter': 0.1.3 + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.5.0 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react-native@13.3.3(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react-test-renderer@19.2.7(react@18.3.1))(react@18.3.1)': + dependencies: + jest-matcher-utils: 30.4.1 + picocolors: 1.1.1 + pretty-format: 30.4.1 + react: 18.3.1 + react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1) + react-test-renderer: 19.2.7(react@18.3.1) + redent: 3.0.0 + optionalDependencies: + jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@testing-library/dom': 10.4.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + + '@tiptap/core@3.27.1(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-blockquote@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-bold@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-bubble-menu@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@floating-ui/dom': 1.7.6 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + optional: true + + '@tiptap/extension-bullet-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-code-block@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-code@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-color@3.27.1(@tiptap/extension-text-style@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)))': + dependencies: + '@tiptap/extension-text-style': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + + '@tiptap/extension-document@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-dropcursor@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-floating-menu@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@floating-ui/dom': 1.7.6 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + optional: true + + '@tiptap/extension-font-family@3.27.1(@tiptap/extension-text-style@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)))': + dependencies: + '@tiptap/extension-text-style': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + + '@tiptap/extension-gapcursor@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-hard-break@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-heading@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-highlight@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-horizontal-rule@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-image@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-italic@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-link@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + linkifyjs: 4.3.3 + + '@tiptap/extension-list-item@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-list-keymap@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + + '@tiptap/extension-ordered-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + + '@tiptap/extension-paragraph@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-strike@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + + '@tiptap/extension-subscript@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 - '@swc/core-linux-ppc64-gnu@1.15.41': - optional: true + '@tiptap/extension-superscript@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 - '@swc/core-linux-s390x-gnu@1.15.41': - optional: true + '@tiptap/extension-table-cell@3.27.1(@tiptap/extension-table@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-table': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@swc/core-linux-x64-gnu@1.15.41': - optional: true + '@tiptap/extension-table-header@3.27.1(@tiptap/extension-table@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-table': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@swc/core-linux-x64-musl@1.15.41': - optional: true + '@tiptap/extension-table-row@3.27.1(@tiptap/extension-table@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-table': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@swc/core-win32-arm64-msvc@1.15.41': - optional: true + '@tiptap/extension-table@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 - '@swc/core-win32-ia32-msvc@1.15.41': - optional: true + '@tiptap/extension-task-item@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@swc/core-win32-x64-msvc@1.15.41': - optional: true + '@tiptap/extension-task-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) - '@swc/core@1.15.41': + '@tiptap/extension-text-align@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.27 - optionalDependencies: - '@swc/core-darwin-arm64': 1.15.41 - '@swc/core-darwin-x64': 1.15.41 - '@swc/core-linux-arm-gnueabihf': 1.15.41 - '@swc/core-linux-arm64-gnu': 1.15.41 - '@swc/core-linux-arm64-musl': 1.15.41 - '@swc/core-linux-ppc64-gnu': 1.15.41 - '@swc/core-linux-s390x-gnu': 1.15.41 - '@swc/core-linux-x64-gnu': 1.15.41 - '@swc/core-linux-x64-musl': 1.15.41 - '@swc/core-win32-arm64-msvc': 1.15.41 - '@swc/core-win32-ia32-msvc': 1.15.41 - '@swc/core-win32-x64-msvc': 1.15.41 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@swc/counter@0.1.3': {} + '@tiptap/extension-text-style@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@swc/jest@0.2.39(@swc/core@1.15.41)': + '@tiptap/extension-text@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@jest/create-cache-key-function': 30.4.1 - '@swc/core': 1.15.41 - '@swc/counter': 0.1.3 - jsonc-parser: 3.3.1 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@swc/types@0.1.27': + '@tiptap/extension-underline@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@swc/counter': 0.1.3 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@testing-library/dom@10.4.1': + '@tiptap/extension-youtube@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))': dependencies: - '@babel/code-frame': 7.29.7 - '@babel/runtime': 7.29.7 - '@types/aria-query': 5.0.4 - aria-query: 5.3.0 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - picocolors: 1.1.1 - pretty-format: 27.5.1 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) - '@testing-library/jest-dom@6.9.1': + '@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)': dependencies: - '@adobe/css-tools': 4.5.0 - aria-query: 5.3.2 - css.escape: 1.5.1 - dom-accessibility-api: 0.6.3 - picocolors: 1.1.1 - redent: 3.0.0 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 - '@testing-library/react-native@13.3.3(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(react-native@0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1))(react-test-renderer@19.2.7(react@18.3.1))(react@18.3.1)': + '@tiptap/pm@3.27.1': dependencies: - jest-matcher-utils: 30.4.1 - picocolors: 1.1.1 - pretty-format: 30.4.1 - react: 18.3.1 - react-native: 0.86.0(@babel/core@7.29.7)(@types/react@19.2.17)(react@18.3.1) - react-test-renderer: 19.2.7(react@18.3.1) - redent: 3.0.0 - optionalDependencies: - jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + prosemirror-changeset: 2.4.1 + prosemirror-commands: 1.7.1 + prosemirror-dropcursor: 1.8.2 + prosemirror-gapcursor: 1.4.1 + prosemirror-history: 1.5.0 + prosemirror-inputrules: 1.5.1 + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.9 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.4 + prosemirror-tables: 1.8.5 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tiptap/react@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.29.7 - '@testing-library/dom': 10.4.1 + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/use-sync-external-store': 0.0.6 + fast-equals: 5.4.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': - dependencies: - '@testing-library/dom': 10.4.1 + '@tiptap/extension-bubble-menu': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-floating-menu': 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + transitivePeerDependencies: + - '@floating-ui/dom' + + '@tiptap/starter-kit@3.27.1': + dependencies: + '@tiptap/core': 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/extension-blockquote': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-bold': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-bullet-list': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-code': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-code-block': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-document': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-dropcursor': 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-gapcursor': 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-hard-break': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-heading': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-horizontal-rule': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-italic': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-link': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/extension-list-item': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-list-keymap': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-ordered-list': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) + '@tiptap/extension-paragraph': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-strike': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-text': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extension-underline': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1)) + '@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) + '@tiptap/pm': 3.27.1 '@tootallnate/once@2.0.1': {} @@ -14074,22 +14956,22 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@turbo/darwin-64@2.9.18': + '@turbo/darwin-64@2.10.2': optional: true - '@turbo/darwin-arm64@2.9.18': + '@turbo/darwin-arm64@2.10.2': optional: true - '@turbo/linux-64@2.9.18': + '@turbo/linux-64@2.10.2': optional: true - '@turbo/linux-arm64@2.9.18': + '@turbo/linux-arm64@2.10.2': optional: true - '@turbo/windows-64@2.9.18': + '@turbo/windows-64@2.10.2': optional: true - '@turbo/windows-arm64@2.9.18': + '@turbo/windows-arm64@2.10.2': optional: true '@turf/area@7.3.5': @@ -14124,7 +15006,7 @@ snapshots: '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@tybys/wasm-util@0.10.2': + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true @@ -14190,7 +15072,7 @@ snapshots: '@types/minimatch': 3.0.5 '@types/node': 24.12.4 - '@types/google.maps@3.65.1': {} + '@types/google.maps@3.65.2': {} '@types/graceful-fs@4.1.9': dependencies: @@ -14354,6 +15236,8 @@ snapshots: '@types/trusted-types@2.0.7': optional: true + '@types/use-sync-external-store@0.0.6': {} + '@types/warning@3.0.4': {} '@types/whatwg-mimetype@3.0.2': {} @@ -14364,14 +15248,14 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/type-utils': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.62.1 eslint: 9.39.4(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 @@ -14380,82 +15264,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.1 - eslint: 9.39.4(jiti@2.6.1) - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.62.1 debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.1 - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.61.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.62.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) - '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@5.9.3) + '@typescript-eslint/types': 8.62.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.1(typescript@6.0.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) - '@typescript-eslint/types': 8.61.1 - debug: 4.4.3 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.61.1': + '@typescript-eslint/scope-manager@8.62.1': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 - '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.62.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.61.1(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - typescript: 6.0.3 - - '@typescript-eslint/type-utils@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.9.3) @@ -14463,119 +15306,75 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.61.1': {} + '@typescript-eslint/types@8.62.1': {} - '@typescript-eslint/typescript-estree@8.61.1(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.62.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.61.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/project-service': 8.62.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@5.9.3) + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3)': - dependencies: - '@typescript-eslint/project-service': 8.61.1(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 - debug: 4.4.3 - minimatch: 10.2.5 - semver: 7.8.4 - tinyglobby: 0.2.17 - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': + '@typescript-eslint/visitor-keys@8.62.1': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color + '@typescript-eslint/types': 8.62.1 + eslint-visitor-keys: 5.0.1 - '@typescript-eslint/visitor-keys@8.61.1': + '@uiw/color-convert@2.10.3(@babel/runtime@7.29.7)': dependencies: - '@typescript-eslint/types': 8.61.1 - eslint-visitor-keys: 5.0.1 + '@babel/runtime': 7.29.7 - '@uiw/codemirror-extensions-basic-setup@4.25.10(@codemirror/autocomplete@6.20.3)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1)': + '@uiw/react-color-compact@2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.10.3 - '@codemirror/language': 6.12.3 - '@codemirror/lint': 6.9.7 - '@codemirror/search': 6.7.0 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 + '@babel/runtime': 7.29.7 + '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@uiw/react-color-swatch': 2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - '@uiw/codemirror-theme-github@4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1)': + '@uiw/react-color-editable-input-rgba@2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@uiw/codemirror-themes': 4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1) - transitivePeerDependencies: - - '@codemirror/language' - - '@codemirror/state' - - '@codemirror/view' + '@babel/runtime': 7.29.7 + '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - '@uiw/codemirror-themes@4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1)': + '@uiw/react-color-editable-input@2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 + '@babel/runtime': 7.29.7 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - '@uiw/react-codemirror@4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.1)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@uiw/react-color-swatch@2.10.3(@babel/runtime@7.29.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.29.7 - '@codemirror/commands': 6.10.3 - '@codemirror/state': 6.6.0 - '@codemirror/theme-one-dark': 6.1.3 - '@codemirror/view': 6.43.1 - '@uiw/codemirror-extensions-basic-setup': 4.25.10(@codemirror/autocomplete@6.20.3)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1) - codemirror: 6.0.2 + '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - transitivePeerDependencies: - - '@codemirror/autocomplete' - - '@codemirror/language' - - '@codemirror/lint' - - '@codemirror/search' - '@ungap/structured-clone@1.3.1': {} + '@ungap/structured-clone@1.3.2': {} '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true @@ -14635,7 +15434,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': @@ -14649,7 +15448,7 @@ snapshots: '@vis.gl/react-google-maps@0.8.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@types/google.maps': 3.65.1 + '@types/google.maps': 3.65.2 fast-deep-equal: 3.1.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -14726,7 +15525,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 + fast-uri: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -14763,6 +15562,10 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -14878,7 +15681,7 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.11.4: {} + axe-core@4.12.1: {} babel-jest@29.7.0(@babel/core@7.29.7): dependencies: @@ -15022,7 +15825,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.37: {} + baseline-browser-mapping@2.10.40: {} big.js@5.2.2: {} @@ -15055,7 +15858,7 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.15.2 + qs: 6.15.3 raw-body: 3.0.2 type-is: 2.1.0 transitivePeerDependencies: @@ -15072,7 +15875,7 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.6: + brace-expansion@5.0.7: dependencies: balanced-match: 4.0.4 @@ -15087,13 +15890,13 @@ snapshots: brandi@5.1.0: {} - browserslist@4.28.2: + browserslist@4.28.4: dependencies: - baseline-browser-mapping: 2.10.37 - caniuse-lite: 1.0.30001799 - electron-to-chromium: 1.5.373 - node-releases: 2.0.47 - update-browserslist-db: 1.2.3(browserslist@4.28.2) + baseline-browser-mapping: 2.10.40 + caniuse-lite: 1.0.30001800 + electron-to-chromium: 1.5.383 + node-releases: 2.0.50 + update-browserslist-db: 1.2.3(browserslist@4.28.4) bs-logger@0.2.6: dependencies: @@ -15137,12 +15940,12 @@ snapshots: caniuse-api@3.0.0: dependencies: - browserslist: 4.28.2 - caniuse-lite: 1.0.30001799 + browserslist: 4.28.4 + caniuse-lite: 1.0.30001800 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001799: {} + caniuse-lite@1.0.30001800: {} canvas@3.2.3: dependencies: @@ -15251,16 +16054,6 @@ snapshots: co@4.6.0: {} - codemirror@6.0.2: - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.10.3 - '@codemirror/language': 6.12.3 - '@codemirror/lint': 6.9.7 - '@codemirror/search': 6.7.0 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - collect-v8-coverage@1.0.3: {} color-alpha@1.1.3: @@ -15360,7 +16153,7 @@ snapshots: spawn-command: 0.0.2 supports-color: 8.1.1 tree-kill: 1.2.2 - yargs: 16.2.0 + yargs: 16.2.2 config-chain@1.1.13: dependencies: @@ -15412,7 +16205,7 @@ snapshots: core-js-compat@3.49.0: dependencies: - browserslist: 4.28.2 + browserslist: 4.28.4 core-js@3.49.0: {} @@ -15423,31 +16216,31 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cosmiconfig-typescript-loader@6.3.0(@types/node@24.12.4)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3): + cosmiconfig-typescript-loader@6.3.0(@types/node@24.12.4)(cosmiconfig@9.0.2(typescript@5.9.3))(typescript@5.9.3): dependencies: '@types/node': 24.12.4 - cosmiconfig: 9.0.2(typescript@6.0.3) + cosmiconfig: 9.0.2(typescript@5.9.3) jiti: 2.6.1 - typescript: 6.0.3 + typescript: 5.9.3 - cosmiconfig@9.0.2(typescript@6.0.3): + cosmiconfig@9.0.2(typescript@5.9.3): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.2.0 + js-yaml: 4.3.0 parse-json: 5.2.0 optionalDependencies: - typescript: 6.0.3 + typescript: 5.9.3 country-regex@1.1.0: {} - create-jest@29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)): + create-jest@29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + jest-config: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -15458,8 +16251,6 @@ snapshots: create-require@1.1.1: {} - crelt@1.0.6: {} - cross-env@7.0.3: dependencies: cross-spawn: 7.0.6 @@ -15480,9 +16271,9 @@ snapshots: cross-zip@4.0.1: {} - css-declaration-sorter@6.4.1(postcss@8.5.15): + css-declaration-sorter@6.4.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 css-font-size-keywords@1.0.0: {} @@ -15531,48 +16322,48 @@ snapshots: cssfontparser@1.2.1: {} - cssnano-preset-default@5.2.14(postcss@8.5.15): - dependencies: - css-declaration-sorter: 6.4.1(postcss@8.5.15) - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 - postcss-calc: 8.2.4(postcss@8.5.15) - postcss-colormin: 5.3.1(postcss@8.5.15) - postcss-convert-values: 5.1.3(postcss@8.5.15) - postcss-discard-comments: 5.1.2(postcss@8.5.15) - postcss-discard-duplicates: 5.1.0(postcss@8.5.15) - postcss-discard-empty: 5.1.1(postcss@8.5.15) - postcss-discard-overridden: 5.1.0(postcss@8.5.15) - postcss-merge-longhand: 5.1.7(postcss@8.5.15) - postcss-merge-rules: 5.1.4(postcss@8.5.15) - postcss-minify-font-values: 5.1.0(postcss@8.5.15) - postcss-minify-gradients: 5.1.1(postcss@8.5.15) - postcss-minify-params: 5.1.4(postcss@8.5.15) - postcss-minify-selectors: 5.2.1(postcss@8.5.15) - postcss-normalize-charset: 5.1.0(postcss@8.5.15) - postcss-normalize-display-values: 5.1.0(postcss@8.5.15) - postcss-normalize-positions: 5.1.1(postcss@8.5.15) - postcss-normalize-repeat-style: 5.1.1(postcss@8.5.15) - postcss-normalize-string: 5.1.0(postcss@8.5.15) - postcss-normalize-timing-functions: 5.1.0(postcss@8.5.15) - postcss-normalize-unicode: 5.1.1(postcss@8.5.15) - postcss-normalize-url: 5.1.0(postcss@8.5.15) - postcss-normalize-whitespace: 5.1.1(postcss@8.5.15) - postcss-ordered-values: 5.1.3(postcss@8.5.15) - postcss-reduce-initial: 5.1.2(postcss@8.5.15) - postcss-reduce-transforms: 5.1.0(postcss@8.5.15) - postcss-svgo: 5.1.0(postcss@8.5.15) - postcss-unique-selectors: 5.1.1(postcss@8.5.15) - - cssnano-utils@3.1.0(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - - cssnano@5.1.15(postcss@8.5.15): - dependencies: - cssnano-preset-default: 5.2.14(postcss@8.5.15) + cssnano-preset-default@5.2.14(postcss@8.5.16): + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.5.16) + cssnano-utils: 3.1.0(postcss@8.5.16) + postcss: 8.5.16 + postcss-calc: 8.2.4(postcss@8.5.16) + postcss-colormin: 5.3.1(postcss@8.5.16) + postcss-convert-values: 5.1.3(postcss@8.5.16) + postcss-discard-comments: 5.1.2(postcss@8.5.16) + postcss-discard-duplicates: 5.1.0(postcss@8.5.16) + postcss-discard-empty: 5.1.1(postcss@8.5.16) + postcss-discard-overridden: 5.1.0(postcss@8.5.16) + postcss-merge-longhand: 5.1.7(postcss@8.5.16) + postcss-merge-rules: 5.1.4(postcss@8.5.16) + postcss-minify-font-values: 5.1.0(postcss@8.5.16) + postcss-minify-gradients: 5.1.1(postcss@8.5.16) + postcss-minify-params: 5.1.4(postcss@8.5.16) + postcss-minify-selectors: 5.2.1(postcss@8.5.16) + postcss-normalize-charset: 5.1.0(postcss@8.5.16) + postcss-normalize-display-values: 5.1.0(postcss@8.5.16) + postcss-normalize-positions: 5.1.1(postcss@8.5.16) + postcss-normalize-repeat-style: 5.1.1(postcss@8.5.16) + postcss-normalize-string: 5.1.0(postcss@8.5.16) + postcss-normalize-timing-functions: 5.1.0(postcss@8.5.16) + postcss-normalize-unicode: 5.1.1(postcss@8.5.16) + postcss-normalize-url: 5.1.0(postcss@8.5.16) + postcss-normalize-whitespace: 5.1.1(postcss@8.5.16) + postcss-ordered-values: 5.1.3(postcss@8.5.16) + postcss-reduce-initial: 5.1.2(postcss@8.5.16) + postcss-reduce-transforms: 5.1.0(postcss@8.5.16) + postcss-svgo: 5.1.0(postcss@8.5.16) + postcss-unique-selectors: 5.1.1(postcss@8.5.16) + + cssnano-utils@3.1.0(postcss@8.5.16): + dependencies: + postcss: 8.5.16 + + cssnano@5.1.15(postcss@8.5.16): + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.5.16) lilconfig: 2.1.0 - postcss: 8.5.15 + postcss: 8.5.16 yaml: 1.10.3 csso@4.2.0: @@ -15784,6 +16575,8 @@ snapshots: detect-newline@4.0.1: {} + detect-node-es@1.1.0: {} + diff-sequences@29.6.3: {} diff@4.0.4: {} @@ -15879,7 +16672,7 @@ snapshots: react-is: 17.0.2 tslib: 2.8.1 - downshift@9.3.6(react@18.3.1): + downshift@9.4.0(react@18.3.1): dependencies: '@babel/runtime': 7.29.7 compute-scroll-into-view: 3.1.1 @@ -15912,7 +16705,7 @@ snapshots: earcut@2.2.4: {} - earcut@3.0.2: {} + earcut@3.2.0: {} eastasianwidth@0.2.0: {} @@ -15921,11 +16714,11 @@ snapshots: '@one-ini/wasm': 0.1.1 commander: 10.0.1 minimatch: 9.0.9 - semver: 7.8.4 + semver: 7.8.5 ee-first@1.1.1: {} - electron-to-chromium@1.5.373: {} + electron-to-chromium@1.5.383: {} elementary-circuits-directed-graph@1.3.1: dependencies: @@ -15993,7 +16786,7 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.1 + es-to-primitive: 1.3.4 function.prototype.name: 1.2.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 @@ -16086,9 +16879,10 @@ snapshots: dependencies: hasown: 2.0.4 - es-to-primitive@1.3.1: + es-to-primitive@1.3.4: dependencies: es-abstract-get: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 @@ -16153,11 +16947,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.6.1)): + eslint-module-utils@2.13.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 transitivePeerDependencies: @@ -16166,9 +16960,9 @@ snapshots: eslint-plugin-cypress@5.4.0(eslint@9.39.4(jiti@2.6.1)): dependencies: eslint: 9.39.4(jiti@2.6.1) - globals: 17.6.0 + globals: 17.7.0 - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -16179,7 +16973,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.6.1)) + eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.6.1)) hasown: 2.0.4 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -16191,20 +16985,20 @@ snapshots: string.prototype.trimend: 1.0.10 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.3.0(@types/node@24.12.4))(typescript@6.0.3): + eslint-plugin-jest@29.15.4(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.3.0(@types/node@24.12.4))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) jest: 30.3.0(@types/node@24.12.4) - typescript: 6.0.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -16218,7 +17012,7 @@ snapshots: eslint-fix-utils: 0.4.2(@types/estree@1.0.9)(eslint@9.39.4(jiti@2.6.1)) jsonc-eslint-parser: 3.1.0 package-json-validator: 1.5.2 - semver: 7.8.4 + semver: 7.8.5 sort-object-keys: 2.1.0 sort-package-json: 3.7.1 validate-npm-package-name: 7.0.2 @@ -16228,12 +17022,12 @@ snapshots: eslint-plugin-playwright@2.10.4(eslint@9.39.4(jiti@2.6.1)): dependencies: eslint: 9.39.4(jiti@2.6.1) - globals: 17.6.0 + globals: 17.7.0 - eslint-plugin-prettier@5.5.6(eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(prettier@3.8.4): + eslint-plugin-prettier@5.5.6(eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(prettier@3.9.4): dependencies: eslint: 9.39.4(jiti@2.6.1) - prettier: 3.8.4 + prettier: 3.9.4 prettier-linter-helpers: 1.0.1 synckit: 0.11.13 optionalDependencies: @@ -16380,8 +17174,6 @@ snapshots: eventemitter3@4.0.7: {} - eventemitter3@5.0.4: {} - events@3.3.0: {} eventsource-parser@3.1.0: {} @@ -16474,8 +17266,8 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.2 - range-parser: 1.2.1 + qs: 6.15.3 + range-parser: 1.3.0 router: 2.2.0 send: 1.2.1 serve-static: 2.2.1 @@ -16498,6 +17290,8 @@ snapshots: fast-diff@1.3.0: {} + fast-equals@5.4.0: {} + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -16514,7 +17308,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.1.2: {} + fast-uri@3.1.3: {} fast-xml-parser@4.5.6: dependencies: @@ -16617,7 +17411,11 @@ snapshots: flow-enums-runtime@0.0.6: {} - flow-parser@0.318.0: {} + flow-estree@0.321.0: {} + + flow-parser@0.321.0: + dependencies: + flow-estree: 0.321.0 font-atlas@2.1.0: dependencies: @@ -16662,7 +17460,7 @@ snapshots: fs-constants@1.0.0: optional: true - fs-extra@11.3.5: + fs-extra@11.3.6: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.1 @@ -16727,6 +17525,8 @@ snapshots: hasown: 2.0.4 math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-package-type@0.1.0: {} get-proto@1.0.1: @@ -16849,7 +17649,7 @@ snapshots: globals@14.0.0: {} - globals@17.6.0: {} + globals@17.7.0: {} globalthis@1.0.4: dependencies: @@ -17022,9 +17822,11 @@ snapshots: dependencies: hermes-estree: 0.36.0 + highlight.js@11.11.1: {} + hoist-non-react-statics@2.5.5: {} - hono@4.12.25: {} + hono@4.12.27: {} hosted-git-info@9.0.3: dependencies: @@ -17102,9 +17904,9 @@ snapshots: icss-replace-symbols@1.1.0: {} - icss-utils@5.1.0(postcss@8.5.15): + icss-utils@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 identity-obj-proxy@3.0.0: dependencies: @@ -17122,7 +17924,7 @@ snapshots: immediate@3.0.6: {} - immutable@5.1.6: {} + immutable@5.1.9: {} import-cwd@3.0.0: dependencies: @@ -17392,7 +18194,7 @@ snapshots: '@babel/parser': 7.29.7 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 - semver: 7.8.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -17518,19 +18320,19 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)): + jest-cli@29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + create-jest: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + jest-config: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) jest-util: 29.7.0 jest-validate: 29.7.0 - yargs: 17.7.2 + yargs: 17.7.3 transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -17548,7 +18350,7 @@ snapshots: jest-config: 30.3.0(@types/node@24.12.4) jest-util: 30.3.0 jest-validate: 30.3.0 - yargs: 17.7.2 + yargs: 17.7.3 transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -17557,18 +18359,18 @@ snapshots: - ts-node optional: true - jest-cli@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): + jest-cli@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: - '@jest/core': 30.3.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + '@jest/core': 30.3.0(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) '@jest/test-result': 30.3.0 '@jest/types': 30.3.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest-config: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) jest-util: 30.3.0 jest-validate: 30.3.0 - yargs: 17.7.2 + yargs: 17.7.3 transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -17576,7 +18378,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)): + jest-config@29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: '@babel/core': 7.29.7 '@jest/test-sequencer': 29.7.0 @@ -17602,7 +18404,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 24.12.4 - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3) + ts-node: 10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -17639,7 +18441,7 @@ snapshots: - supports-color optional: true - jest-config@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): + jest-config@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: '@babel/core': 7.29.7 '@jest/get-type': 30.1.0 @@ -17666,7 +18468,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 24.12.4 - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -17821,7 +18623,7 @@ snapshots: dependencies: mkdirp: 1.0.4 strip-ansi: 6.0.1 - uuid: 14.0.0 + uuid: 14.0.1 xml: 1.0.1 jest-leak-detector@29.7.0: @@ -18089,7 +18891,7 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.8.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -18114,7 +18916,7 @@ snapshots: jest-message-util: 30.3.0 jest-util: 30.3.0 pretty-format: 30.3.0 - semver: 7.8.4 + semver: 7.8.5 synckit: 0.11.13 transitivePeerDependencies: - supports-color @@ -18196,17 +18998,17 @@ snapshots: jest-worker@30.3.0: dependencies: '@types/node': 24.12.4 - '@ungap/structured-clone': 1.3.1 + '@ungap/structured-clone': 1.3.2 jest-util: 30.3.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)): + jest@29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + jest-cli: 29.7.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -18227,12 +19029,12 @@ snapshots: - ts-node optional: true - jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): + jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: - '@jest/core': 30.3.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + '@jest/core': 30.3.0(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) '@jest/types': 30.3.0 import-local: 3.2.0 - jest-cli: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest-cli: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -18256,12 +19058,12 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@3.14.2: + js-yaml@3.15.0: dependencies: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.2.0: + js-yaml@4.3.0: dependencies: argparse: 2.0.1 @@ -18281,12 +19083,12 @@ snapshots: '@babel/preset-flow': 7.29.7(@babel/core@7.29.7) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) '@babel/register': 7.29.7(@babel/core@7.29.7) - flow-parser: 0.318.0 + flow-parser: 0.321.0 graceful-fs: 4.2.11 micromatch: 4.0.8 neo-async: 2.6.2 picocolors: 1.1.1 - recast: 0.23.11 + recast: 0.23.12 tmp: 0.2.7 write-file-atomic: 5.0.1 optionalDependencies: @@ -18382,7 +19184,7 @@ snapshots: dependencies: acorn: 8.17.0 eslint-visitor-keys: 5.0.1 - semver: 7.8.4 + semver: 7.8.5 jsonc-parser@3.3.1: {} @@ -18501,12 +19303,8 @@ snapshots: lodash.camelcase@4.3.0: {} - lodash.clonedeep@4.5.0: {} - lodash.debounce@4.0.8: {} - lodash.isequal@4.5.0: {} - lodash.isplainobject@4.0.6: {} lodash.kebabcase@4.1.1: {} @@ -18573,7 +19371,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.8.4 + semver: 7.8.5 make-dir@5.1.0: {} @@ -18593,7 +19391,7 @@ snapshots: dependencies: '@mapbox/geojson-rewind': 0.5.2 '@mapbox/geojson-types': 1.0.2 - '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/jsonlint-lines-primitives': 2.0.3 '@mapbox/mapbox-gl-supported': 1.5.0(mapbox-gl@1.13.3) '@mapbox/point-geometry': 0.1.0 '@mapbox/tiny-sdf': 1.2.5 @@ -18617,7 +19415,7 @@ snapshots: maplibre-gl@4.7.1: dependencies: '@mapbox/geojson-rewind': 0.5.2 - '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/jsonlint-lines-primitives': 2.0.3 '@mapbox/point-geometry': 0.1.0 '@mapbox/tiny-sdf': 2.2.0 '@mapbox/unitbezier': 0.0.1 @@ -18630,7 +19428,7 @@ snapshots: '@types/mapbox__vector-tile': 1.3.4 '@types/pbf': 3.0.5 '@types/supercluster': 7.1.3 - earcut: 3.0.2 + earcut: 3.2.0 geojson-vt: 4.0.3 gl-matrix: 3.4.4 global-prefix: 4.0.0 @@ -18687,7 +19485,7 @@ snapshots: '@types/big.js': 6.2.2 '@types/react': 19.2.17 - mendix@11.10.0: + mendix@11.12.0: dependencies: '@types/big.js': 6.2.2 '@types/react': 19.2.17 @@ -18872,7 +19670,7 @@ snapshots: source-map: 0.5.7 throat: 5.0.0 ws: 7.5.11 - yargs: 17.7.2 + yargs: 17.7.3 transitivePeerDependencies: - bufferutil - supports-color @@ -18914,7 +19712,7 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.6 + brace-expansion: 5.0.7 minimatch@3.1.5: dependencies: @@ -18992,7 +19790,7 @@ snapshots: nanoevents@9.1.0: {} - nanoid@3.3.12: {} + nanoid@3.3.15: {} napi-build-utils@2.0.0: optional: true @@ -19019,9 +19817,9 @@ snapshots: nice-try@1.0.5: {} - node-abi@3.92.0: + node-abi@3.93.0: dependencies: - semver: 7.8.4 + semver: 7.8.5 optional: true node-addon-api@7.1.1: @@ -19029,7 +19827,7 @@ snapshots: node-domexception@1.0.0: {} - node-exports-info@1.6.0: + node-exports-info@1.6.2: dependencies: array.prototype.flatmap: 1.3.3 es-errors: 1.3.0 @@ -19048,7 +19846,7 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.47: {} + node-releases@2.0.50: {} nopt@7.2.1: dependencies: @@ -19068,7 +19866,7 @@ snapshots: dependencies: hosted-git-info: 9.0.3 proc-log: 6.1.0 - semver: 7.8.4 + semver: 7.8.5 validate-npm-package-name: 7.0.2 npm-run-path@2.0.2: @@ -19190,6 +19988,8 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + orderedmap@2.1.1: {} + own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 @@ -19242,7 +20042,7 @@ snapshots: package-json-validator@1.5.2: dependencies: npm-package-arg: 13.0.2 - semver: 7.8.4 + semver: 7.8.5 validate-npm-package-license: 3.0.4 validate-npm-package-name: 7.0.2 @@ -19250,8 +20050,6 @@ snapshots: pako@1.0.11: {} - parchment@3.0.0: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -19350,13 +20148,13 @@ snapshots: dependencies: find-up: 4.1.0 - playwright-core@1.61.0: {} + playwright-core@1.61.1: {} playwright-ctrf-json-reporter@0.0.27: {} - playwright@1.61.0: + playwright@1.61.1: dependencies: - playwright-core: 1.61.0 + playwright-core: 1.61.1 optionalDependencies: fsevents: 2.3.2 @@ -19417,204 +20215,196 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-calc@8.2.4(postcss@8.5.15): + postcss-calc@8.2.4(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-selector-parser: 6.1.4 postcss-value-parser: 4.2.0 - postcss-colormin@5.3.1(postcss@8.5.15): + postcss-colormin@5.3.1(postcss@8.5.16): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.4 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-convert-values@5.1.3(postcss@8.5.15): + postcss-convert-values@5.1.3(postcss@8.5.16): dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 + browserslist: 4.28.4 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-discard-comments@5.1.2(postcss@8.5.15): + postcss-discard-comments@5.1.2(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 - postcss-discard-duplicates@5.1.0(postcss@8.5.15): + postcss-discard-duplicates@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 - postcss-discard-empty@5.1.1(postcss@8.5.15): + postcss-discard-empty@5.1.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 - postcss-discard-overridden@5.1.0(postcss@8.5.15): + postcss-discard-overridden@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 - postcss-import@14.1.0(postcss@8.5.15): + postcss-import@14.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.12 - postcss-import@16.1.1(postcss@8.5.15): + postcss-import@16.1.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.12 - postcss-load-config@3.1.4(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): - dependencies: - lilconfig: 2.1.0 - yaml: 1.10.3 - optionalDependencies: - postcss: 8.5.15 - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3) - - postcss-load-config@3.1.4(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)): + postcss-load-config@3.1.4(postcss@8.5.16)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.3 optionalDependencies: - postcss: 8.5.15 - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3) + postcss: 8.5.16 + ts-node: 10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3) - postcss-merge-longhand@5.1.7(postcss@8.5.15): + postcss-merge-longhand@5.1.7(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.5.15) + stylehacks: 5.1.1(postcss@8.5.16) - postcss-merge-rules@5.1.4(postcss@8.5.15): + postcss-merge-rules@5.1.4(postcss@8.5.16): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.4 caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 3.1.0(postcss@8.5.16) + postcss: 8.5.16 postcss-selector-parser: 6.1.4 - postcss-minify-font-values@5.1.0(postcss@8.5.15): + postcss-minify-font-values@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-minify-gradients@5.1.1(postcss@8.5.15): + postcss-minify-gradients@5.1.1(postcss@8.5.16): dependencies: colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 3.1.0(postcss@8.5.16) + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-minify-params@5.1.4(postcss@8.5.15): + postcss-minify-params@5.1.4(postcss@8.5.16): dependencies: - browserslist: 4.28.2 - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 + browserslist: 4.28.4 + cssnano-utils: 3.1.0(postcss@8.5.16) + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-minify-selectors@5.2.1(postcss@8.5.15): + postcss-minify-selectors@5.2.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-selector-parser: 6.1.4 - postcss-modules-extract-imports@3.1.0(postcss@8.5.15): + postcss-modules-extract-imports@3.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 - postcss-modules-local-by-default@4.2.0(postcss@8.5.15): + postcss-modules-local-by-default@4.2.0(postcss@8.5.16): dependencies: - icss-utils: 5.1.0(postcss@8.5.15) - postcss: 8.5.15 + icss-utils: 5.1.0(postcss@8.5.16) + postcss: 8.5.16 postcss-selector-parser: 7.1.4 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.1(postcss@8.5.15): + postcss-modules-scope@3.2.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-selector-parser: 7.1.4 - postcss-modules-values@4.0.0(postcss@8.5.15): + postcss-modules-values@4.0.0(postcss@8.5.16): dependencies: - icss-utils: 5.1.0(postcss@8.5.15) - postcss: 8.5.15 + icss-utils: 5.1.0(postcss@8.5.16) + postcss: 8.5.16 - postcss-modules@4.3.1(postcss@8.5.15): + postcss-modules@4.3.1(postcss@8.5.16): dependencies: generic-names: 4.0.0 icss-replace-symbols: 1.1.0 lodash.camelcase: 4.3.0 - postcss: 8.5.15 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.15) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.15) - postcss-modules-scope: 3.2.1(postcss@8.5.15) - postcss-modules-values: 4.0.0(postcss@8.5.15) + postcss: 8.5.16 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.16) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.16) + postcss-modules-scope: 3.2.1(postcss@8.5.16) + postcss-modules-values: 4.0.0(postcss@8.5.16) string-hash: 1.1.3 - postcss-normalize-charset@5.1.0(postcss@8.5.15): + postcss-normalize-charset@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 - postcss-normalize-display-values@5.1.0(postcss@8.5.15): + postcss-normalize-display-values@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-normalize-positions@5.1.1(postcss@8.5.15): + postcss-normalize-positions@5.1.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@5.1.1(postcss@8.5.15): + postcss-normalize-repeat-style@5.1.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-normalize-string@5.1.0(postcss@8.5.15): + postcss-normalize-string@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@5.1.0(postcss@8.5.15): + postcss-normalize-timing-functions@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@5.1.1(postcss@8.5.15): + postcss-normalize-unicode@5.1.1(postcss@8.5.16): dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 + browserslist: 4.28.4 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-normalize-url@5.1.0(postcss@8.5.15): + postcss-normalize-url@5.1.0(postcss@8.5.16): dependencies: normalize-url: 6.1.0 - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@5.1.1(postcss@8.5.15): + postcss-normalize-whitespace@5.1.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-ordered-values@5.1.3(postcss@8.5.15): + postcss-ordered-values@5.1.3(postcss@8.5.16): dependencies: - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 3.1.0(postcss@8.5.16) + postcss: 8.5.16 postcss-value-parser: 4.2.0 - postcss-reduce-initial@5.1.2(postcss@8.5.15): + postcss-reduce-initial@5.1.2(postcss@8.5.16): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.4 caniuse-api: 3.0.0 - postcss: 8.5.15 + postcss: 8.5.16 - postcss-reduce-transforms@5.1.0(postcss@8.5.15): + postcss-reduce-transforms@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 postcss-selector-parser@6.1.4: @@ -19627,30 +20417,30 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@5.1.0(postcss@8.5.15): + postcss-svgo@5.1.0(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-value-parser: 4.2.0 svgo: 2.8.2 - postcss-unique-selectors@5.1.1(postcss@8.5.15): + postcss-unique-selectors@5.1.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 postcss-selector-parser: 6.1.4 - postcss-url@10.1.4(postcss@8.5.15): + postcss-url@10.1.4(postcss@8.5.16): dependencies: make-dir: 3.1.0 mime: 2.5.2 minimatch: 3.1.5 - postcss: 8.5.15 + postcss: 8.5.16 xxhashjs: 0.2.2 postcss-value-parser@4.2.0: {} - postcss@8.5.15: + postcss@8.5.16: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.15 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -19666,11 +20456,11 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.92.0 + node-abi: 3.93.0 pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 - tar-fs: 2.1.4 + tar-fs: 2.1.5 tunnel-agent: 0.6.0 optional: true @@ -19680,13 +20470,13 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-packagejson@2.5.22(prettier@3.8.4): + prettier-plugin-packagejson@2.5.22(prettier@3.9.4): dependencies: sort-package-json: 3.6.0 optionalDependencies: - prettier: 3.8.4 + prettier: 3.9.4 - prettier@3.8.4: {} + prettier@3.9.4: {} pretty-format@27.5.1: dependencies: @@ -19713,14 +20503,14 @@ snapshots: react-is-18: react-is@18.3.1 react-is-19: react-is@19.2.7 - pretty-quick@4.2.2(prettier@3.8.4): + pretty-quick@4.2.2(prettier@3.9.4): dependencies: '@pkgr/core': 0.2.10 ignore: 7.0.5 mri: 1.2.0 picocolors: 1.1.1 picomatch: 4.0.4 - prettier: 3.8.4 + prettier: 3.9.4 tinyexec: 0.3.2 tslib: 2.8.1 @@ -19763,6 +20553,80 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + prosemirror-changeset@2.4.1: + dependencies: + prosemirror-transform: 1.12.0 + + prosemirror-commands@1.7.1: + dependencies: + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + prosemirror-dropcursor@1.8.2: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + + prosemirror-gapcursor@1.4.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-view: 1.41.9 + + prosemirror-history@1.5.0: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.4 + w3c-keyname: 2.2.8 + + prosemirror-model@1.25.9: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + + prosemirror-state@1.4.4: + dependencies: + prosemirror-model: 1.25.9 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + + prosemirror-tables@1.8.5: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + prosemirror-view: 1.41.9 + + prosemirror-transform@1.12.0: + dependencies: + prosemirror-model: 1.25.9 + + prosemirror-view@1.41.9: + dependencies: + prosemirror-model: 1.25.9 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.0 + proto-list@1.2.4: {} protocol-buffers-schema@3.6.1: {} @@ -19799,8 +20663,9 @@ snapshots: dependencies: react: 18.3.1 - qs@6.15.2: + qs@6.15.3: dependencies: + es-define-property: 1.0.1 side-channel: 1.1.1 querystringify@2.2.0: {} @@ -19815,21 +20680,6 @@ snapshots: quickselect@3.0.0: {} - quill-delta@5.1.0: - dependencies: - fast-diff: 1.3.0 - lodash.clonedeep: 4.5.0 - lodash.isequal: 4.5.0 - - quill-resize-module@2.1.3: {} - - quill@2.0.3: - dependencies: - eventemitter3: 5.0.4 - lodash-es: 4.18.1 - parchment: 3.0.0 - quill-delta: 5.1.0 - raf@3.4.1: dependencies: performance-now: 2.1.0 @@ -19840,6 +20690,8 @@ snapshots: range-parser@1.2.1: {} + range-parser@1.3.0: {} + raw-body@3.0.2: dependencies: bytes: 3.1.2 @@ -19922,7 +20774,7 @@ snapshots: react-devtools-core@6.1.5: dependencies: - shell-quote: 1.8.4 + shell-quote: 1.9.0 ws: 7.5.11 transitivePeerDependencies: - bufferutil @@ -19958,7 +20810,7 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 - react-image-crop@11.0.10(react@18.3.1): + react-image-crop@11.1.2(react@18.3.1): dependencies: react: 18.3.1 @@ -20008,12 +20860,12 @@ snapshots: react-refresh: 0.14.2 regenerator-runtime: 0.13.11 scheduler: 0.27.0 - semver: 7.8.4 + semver: 7.8.5 stacktrace-parser: 0.1.11 tinyglobby: 0.2.17 whatwg-fetch: 3.6.20 ws: 7.5.11 - yargs: 17.7.2 + yargs: 17.7.3 optionalDependencies: '@types/react': 19.2.17 transitivePeerDependencies: @@ -20080,6 +20932,43 @@ snapshots: react-refresh@0.14.2: {} + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@18.3.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.17 + + react-scroll-sync@1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-simple-code-editor@0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-style-singleton@2.2.3(@types/react@19.2.17)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + react-test-renderer@19.2.7(react@18.3.1): dependencies: react: 18.3.1 @@ -20128,7 +21017,7 @@ snapshots: readdirp@5.0.0: {} - recast@0.23.11: + recast@0.23.12: dependencies: ast-types: 0.16.1 esprima: 4.0.1 @@ -20298,7 +21187,7 @@ snapshots: dependencies: es-errors: 1.3.0 is-core-module: 2.16.2 - node-exports-info: 1.6.0 + node-exports-info: 1.6.2 object-keys: 1.1.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -20332,7 +21221,7 @@ snapshots: globby: 10.0.1 is-plain-object: 3.0.1 - rollup-plugin-license@3.7.1(picomatch@4.0.4)(rollup@4.62.0): + rollup-plugin-license@3.7.1(picomatch@4.0.4)(rollup@4.62.2): dependencies: commenting: 1.1.0 fdir: 6.5.0(picomatch@4.0.4) @@ -20340,13 +21229,13 @@ snapshots: magic-string: 0.30.21 moment: 2.30.1 package-name-regex: 2.0.6 - rollup: 4.62.0 + rollup: 4.62.2 spdx-expression-validate: 2.0.0 spdx-satisfies: 5.0.1 transitivePeerDependencies: - picomatch - rollup-plugin-license@3.7.1(rollup@4.62.0): + rollup-plugin-license@3.7.1(rollup@4.62.2): dependencies: commenting: 1.1.0 fdir: 6.5.0 @@ -20354,7 +21243,7 @@ snapshots: magic-string: 0.30.21 moment: 2.30.1 package-name-regex: 2.0.6 - rollup: 4.62.0 + rollup: 4.62.2 spdx-expression-validate: 2.0.0 spdx-satisfies: 5.0.1 transitivePeerDependencies: @@ -20367,36 +21256,17 @@ snapshots: - bufferutil - utf-8-validate - rollup-plugin-postcss@4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): - dependencies: - chalk: 4.1.2 - concat-with-sourcemaps: 1.1.0 - cssnano: 5.1.15(postcss@8.5.15) - import-cwd: 3.0.0 - p-queue: 6.6.2 - pify: 5.0.0 - postcss: 8.5.15 - postcss-load-config: 3.1.4(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) - postcss-modules: 4.3.1(postcss@8.5.15) - promise.series: 0.2.0 - resolve: 1.22.12 - rollup-pluginutils: 2.8.2 - safe-identifier: 0.4.2 - style-inject: 0.3.0 - transitivePeerDependencies: - - ts-node - - rollup-plugin-postcss@4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)): + rollup-plugin-postcss@4.0.2(postcss@8.5.16)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: chalk: 4.1.2 concat-with-sourcemaps: 1.1.0 - cssnano: 5.1.15(postcss@8.5.15) + cssnano: 5.1.15(postcss@8.5.16) import-cwd: 3.0.0 p-queue: 6.6.2 pify: 5.0.0 - postcss: 8.5.15 - postcss-load-config: 3.1.4(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) - postcss-modules: 4.3.1(postcss@8.5.15) + postcss: 8.5.16 + postcss-load-config: 3.1.4(postcss@8.5.16)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) + postcss-modules: 4.3.1(postcss@8.5.16) promise.series: 0.2.0 resolve: 1.22.12 rollup-pluginutils: 2.8.2 @@ -20414,42 +21284,44 @@ snapshots: dependencies: estree-walker: 0.6.1 - rollup-preserve-directives@1.1.3(rollup@4.62.0): + rollup-preserve-directives@1.1.3(rollup@4.62.2): dependencies: magic-string: 0.30.21 - rollup: 4.62.0 + rollup: 4.62.2 - rollup@4.62.0: + rollup@4.62.2: dependencies: '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.0 - '@rollup/rollup-android-arm64': 4.62.0 - '@rollup/rollup-darwin-arm64': 4.62.0 - '@rollup/rollup-darwin-x64': 4.62.0 - '@rollup/rollup-freebsd-arm64': 4.62.0 - '@rollup/rollup-freebsd-x64': 4.62.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.0 - '@rollup/rollup-linux-arm-musleabihf': 4.62.0 - '@rollup/rollup-linux-arm64-gnu': 4.62.0 - '@rollup/rollup-linux-arm64-musl': 4.62.0 - '@rollup/rollup-linux-loong64-gnu': 4.62.0 - '@rollup/rollup-linux-loong64-musl': 4.62.0 - '@rollup/rollup-linux-ppc64-gnu': 4.62.0 - '@rollup/rollup-linux-ppc64-musl': 4.62.0 - '@rollup/rollup-linux-riscv64-gnu': 4.62.0 - '@rollup/rollup-linux-riscv64-musl': 4.62.0 - '@rollup/rollup-linux-s390x-gnu': 4.62.0 - '@rollup/rollup-linux-x64-gnu': 4.62.0 - '@rollup/rollup-linux-x64-musl': 4.62.0 - '@rollup/rollup-openbsd-x64': 4.62.0 - '@rollup/rollup-openharmony-arm64': 4.62.0 - '@rollup/rollup-win32-arm64-msvc': 4.62.0 - '@rollup/rollup-win32-ia32-msvc': 4.62.0 - '@rollup/rollup-win32-x64-gnu': 4.62.0 - '@rollup/rollup-win32-x64-msvc': 4.62.0 + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 fsevents: 2.3.3 + rope-sequence@1.3.4: {} + router@2.2.0: dependencies: debug: 4.4.3 @@ -20502,7 +21374,7 @@ snapshots: sass@1.101.0: dependencies: chokidar: 5.0.0 - immutable: 5.1.6 + immutable: 5.1.9 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.6 @@ -20523,7 +21395,7 @@ snapshots: semver@6.3.1: {} - semver@7.8.4: {} + semver@7.8.5: {} send@0.19.2: dependencies: @@ -20554,7 +21426,7 @@ snapshots: mime-types: 3.0.2(patch_hash=f54449b9273bc9e74fb67a14fcd001639d788d038b7eb0b5f43c10dff2b1adfb) ms: 2.1.3 on-finished: 2.4.1 - range-parser: 1.2.1 + range-parser: 1.3.0 statuses: 2.0.2 transitivePeerDependencies: - supports-color @@ -20565,7 +21437,7 @@ snapshots: dependencies: randombytes: 2.1.0 - serialize-javascript@7.0.5: {} + serialize-javascript@7.0.7: {} serve-static@1.16.3: dependencies: @@ -20631,7 +21503,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.4: {} + shell-quote@1.9.0: {} shelljs@0.10.0: dependencies: @@ -20720,7 +21592,7 @@ snapshots: detect-newline: 4.0.1 git-hooks-list: 4.2.1 is-plain-obj: 4.1.0 - semver: 7.8.4 + semver: 7.8.5 sort-object-keys: 2.1.0 tinyglobby: 0.2.17 @@ -20730,7 +21602,7 @@ snapshots: detect-newline: 4.0.1 git-hooks-list: 4.2.1 is-plain-obj: 4.1.0 - semver: 7.8.4 + semver: 7.8.5 sort-object-keys: 2.1.0 tinyglobby: 0.2.17 @@ -20932,12 +21804,10 @@ snapshots: style-inject@0.3.0: {} - style-mod@4.1.3: {} - - stylehacks@5.1.1(postcss@8.5.15): + stylehacks@5.1.1(postcss@8.5.16): dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 + browserslist: 4.28.4 + postcss: 8.5.16 postcss-selector-parser: 6.1.4 supercluster@7.1.5: @@ -20995,9 +21865,9 @@ snapshots: dependencies: '@pkgr/core': 0.3.6 - tabbable@6.4.0: {} + tabbable@6.5.0: {} - tar-fs@2.1.4: + tar-fs@2.1.5: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 @@ -21113,22 +21983,18 @@ snapshots: dependencies: typescript: 5.9.3 - ts-api-utils@2.5.0(typescript@6.0.3): - dependencies: - typescript: 6.0.3 - ts-custom-error@3.3.1: {} - ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@29.7.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@29.7.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.9 - jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.8.4 + semver: 7.8.5 type-fest: 4.41.0 typescript: 5.9.3 yargs-parser: 21.1.1 @@ -21139,7 +22005,7 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.29.7) jest-util: 30.4.1 - ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3): + ts-node@10.9.2(@swc/core@1.15.43)(@types/node@24.12.4)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 @@ -21157,27 +22023,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.15.41 - - ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 24.12.4 - acorn: 8.17.0 - acorn-walk: 8.3.5 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.4 - make-error: 1.3.6 - typescript: 6.0.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.15.41 + '@swc/core': 1.15.43 tsconfig-paths@3.15.0: dependencies: @@ -21195,14 +22041,14 @@ snapshots: safe-buffer: 5.2.1 optional: true - turbo@2.9.18: + turbo@2.10.2: optionalDependencies: - '@turbo/darwin-64': 2.9.18 - '@turbo/darwin-arm64': 2.9.18 - '@turbo/linux-64': 2.9.18 - '@turbo/linux-arm64': 2.9.18 - '@turbo/windows-64': 2.9.18 - '@turbo/windows-arm64': 2.9.18 + '@turbo/darwin-64': 2.10.2 + '@turbo/darwin-arm64': 2.10.2 + '@turbo/linux-64': 2.10.2 + '@turbo/linux-arm64': 2.10.2 + '@turbo/windows-64': 2.10.2 + '@turbo/windows-arm64': 2.10.2 tweetnacl@1.0.3: {} @@ -21266,21 +22112,19 @@ snapshots: typedarray@0.0.6: {} - typescript-eslint@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3): + typescript-eslint@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color typescript@5.9.3: {} - typescript@6.0.3: {} - uc.micro@2.1.0: {} uglify-js@3.19.3: @@ -21358,9 +22202,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 - update-browserslist-db@1.2.3(browserslist@4.28.2): + update-browserslist-db@1.2.3(browserslist@4.28.4): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.4 escalade: 3.2.0 picocolors: 1.1.1 @@ -21375,6 +22219,21 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + use-callback-ref@1.3.3(@types/react@19.2.17)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + use-sidecar@1.1.3(@types/react@19.2.17)(react@18.3.1): + dependencies: + detect-node-es: 1.1.0 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + use-sync-external-store@1.6.0(react@18.3.1): dependencies: react: 18.3.1 @@ -21383,7 +22242,7 @@ snapshots: utils-merge@1.0.1: {} - uuid@14.0.0: {} + uuid@14.0.1: {} v8-compile-cache-lib@3.0.1: {} @@ -21611,7 +22470,7 @@ snapshots: yargs-parser@22.0.0: {} - yargs@16.2.0: + yargs@16.2.2: dependencies: cliui: 7.0.4 escalade: 3.2.0 @@ -21621,7 +22480,7 @@ snapshots: y18n: 5.0.8 yargs-parser: 20.2.9 - yargs@17.7.2: + yargs@17.7.3: dependencies: cliui: 8.0.1 escalade: 3.2.0 @@ -21637,7 +22496,7 @@ snapshots: yocto-queue@1.2.2: {} - zip-a-folder@6.1.1: + zip-a-folder@6.1.2: dependencies: lzma: 2.3.2 tinyglobby: 0.2.17