@pie-lib/editable-html-tip-tap 1.1.1-next.2 → 1.1.1-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/lib/__tests__/EditableHtml.test.js +1 -11
  2. package/lib/__tests__/constants.test.js +1 -8
  3. package/lib/__tests__/extensions.test.js +1 -6
  4. package/lib/__tests__/index.test.js +1 -12
  5. package/lib/components/CharacterPicker.js +5 -24
  6. package/lib/components/CharacterPicker.js.map +1 -1
  7. package/lib/components/EditableHtml.js +1 -18
  8. package/lib/components/EditableHtml.js.map +1 -1
  9. package/lib/components/MenuBar.js +1 -18
  10. package/lib/components/TiptapContainer.js +26 -23
  11. package/lib/components/TiptapContainer.js.map +1 -1
  12. package/lib/components/__tests__/AltDialog.test.js +1 -1
  13. package/lib/components/__tests__/CharacterPicker.test.js +4 -12
  14. package/lib/components/__tests__/DragInTheBlank.test.js +1 -15
  15. package/lib/components/__tests__/ExplicitConstructedResponse.test.js +1 -11
  16. package/lib/components/__tests__/ImageToolbar.test.js +1 -11
  17. package/lib/components/__tests__/InlineDropdown.test.js +1 -11
  18. package/lib/components/__tests__/MediaDialog.test.js +1 -3
  19. package/lib/components/__tests__/MediaToolbar.test.js +1 -1
  20. package/lib/components/__tests__/MenuBar.test.js +1 -1
  21. package/lib/components/__tests__/TableIcons.test.js +1 -1
  22. package/lib/components/__tests__/TextAlign.test.js +1 -8
  23. package/lib/components/__tests__/characterUtils.test.js +1 -12
  24. package/lib/components/__tests__/choice.test.js +1 -1
  25. package/lib/components/__tests__/done-button.test.js +1 -1
  26. package/lib/components/__tests__/toolbar-buttons.test.js +1 -1
  27. package/lib/components/characters/characterUtils.js +1 -3
  28. package/lib/components/characters/custom-popper.js +1 -2
  29. package/lib/components/common/done-button.js +1 -2
  30. package/lib/components/common/toolbar-buttons.js +1 -13
  31. package/lib/components/icons/CssIcon.js +1 -2
  32. package/lib/components/icons/RespArea.js +1 -11
  33. package/lib/components/icons/TableIcons.js +1 -2
  34. package/lib/components/icons/TextAlign.js +1 -8
  35. package/lib/components/image/AltDialog.js +1 -6
  36. package/lib/components/image/ImageToolbar.js +1 -14
  37. package/lib/components/image/InsertImageHandler.js +1 -11
  38. package/lib/components/media/MediaDialog.js +1 -20
  39. package/lib/components/media/MediaToolbar.js +1 -3
  40. package/lib/components/media/MediaWrapper.js +1 -12
  41. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +3 -13
  42. package/lib/components/respArea/DragInTheBlank/choice.js +1 -9
  43. package/lib/components/respArea/ExplicitConstructedResponse.js +1 -9
  44. package/lib/components/respArea/InlineDropdown.js +1 -9
  45. package/lib/components/respArea/ToolbarIcon.js +1 -11
  46. package/lib/constants.js +1 -2
  47. package/lib/extensions/__tests__/css.test.js +1 -5
  48. package/lib/extensions/__tests__/custom-toolbar-wrapper.test.js +1 -11
  49. package/lib/extensions/__tests__/extended-table.test.js +1 -23
  50. package/lib/extensions/__tests__/image-component.test.js +1 -11
  51. package/lib/extensions/__tests__/image.test.js +1 -15
  52. package/lib/extensions/__tests__/media.test.js +1 -5
  53. package/lib/extensions/__tests__/responseArea.test.js +1 -3
  54. package/lib/extensions/css.js +1 -12
  55. package/lib/extensions/custom-toolbar-wrapper.js +2 -20
  56. package/lib/extensions/extended-table.js +2 -6
  57. package/lib/extensions/extended-table.js.map +1 -1
  58. package/lib/extensions/image-component.js +5 -22
  59. package/lib/extensions/image.js +1 -12
  60. package/lib/extensions/image.js.map +1 -1
  61. package/lib/extensions/index.js +4 -13
  62. package/lib/extensions/math.js +1 -17
  63. package/lib/extensions/media.js +1 -16
  64. package/lib/extensions/responseArea.js +1 -24
  65. package/lib/index.js +1 -8
  66. package/lib/styles/editorContainerStyles.js +1 -3
  67. package/lib/theme.js +1 -2
  68. package/lib/utils/size.js +1 -7
  69. package/package.json +9 -9
  70. package/src/__tests__/constants.test.js +1 -2
  71. package/src/__tests__/extensions.test.js +1 -1
  72. package/src/__tests__/index.test.jsx +1 -1
  73. package/src/components/CharacterPicker.jsx +2 -6
  74. package/src/components/EditableHtml.jsx +1 -1
  75. package/src/components/MenuBar.jsx +32 -186
  76. package/src/components/TiptapContainer.jsx +25 -5
  77. package/src/components/__tests__/AltDialog.test.jsx +1 -1
  78. package/src/components/__tests__/CharacterPicker.test.jsx +5 -3
  79. package/src/components/__tests__/DragInTheBlank.test.jsx +1 -1
  80. package/src/components/__tests__/ExplicitConstructedResponse.test.jsx +1 -1
  81. package/src/components/__tests__/ImageToolbar.test.jsx +1 -1
  82. package/src/components/__tests__/InlineDropdown.test.jsx +1 -1
  83. package/src/components/__tests__/MediaDialog.test.jsx +1 -1
  84. package/src/components/__tests__/MediaToolbar.test.jsx +1 -1
  85. package/src/components/__tests__/MenuBar.test.jsx +1 -1
  86. package/src/components/__tests__/TableIcons.test.jsx +1 -1
  87. package/src/components/__tests__/TextAlign.test.jsx +2 -2
  88. package/src/components/__tests__/choice.test.jsx +1 -1
  89. package/src/components/__tests__/done-button.test.jsx +2 -2
  90. package/src/components/__tests__/toolbar-buttons.test.jsx +2 -2
  91. package/src/components/image/AltDialog.jsx +1 -1
  92. package/src/components/image/InsertImageHandler.js +1 -1
  93. package/src/components/media/MediaDialog.js +4 -15
  94. package/src/components/respArea/DragInTheBlank/DragInTheBlank.jsx +1 -1
  95. package/src/components/respArea/DragInTheBlank/choice.jsx +34 -27
  96. package/src/components/respArea/ExplicitConstructedResponse.jsx +1 -1
  97. package/src/components/respArea/InlineDropdown.jsx +2 -3
  98. package/src/extensions/__tests__/custom-toolbar-wrapper.test.jsx +1 -1
  99. package/src/extensions/__tests__/extended-table.test.js +0 -21
  100. package/src/extensions/__tests__/image-component.test.jsx +1 -1
  101. package/src/extensions/__tests__/media.test.js +1 -1
  102. package/src/extensions/__tests__/responseArea.test.js +2 -2
  103. package/src/extensions/css.js +25 -23
  104. package/src/extensions/custom-toolbar-wrapper.jsx +1 -2
  105. package/src/extensions/extended-table.js +3 -1
  106. package/src/extensions/image-component.jsx +2 -2
  107. package/src/extensions/image.js +1 -1
  108. package/src/extensions/index.js +1 -3
  109. package/src/extensions/math.js +29 -27
  110. package/src/extensions/media.js +12 -14
  111. package/src/extensions/responseArea.js +91 -86
  112. package/src/styles/editorContainerStyles.js +0 -1
@@ -1,4 +1,4 @@
1
- import React, { useRef, useEffect, useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useDraggable, useDroppable } from '@dnd-kit/core';
4
4
  import { color } from '@pie-lib/render-ui';
@@ -107,38 +107,45 @@ BlankContent.propTypes = {
107
107
  };
108
108
 
109
109
  function DragDropChoice({ value, disabled, instanceId, children, n, onChange, removeResponse, duplicates, pos }) {
110
- const { attributes: dragAttributes, listeners: dragListeners, setNodeRef: setDragNodeRef, isDragging } = useDraggable(
111
- {
110
+ const {
111
+ attributes: dragAttributes,
112
+ listeners: dragListeners,
113
+ setNodeRef: setDragNodeRef,
114
+ isDragging,
115
+ } = useDraggable({
116
+ id: `drag-${n.index}`,
117
+ disabled: disabled || !value?.value,
118
+ data: {
112
119
  id: `drag-${n.index}`,
113
- disabled: disabled || !value?.value,
114
- data: {
115
- id: `drag-${n.index}`,
116
- value,
117
- instanceId,
118
- n,
119
- pos,
120
- opts: { duplicates },
121
- type: 'drag-in-the-blank-placed-choice',
122
- fromChoice: !value,
123
- onRemove: (draggedData) => removeResponse(draggedData),
124
- onDrop: (draggedData, dropData) => {
125
- // check if we're dropping into a blank
126
- const isValidBlank = dropData?.type === 'drag-in-the-blank-drop-choice';
127
-
128
- if (!isValidBlank) return;
120
+ value,
121
+ instanceId,
122
+ n,
123
+ pos,
124
+ opts: { duplicates },
125
+ type: 'drag-in-the-blank-placed-choice',
126
+ fromChoice: !value,
127
+ onRemove: (draggedData) => removeResponse(draggedData),
128
+ onDrop: (draggedData, dropData) => {
129
+ // check if we're dropping into a blank
130
+ const isValidBlank = dropData?.type === 'drag-in-the-blank-drop-choice';
129
131
 
130
- // place into blank
131
- onChange(draggedData);
132
+ if (!isValidBlank) return;
132
133
 
133
- if (!duplicates && draggedData.fromChoice) {
134
- removeResponse(draggedData);
135
- }
136
- },
134
+ // place into blank
135
+ onChange(draggedData);
136
+
137
+ if (!duplicates && draggedData.fromChoice) {
138
+ removeResponse(draggedData);
139
+ }
137
140
  },
138
141
  },
139
- );
142
+ });
140
143
 
141
- const { setNodeRef: setDropNodeRef, isOver, active: dragItem } = useDroppable({
144
+ const {
145
+ setNodeRef: setDropNodeRef,
146
+ isOver,
147
+ active: dragItem,
148
+ } = useDroppable({
142
149
  id: `drop-${n.index}`,
143
150
  data: {
144
151
  type: 'drag-in-the-blank-drop-choice',
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState, useRef } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import { NodeViewWrapper } from '@tiptap/react';
3
3
  import PropTypes from 'prop-types';
4
4
 
@@ -1,9 +1,8 @@
1
- import React, { useEffect, useRef, useState } from "react";
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { NodeViewWrapper } from '@tiptap/react';
4
4
  import { Chevron } from '../icons/RespArea';
5
- import ReactDOM from "react-dom";
6
- import { MathToolbar } from "@pie-lib/math-toolbar";
5
+ import ReactDOM from 'react-dom';
7
6
 
8
7
  const InlineDropdown = (props) => {
9
8
  const { editor, node, getPos, options, selected } = props;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render, fireEvent } from '@testing-library/react';
2
+ import { fireEvent, render } from '@testing-library/react';
3
3
  import CustomToolbarWrapper from '../custom-toolbar-wrapper';
4
4
 
5
5
  jest.mock('../../components/common/done-button', () => ({
@@ -32,10 +32,6 @@ describe('ExtendedTable', () => {
32
32
  expect(mockParent).toHaveBeenCalledWith(props);
33
33
  expect(result[0]).toBe('table');
34
34
  expect(result[1]).toHaveProperty('style');
35
- expect(result[1].style).toContain('width: 100%');
36
- expect(result[1].style).toContain('table-layout: fixed');
37
- expect(result[1].style).toContain('border-collapse: collapse');
38
- expect(result[1].border).toBe('1');
39
35
  });
40
36
 
41
37
  it('renders table with custom border', () => {
@@ -54,23 +50,6 @@ describe('ExtendedTable', () => {
54
50
  expect(result[1].border).toBe('2');
55
51
  });
56
52
 
57
- it('preserves existing styles', () => {
58
- const props = {
59
- HTMLAttributes: { border: '1' },
60
- };
61
-
62
- const mockParent = jest.fn(() => ['table', { style: 'margin: 10px;' }]);
63
-
64
- const context = {
65
- parent: mockParent,
66
- };
67
-
68
- const result = ExtendedTable.renderHTML.call(context, props);
69
-
70
- expect(result[1].style).toContain('margin: 10px;');
71
- expect(result[1].style).toContain('width: 100%');
72
- });
73
-
74
53
  it('handles styles ending with semicolon', () => {
75
54
  const props = {
76
55
  HTMLAttributes: {},
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render, fireEvent, waitFor } from '@testing-library/react';
2
+ import { fireEvent, render, waitFor } from '@testing-library/react';
3
3
  import ImageComponent from '../image-component';
4
4
 
5
5
  jest.mock('@tiptap/react', () => ({
@@ -1,4 +1,4 @@
1
- import { Media, insertDialog } from '../media';
1
+ import { insertDialog, Media } from '../media';
2
2
 
3
3
  jest.mock('@tiptap/core', () => ({
4
4
  Node: { create: jest.fn((config) => config) },
@@ -1,8 +1,8 @@
1
1
  import {
2
- ResponseAreaExtension,
3
- ExplicitConstructedResponseNode,
4
2
  DragInTheBlankNode,
3
+ ExplicitConstructedResponseNode,
5
4
  InlineDropdownNode,
5
+ ResponseAreaExtension,
6
6
  } from '../responseArea';
7
7
 
8
8
  jest.mock('@tiptap/core', () => ({
@@ -188,31 +188,33 @@ export const CSSMark = Mark.create({
188
188
 
189
189
  addCommands() {
190
190
  return {
191
- setCSSClass: (className) => ({ commands }) => {
192
- return commands.setMark(this.name, { class: className });
193
- },
191
+ setCSSClass:
192
+ (className) =>
193
+ ({ commands }) => {
194
+ return commands.setMark(this.name, { class: className });
195
+ },
194
196
 
195
- unsetCSSClass: () => ({ commands }) => {
196
- return commands.unsetMark(this.name);
197
- },
197
+ unsetCSSClass:
198
+ () =>
199
+ ({ commands }) => {
200
+ return commands.unsetMark(this.name);
201
+ },
198
202
 
199
- openCSSClassDialog: () => ({ editor }) => {
200
- insertDialog({
201
- editor,
202
- selectedText: editor.state.doc.textBetween(editor.state.selection.from, editor.state.selection.to),
203
- parentNode: editor.state.selection.$from.nodeAfter,
204
- opts: this.options.extraCSSRules,
205
- callback: (className) => {
206
- if (className) {
207
- editor
208
- .chain()
209
- .focus()
210
- .setCSSClass(className)
211
- .run();
212
- }
213
- },
214
- });
215
- },
203
+ openCSSClassDialog:
204
+ () =>
205
+ ({ editor }) => {
206
+ insertDialog({
207
+ editor,
208
+ selectedText: editor.state.doc.textBetween(editor.state.selection.from, editor.state.selection.to),
209
+ parentNode: editor.state.selection.$from.nodeAfter,
210
+ opts: this.options.extraCSSRules,
211
+ callback: (className) => {
212
+ if (className) {
213
+ editor.chain().focus().setCSSClass(className).run();
214
+ }
215
+ },
216
+ });
217
+ },
216
218
  };
217
219
  },
218
220
  });
@@ -1,7 +1,6 @@
1
- import React, { useCallback } from 'react';
1
+ import React from 'react';
2
2
  import IconButton from '@mui/material/IconButton';
3
3
  import Delete from '@mui/icons-material/Delete';
4
- import classNames from 'classnames';
5
4
  import { PIE_TOOLBAR__CLASS } from '../constants';
6
5
  import { styled } from '@mui/material/styles';
7
6
  import { DoneButton } from '../components/common/done-button';
@@ -12,7 +12,9 @@ const ExtendedTable = Table.extend({
12
12
 
13
13
  const previousStyle = `${originalTable[1].style}${originalTable[1].style.match(/.*; */) ? '' : ';'}`;
14
14
 
15
- originalTable[1].style = `${previousStyle}`;
15
+ originalTable[1].style = `${previousStyle}
16
+ color: var(--pie-text, black);
17
+ background-color: var(--pie-background, rgba(255, 255, 255))`;
16
18
  originalTable[1].border = border ? border : '1';
17
19
 
18
20
  return originalTable;
@@ -1,6 +1,6 @@
1
- import React, { useState, useRef, useEffect, useCallback } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import isEqual from 'lodash/isEqual';
3
+ import { isEqual } from 'lodash-es';
4
4
  import debug from 'debug';
5
5
  import LinearProgress from '@mui/material/LinearProgress';
6
6
  import { styled } from '@mui/material/styles';
@@ -1,4 +1,4 @@
1
- import { Node, mergeAttributes } from '@tiptap/core';
1
+ import { mergeAttributes, Node } from '@tiptap/core';
2
2
  import { ReactNodeViewRenderer } from '@tiptap/react';
3
3
  import React from 'react';
4
4
  import ImageComponent from './image-component';
@@ -1,6 +1,4 @@
1
- import React from 'react';
2
- import compact from 'lodash/compact';
3
- import isEmpty from 'lodash/isEmpty';
1
+ import { compact, isEmpty } from 'lodash-es';
4
2
  import debug from 'debug';
5
3
 
6
4
  const log = debug('@pie-lib:editable-html:plugins');
@@ -1,10 +1,10 @@
1
- import React, { useState, useEffect, useRef } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import ReactDOM from 'react-dom';
3
- import { Extension, Node, mergeAttributes } from '@tiptap/core';
4
- import { NodeViewWrapper, ReactRenderer, ReactNodeViewRenderer } from '@tiptap/react';
5
- import { Plugin, PluginKey, NodeSelection, TextSelection } from 'prosemirror-state';
3
+ import { Node } from '@tiptap/core';
4
+ import { NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react';
5
+ import { NodeSelection, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
6
6
  import { MathPreview, MathToolbar } from '@pie-lib/math-toolbar';
7
- import { wrapMath, mmlToLatex, renderMath } from '@pie-lib/math-rendering';
7
+ import { wrapMath } from '@pie-lib/math-rendering';
8
8
 
9
9
  const ensureTextAfterMathPluginKey = new PluginKey('ensureTextAfterMath');
10
10
 
@@ -118,30 +118,32 @@ export const MathNode = Node.create({
118
118
 
119
119
  addCommands() {
120
120
  return {
121
- insertMath: (latex = '') => ({ tr, editor, dispatch }) => {
122
- const { state } = editor.view;
123
- const node = state.schema.nodes.math.create({
124
- latex,
125
- });
126
- const { selection } = state;
127
-
128
- // The inserted node is typically just before the cursor
129
- const pos = selection.$from.pos;
130
-
131
- tr.insert(pos, node);
132
-
133
- if (node?.type?.name === this.name) {
134
- // Create a NodeSelection from the current doc
135
- const sel = NodeSelection.create(tr.doc, selection.$from.pos);
136
-
137
- // Build a fresh transaction from the current state and set the selection
138
- tr.setSelection(sel);
139
- }
121
+ insertMath:
122
+ (latex = '') =>
123
+ ({ tr, editor, dispatch }) => {
124
+ const { state } = editor.view;
125
+ const node = state.schema.nodes.math.create({
126
+ latex,
127
+ });
128
+ const { selection } = state;
129
+
130
+ // The inserted node is typically just before the cursor
131
+ const pos = selection.$from.pos;
132
+
133
+ tr.insert(pos, node);
134
+
135
+ if (node?.type?.name === this.name) {
136
+ // Create a NodeSelection from the current doc
137
+ const sel = NodeSelection.create(tr.doc, selection.$from.pos);
138
+
139
+ // Build a fresh transaction from the current state and set the selection
140
+ tr.setSelection(sel);
141
+ }
140
142
 
141
- dispatch(tr);
143
+ dispatch(tr);
142
144
 
143
- return true;
144
- },
145
+ return true;
146
+ },
145
147
  // insertMath: (latex = '') => ({ commands }) => {
146
148
  // return commands.insertContent({
147
149
  // type: this.name,
@@ -78,12 +78,16 @@ export const Media = Node.create({
78
78
 
79
79
  addCommands() {
80
80
  return {
81
- insertMedia: (attrs) => ({ commands }) => {
82
- return commands.insertContent({ type: this.name, attrs });
83
- },
84
- updateMedia: (attrs) => ({ commands }) => {
85
- return commands.updateAttributes(this.name, attrs);
86
- },
81
+ insertMedia:
82
+ (attrs) =>
83
+ ({ commands }) => {
84
+ return commands.insertContent({ type: this.name, attrs });
85
+ },
86
+ updateMedia:
87
+ (attrs) =>
88
+ ({ commands }) => {
89
+ return commands.updateAttributes(this.name, attrs);
90
+ },
87
91
  };
88
92
  },
89
93
 
@@ -143,10 +147,7 @@ export default function MediaNodeView({ editor, node, updateAttributes, deleteNo
143
147
  updateAttributes(data);
144
148
  }
145
149
 
146
- editor
147
- .chain()
148
- .focus()
149
- .run();
150
+ editor.chain().focus().run();
150
151
  },
151
152
  });
152
153
  };
@@ -163,10 +164,7 @@ export default function MediaNodeView({ editor, node, updateAttributes, deleteNo
163
164
  deleteNode();
164
165
  }
165
166
 
166
- editor
167
- .chain()
168
- .focus()
169
- .run();
167
+ editor.chain().focus().run();
170
168
  },
171
169
  });
172
170
  }, []);
@@ -1,11 +1,10 @@
1
1
  import React from 'react';
2
- import { Plugin, PluginKey, TextSelection, NodeSelection } from 'prosemirror-state';
2
+ import { NodeSelection, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
3
3
  import { Extension } from '@tiptap/core';
4
4
  import { Node, ReactNodeViewRenderer } from '@tiptap/react';
5
5
  import ExplicitConstructedResponse from '../components/respArea/ExplicitConstructedResponse';
6
6
  import DragInTheBlank from '../components/respArea/DragInTheBlank/DragInTheBlank';
7
7
  import InlineDropdown from '../components/respArea/InlineDropdown';
8
- import PropTypes from 'prop-types';
9
8
 
10
9
  const lastIndexMap = {};
11
10
 
@@ -130,94 +129,100 @@ export const ResponseAreaExtension = Extension.create({
130
129
 
131
130
  addCommands() {
132
131
  return {
133
- insertResponseArea: (type) => ({ tr, state, dispatch, commands }) => {
134
- const typeName = normalizeType(type);
135
-
136
- // --- Slate: currentRespAreaList + max check ---
137
- const currentCount = countNodesOfType(state.doc, typeName);
138
- if (currentCount >= this.options.maxResponseAreas) {
139
- return false;
140
- }
141
-
142
- // --- Slate: indexing logic (kept identical) ---
143
- if (lastIndexMap[typeName] === undefined) lastIndexMap[typeName] = 0;
144
-
145
- const prevIndex = lastIndexMap[typeName];
146
- const newIndex = prevIndex === 0 ? prevIndex : prevIndex + 1;
147
-
148
- // Slate increments map even if newIndex === 0
149
- lastIndexMap[typeName] += 1;
150
-
151
- const newInline = getDefaultNode({
152
- schema: state.schema,
153
- typeName,
154
- index: newIndex,
155
- });
156
-
157
- if (!newInline) return false;
158
-
159
- // --- Insert logic ---
160
- const { selection } = state;
161
- let insertPos = selection.from;
162
-
163
- // If we're in a NodeSelection, insert before/after is ambiguous;
164
- // We'll insert at its "from" (like your current code).
165
- // If insertion fails, we fallback to end of doc.
166
- const tryInsertAt = (pos) => {
167
- try {
168
- tr.insert(pos, newInline);
169
- return pos;
170
- } catch (e) {
171
- return null;
132
+ insertResponseArea:
133
+ (type) =>
134
+ ({ tr, state, dispatch, commands }) => {
135
+ const typeName = normalizeType(type);
136
+
137
+ // --- Slate: currentRespAreaList + max check ---
138
+ const currentCount = countNodesOfType(state.doc, typeName);
139
+ if (currentCount >= this.options.maxResponseAreas) {
140
+ return false;
172
141
  }
173
- };
174
-
175
- let usedPos = tryInsertAt(insertPos);
176
-
177
- // Slate branch: "markup empty and there's no focus"
178
- // ProseMirror doesn't expose "no focus" the same way, so the closest
179
- // equivalent fallback is inserting at end of document.
180
- if (usedPos == null) {
181
- usedPos = tryInsertAt(tr.doc.content.size);
182
- }
183
- if (usedPos == null) return false;
184
-
185
- // Optionally select the node you just inserted (like your original command)
186
- // tr.setSelection(NodeSelection.create(tr.doc, usedPos))
187
-
188
- // --- Cursor move behavior for certain types (Slate: moveFocusTo next text) ---
189
- if (
190
- ['math_templated', 'inline_dropdown', 'drag_in_the_blank', 'explicit_constructed_response'].includes(typeName)
191
- ) {
192
- tr.setSelection(NodeSelection.create(tr.doc, usedPos));
193
- } else {
194
- // Default: put cursor after inserted node
195
- const after = usedPos + newInline.nodeSize;
196
- tr.setSelection(selectionAfterPos(tr.doc, after));
197
- }
198
-
199
- if (dispatch) {
200
- commands.focus();
201
- dispatch(tr);
202
- }
203
-
204
- return true;
205
- },
206
- refreshResponseArea: () => ({ tr, state, commands, dispatch }) => {
207
- const { selection } = state;
208
- const node = selection.$from.nodeAfter;
209
- const nodePos = selection.from;
210
142
 
211
- tr.setNodeMarkup(nodePos, undefined, { ...node.attrs, updated: `${Date.now()}` });
212
- tr.setSelection(NodeSelection.create(tr.doc, nodePos));
143
+ // --- Slate: indexing logic (kept identical) ---
144
+ if (lastIndexMap[typeName] === undefined) lastIndexMap[typeName] = 0;
145
+
146
+ const prevIndex = lastIndexMap[typeName];
147
+ const newIndex = prevIndex === 0 ? prevIndex : prevIndex + 1;
148
+
149
+ // Slate increments map even if newIndex === 0
150
+ lastIndexMap[typeName] += 1;
151
+
152
+ const newInline = getDefaultNode({
153
+ schema: state.schema,
154
+ typeName,
155
+ index: newIndex,
156
+ });
157
+
158
+ if (!newInline) return false;
159
+
160
+ // --- Insert logic ---
161
+ const { selection } = state;
162
+ let insertPos = selection.from;
163
+
164
+ // If we're in a NodeSelection, insert before/after is ambiguous;
165
+ // We'll insert at its "from" (like your current code).
166
+ // If insertion fails, we fallback to end of doc.
167
+ const tryInsertAt = (pos) => {
168
+ try {
169
+ tr.insert(pos, newInline);
170
+ return pos;
171
+ } catch (e) {
172
+ return null;
173
+ }
174
+ };
213
175
 
214
- if (dispatch) {
215
- commands.focus();
216
- dispatch(tr);
217
- }
176
+ let usedPos = tryInsertAt(insertPos);
218
177
 
219
- return true;
220
- },
178
+ // Slate branch: "markup empty and there's no focus"
179
+ // ProseMirror doesn't expose "no focus" the same way, so the closest
180
+ // equivalent fallback is inserting at end of document.
181
+ if (usedPos == null) {
182
+ usedPos = tryInsertAt(tr.doc.content.size);
183
+ }
184
+ if (usedPos == null) return false;
185
+
186
+ // Optionally select the node you just inserted (like your original command)
187
+ // tr.setSelection(NodeSelection.create(tr.doc, usedPos))
188
+
189
+ // --- Cursor move behavior for certain types (Slate: moveFocusTo next text) ---
190
+ if (
191
+ ['math_templated', 'inline_dropdown', 'drag_in_the_blank', 'explicit_constructed_response'].includes(
192
+ typeName,
193
+ )
194
+ ) {
195
+ tr.setSelection(NodeSelection.create(tr.doc, usedPos));
196
+ } else {
197
+ // Default: put cursor after inserted node
198
+ const after = usedPos + newInline.nodeSize;
199
+ tr.setSelection(selectionAfterPos(tr.doc, after));
200
+ }
201
+
202
+ if (dispatch) {
203
+ commands.focus();
204
+ dispatch(tr);
205
+ }
206
+
207
+ return true;
208
+ },
209
+ refreshResponseArea:
210
+ () =>
211
+ ({ tr, state, commands, dispatch }) => {
212
+ const { selection } = state;
213
+ const node = selection.$from.nodeAfter;
214
+ const nodePos = selection.from;
215
+
216
+ tr.setNodeMarkup(nodePos, undefined, { ...node.attrs, updated: `${Date.now()}` });
217
+ tr.setSelection(NodeSelection.create(tr.doc, nodePos));
218
+
219
+ if (dispatch) {
220
+ commands.focus();
221
+ dispatch(tr);
222
+ }
223
+
224
+ return true;
225
+ },
221
226
  };
222
227
  },
223
228
  });
@@ -1,5 +1,4 @@
1
1
  import { color } from '@pie-lib/render-ui';
2
- import { primary } from '../theme';
3
2
 
4
3
  const styles = (theme) => ({
5
4
  root: {