@ai-matrx/rich-editor 0.4.0 → 0.4.2
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.
- package/CHANGELOG.md +14 -0
- package/dist/editor/RichEditorImpl.d.ts +12 -2
- package/dist/editor/RichEditorImpl.js +21 -8
- package/dist/editor/SplitPanes.d.ts +11 -0
- package/dist/editor/SplitPanes.js +24 -0
- package/dist/format/FormatButtons.js +42 -20
- package/dist/in-place/InPlaceEditor.d.ts +11 -1
- package/dist/in-place/InPlaceEditor.js +6 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.2
|
|
4
|
+
### A short toolbar keeps what people reach for (2026-10-08)
|
|
5
|
+
|
|
6
|
+
- `FormatButtons` folds by priority, not position: Bold, Italic, Text style, Bulleted list, Checklist and Link stay in view longest; history, inserts and the rest fold into More first. Display order never changes; dividers sit only between visible groups. The text-style menu is narrower (88px). Live: notes beside the chat panel showed only Undo, Redo, Text style and More.
|
|
7
|
+
|
|
8
|
+
Consumer action: none.
|
|
9
|
+
|
|
10
|
+
## 0.4.1
|
|
11
|
+
### Edit in place: Write, Split, Source, Preview (2026-10-08)
|
|
12
|
+
|
|
13
|
+
- Every edit-in-place editor (an AI answer in chat, window panels and the side drawer) offers Write (the visual editor, default), **Split** (the raw markdown editable on the left, the full live render on the right; stacked on a phone), Source and Preview — in the editor's own view switch, never behind a menu on desktop.
|
|
14
|
+
- `RichEditor` gains `views` (the views offered, in order) and `viewLabels` (a host's own word for a view). Hosts that pass neither are unchanged.
|
|
15
|
+
- The formatting toolbar works in Split's raw pane like in Source.
|
|
16
|
+
|
|
3
17
|
## 0.4.0
|
|
4
18
|
### ONE formatting toolbar in every editor and every mode (2026-10-07)
|
|
5
19
|
|
|
@@ -8,7 +8,9 @@ import type { BlockPlacement } from "../core/text-insertion.js";
|
|
|
8
8
|
import type { DeclaredVariable } from "../core/variables.js";
|
|
9
9
|
import { type ImagePolicyDeclaration } from "@ai-matrx/rich-content/levels/prose/remote-image-policy";
|
|
10
10
|
import type { AnnotationRecord } from "@ai-matrx/rich-content/rich-document/host-types";
|
|
11
|
-
export type RichEditorView = "visual" | "source" | "preview";
|
|
11
|
+
export type RichEditorView = "visual" | "split" | "source" | "preview";
|
|
12
|
+
/** The views a host gets unless it names its own (`views`). Split is offered where a host asks for it. */
|
|
13
|
+
export declare const DEFAULT_VIEWS: readonly RichEditorView[];
|
|
12
14
|
/**
|
|
13
15
|
* What a host that carries its OWN chrome (view switch, find, outline, dictation,
|
|
14
16
|
* its own right-click menu) drives the editor through. Every verb acts on the
|
|
@@ -72,6 +74,14 @@ export interface RichEditorProps {
|
|
|
72
74
|
*/
|
|
73
75
|
view?: RichEditorView;
|
|
74
76
|
onViewChange?: (view: RichEditorView) => void;
|
|
77
|
+
/**
|
|
78
|
+
* The views the switch offers, in order (default Visual, Source, Preview).
|
|
79
|
+
* Split — the raw text editable on the left, the full render live on the
|
|
80
|
+
* right — is offered by naming it here (edit-in-place: Write, Split, Source, Preview).
|
|
81
|
+
*/
|
|
82
|
+
views?: readonly RichEditorView[];
|
|
83
|
+
/** A host's own word for a view ("Write" for Visual in edit-in-place). */
|
|
84
|
+
viewLabels?: Partial<Record<RichEditorView, string>>;
|
|
75
85
|
/**
|
|
76
86
|
* "full" (default): the editor's toolbar row — views, tools, dictation, Save.
|
|
77
87
|
* "bare": no row of its own; the host's chrome carries the view switch, find,
|
|
@@ -135,4 +145,4 @@ export interface RichEditorProps {
|
|
|
135
145
|
*/
|
|
136
146
|
imagePolicy?: ImagePolicyDeclaration;
|
|
137
147
|
}
|
|
138
|
-
export default function RichEditorImpl({ value, onSave, onChange, variables, onDeclareVariable, defaultView, view: controlledView, onViewChange, chrome, hostContextMenu, controllerRef, readOnly, placeholder, surfaceName, sourceFeature, contentSource, annotationRecord, saveLabel, className, toolbarExtras, onCancel, cancelLabel, defaultOutlineOpen, onNothingToSave, imagePolicy, }: RichEditorProps): import("react").JSX.Element;
|
|
148
|
+
export default function RichEditorImpl({ value, onSave, onChange, variables, onDeclareVariable, defaultView, view: controlledView, onViewChange, views: viewsProp, viewLabels, chrome, hostContextMenu, controllerRef, readOnly, placeholder, surfaceName, sourceFeature, contentSource, annotationRecord, saveLabel, className, toolbarExtras, onCancel, cancelLabel, defaultOutlineOpen, onNothingToSave, imagePolicy, }: RichEditorProps): import("react").JSX.Element;
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Check,
|
|
8
8
|
ChevronDown,
|
|
9
9
|
Code2,
|
|
10
|
+
Columns2,
|
|
10
11
|
Eye,
|
|
11
12
|
Focus,
|
|
12
13
|
Hash,
|
|
@@ -50,6 +51,7 @@ import { RichEditorContext } from "./RichEditorContext.js";
|
|
|
50
51
|
import { VisualEditor } from "../visual/VisualEditor.js";
|
|
51
52
|
import { SourceEditor } from "../source/SourceEditor.js";
|
|
52
53
|
import { FormatButtons } from "../format/FormatButtons.js";
|
|
54
|
+
import { SplitPanes } from "./SplitPanes.js";
|
|
53
55
|
import { FindReplacePanel } from "../panels/FindReplacePanel.js";
|
|
54
56
|
import { OutlinePanel } from "../panels/OutlinePanel.js";
|
|
55
57
|
import { ShortcutsDialog } from "../panels/ShortcutsDialog.js";
|
|
@@ -59,7 +61,7 @@ import { withImagePolicy } from "@ai-matrx/rich-content/levels/prose/remote-imag
|
|
|
59
61
|
import { RecordAnnotations } from "@ai-matrx/rich-content/host/app-bindings";
|
|
60
62
|
import { annotationRecordOf } from "@ai-matrx/rich-content/host/app-bindings";
|
|
61
63
|
import { formatCount } from "@ai-matrx/kit/format";
|
|
62
|
-
const
|
|
64
|
+
const DEFAULT_VIEWS = ["visual", "source", "preview"];
|
|
63
65
|
function describeDelta(delta) {
|
|
64
66
|
const raw = delta.before ?? delta.after ?? "";
|
|
65
67
|
const block = islandMeta(delta.islandType, raw);
|
|
@@ -79,6 +81,7 @@ function describeDelta(delta) {
|
|
|
79
81
|
}
|
|
80
82
|
const VIEW_META = {
|
|
81
83
|
visual: { label: "Visual", icon: Type },
|
|
84
|
+
split: { label: "Split", icon: Columns2 },
|
|
82
85
|
source: { label: "Source", icon: Code2 },
|
|
83
86
|
preview: { label: "Preview", icon: Eye }
|
|
84
87
|
};
|
|
@@ -91,6 +94,8 @@ function RichEditorImpl({
|
|
|
91
94
|
defaultView = "visual",
|
|
92
95
|
view: controlledView,
|
|
93
96
|
onViewChange,
|
|
97
|
+
views: viewsProp,
|
|
98
|
+
viewLabels,
|
|
94
99
|
chrome = "full",
|
|
95
100
|
hostContextMenu = false,
|
|
96
101
|
controllerRef,
|
|
@@ -110,6 +115,8 @@ function RichEditorImpl({
|
|
|
110
115
|
imagePolicy
|
|
111
116
|
}) {
|
|
112
117
|
const isMobile = useIsMobile();
|
|
118
|
+
const VIEWS = viewsProp?.length ? viewsProp : DEFAULT_VIEWS;
|
|
119
|
+
const viewLabel = (option) => viewLabels?.[option] ?? VIEW_META[option].label;
|
|
113
120
|
const [stored, setStored] = useState(value);
|
|
114
121
|
const [current, setCurrent] = useState(value);
|
|
115
122
|
const [view, setView] = useState(controlledView ?? defaultView);
|
|
@@ -404,18 +411,19 @@ function RichEditorImpl({
|
|
|
404
411
|
},
|
|
405
412
|
`source-${mountKey}`
|
|
406
413
|
);
|
|
407
|
-
const
|
|
414
|
+
const rendered = (content) => /* @__PURE__ */ jsx(
|
|
408
415
|
RichDocument,
|
|
409
416
|
{
|
|
410
417
|
imagePolicy: "inherit",
|
|
411
|
-
content
|
|
418
|
+
content,
|
|
412
419
|
source: contentSource,
|
|
413
420
|
enableContextMenu: true,
|
|
414
421
|
hideCopyButton: true,
|
|
415
422
|
allowFullScreenEditor: false,
|
|
416
423
|
actions: { exclude: ["edit", "delete-message"] }
|
|
417
424
|
}
|
|
418
|
-
)
|
|
425
|
+
);
|
|
426
|
+
const editingBody = hostContextMenu ? /* @__PURE__ */ jsx("div", { className: "h-full min-h-0", children: viewEditor }) : /* @__PURE__ */ jsx(
|
|
419
427
|
EditableContextMenu,
|
|
420
428
|
{
|
|
421
429
|
sourceFeature,
|
|
@@ -433,6 +441,10 @@ function RichEditorImpl({
|
|
|
433
441
|
children: /* @__PURE__ */ jsx("div", { className: "h-full min-h-0", children: viewEditor })
|
|
434
442
|
}
|
|
435
443
|
);
|
|
444
|
+
const body = view === "preview" ? /* @__PURE__ */ jsx("div", { className: "h-full overflow-y-auto", children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-3xl px-6 py-6 sm:px-10", children: rendered(current) }) }) : view === "split" ? (
|
|
445
|
+
// Split: the raw text (Source, raw) on the left, the live render of the same text on the right.
|
|
446
|
+
/* @__PURE__ */ jsx(SplitPanes, { stacked: isMobile, raw: editingBody, render: rendered(deferred) })
|
|
447
|
+
) : editingBody;
|
|
436
448
|
const CurrentViewIcon = VIEW_META[view].icon;
|
|
437
449
|
const tools = [
|
|
438
450
|
{ id: "find", label: "Find & replace (\u2318F)", icon: Search, active: findMode !== null, disabled: view === "preview", run: () => setFindMode((mode) => mode ? null : "find") },
|
|
@@ -535,7 +547,7 @@ function RichEditorImpl({
|
|
|
535
547
|
"button",
|
|
536
548
|
{
|
|
537
549
|
type: "button",
|
|
538
|
-
"aria-label": `View: ${
|
|
550
|
+
"aria-label": `View: ${viewLabel(view)}`,
|
|
539
551
|
title: "Switch view",
|
|
540
552
|
className: "flex h-8 shrink-0 items-center gap-1 rounded-md border border-border bg-background/60 px-2 text-xs font-medium text-primary",
|
|
541
553
|
children: [
|
|
@@ -548,7 +560,7 @@ function RichEditorImpl({
|
|
|
548
560
|
const Icon = VIEW_META[option].icon;
|
|
549
561
|
return /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: () => switchView(option), children: [
|
|
550
562
|
/* @__PURE__ */ jsx(Icon, { className: "mr-2 h-4 w-4" }),
|
|
551
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1", children:
|
|
563
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: viewLabel(option) }),
|
|
552
564
|
view === option && /* @__PURE__ */ jsx(Check, { className: "ml-2 h-4 w-4 text-primary" })
|
|
553
565
|
] }, option);
|
|
554
566
|
}) })
|
|
@@ -560,7 +572,7 @@ function RichEditorImpl({
|
|
|
560
572
|
{
|
|
561
573
|
type: "button",
|
|
562
574
|
role: "tab",
|
|
563
|
-
title:
|
|
575
|
+
title: viewLabel(option),
|
|
564
576
|
"aria-selected": view === option,
|
|
565
577
|
onClick: () => switchView(option),
|
|
566
578
|
className: cn(
|
|
@@ -569,7 +581,7 @@ function RichEditorImpl({
|
|
|
569
581
|
),
|
|
570
582
|
children: [
|
|
571
583
|
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
|
|
572
|
-
|
|
584
|
+
viewLabel(option)
|
|
573
585
|
]
|
|
574
586
|
},
|
|
575
587
|
option
|
|
@@ -744,5 +756,6 @@ function ToolbarButton({
|
|
|
744
756
|
);
|
|
745
757
|
}
|
|
746
758
|
export {
|
|
759
|
+
DEFAULT_VIEWS,
|
|
747
760
|
RichEditorImpl as default
|
|
748
761
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface SplitPanesProps {
|
|
3
|
+
/** The editable raw text (the Source engine). */
|
|
4
|
+
raw: ReactNode;
|
|
5
|
+
/** The live render of the same text. */
|
|
6
|
+
render: ReactNode;
|
|
7
|
+
/** Stack top/bottom instead of side by side (phones). */
|
|
8
|
+
stacked?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function SplitPanes({ raw, render, stacked }: SplitPanesProps): import("react").JSX.Element;
|
|
11
|
+
export default SplitPanes;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "@ai-matrx/design-system";
|
|
4
|
+
function SplitPanes({ raw, render, stacked = false }) {
|
|
5
|
+
return /* @__PURE__ */ jsxs(
|
|
6
|
+
"div",
|
|
7
|
+
{
|
|
8
|
+
"data-split-view": "",
|
|
9
|
+
className: cn(
|
|
10
|
+
"grid h-full min-h-0",
|
|
11
|
+
stacked ? "grid-cols-1 grid-rows-2 divide-y divide-border" : "grid-cols-2 divide-x divide-border"
|
|
12
|
+
),
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ jsx("section", { "aria-label": "Raw text", "data-split-pane": "raw", className: "min-h-0 min-w-0 overflow-hidden", children: raw }),
|
|
15
|
+
/* @__PURE__ */ jsx("section", { "aria-label": "Rendered", "data-split-pane": "render", className: "min-h-0 min-w-0 overflow-y-auto", children: /* @__PURE__ */ jsx("div", { className: "px-4 py-4 sm:px-6", children: render }) })
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
var SplitPanes_default = SplitPanes;
|
|
21
|
+
export {
|
|
22
|
+
SplitPanes,
|
|
23
|
+
SplitPanes_default as default
|
|
24
|
+
};
|
|
@@ -54,15 +54,41 @@ const FORMAT_GROUPS = [
|
|
|
54
54
|
];
|
|
55
55
|
const FORMAT_BUTTONS = FORMAT_GROUPS.flat();
|
|
56
56
|
const ESSENTIAL_COMMANDS = ["bold", "italic", "bulletList", "taskList", "link"];
|
|
57
|
+
const KEEP = {
|
|
58
|
+
bold: 20,
|
|
59
|
+
italic: 19,
|
|
60
|
+
style: 18,
|
|
61
|
+
bulletList: 17,
|
|
62
|
+
taskList: 16,
|
|
63
|
+
link: 15,
|
|
64
|
+
orderedList: 14,
|
|
65
|
+
undo: 13,
|
|
66
|
+
redo: 12,
|
|
67
|
+
strike: 11,
|
|
68
|
+
code: 10,
|
|
69
|
+
quote: 9,
|
|
70
|
+
codeBlock: 8,
|
|
71
|
+
table: 7,
|
|
72
|
+
horizontalRule: 6
|
|
73
|
+
};
|
|
74
|
+
const keepOf = (slot) => slot.kind === "button" ? KEEP[slot.spec.command] ?? 0 : slot.kind === "style" ? KEEP.style : 0;
|
|
57
75
|
function slotsOf(variant) {
|
|
58
76
|
if (variant === "essential") {
|
|
59
77
|
return [{ kind: "style" }, ...ESSENTIAL_COMMANDS.map((command) => ({ kind: "button", spec: FORMAT_BUTTONS.find((b) => b.command === command) }))];
|
|
60
78
|
}
|
|
61
79
|
const out = [];
|
|
62
80
|
FORMAT_GROUPS.forEach((group, index) => {
|
|
63
|
-
|
|
64
|
-
out.push(...group.map((b) => ({ kind: "button", spec: b })));
|
|
65
|
-
if (index === 0) out.push({ kind: "
|
|
81
|
+
const g = index === 0 ? 0 : index + 1;
|
|
82
|
+
out.push(...group.map((b) => ({ kind: "button", spec: b, group: g })));
|
|
83
|
+
if (index === 0) out.push({ kind: "style", group: 1 });
|
|
84
|
+
});
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
function withDividers(slots) {
|
|
88
|
+
const out = [];
|
|
89
|
+
slots.forEach((slot, i) => {
|
|
90
|
+
if (i > 0 && slot.group !== void 0 && slot.group !== slots[i - 1].group) out.push({ kind: "divider" });
|
|
91
|
+
out.push(slot);
|
|
66
92
|
});
|
|
67
93
|
return out;
|
|
68
94
|
}
|
|
@@ -123,27 +149,23 @@ function FormatButtons({ resolve, disabled = false, className, size = "sm", vari
|
|
|
123
149
|
resolve()?.run(command);
|
|
124
150
|
};
|
|
125
151
|
const button = size === "xs" ? 26 : 30;
|
|
126
|
-
const styleWidth = variant === "essential" ? size === "xs" ? 40 : 44 :
|
|
152
|
+
const styleWidth = variant === "essential" ? size === "xs" ? 40 : 44 : 90;
|
|
127
153
|
const widthOf = (slot) => slot.kind === "button" ? button : slot.kind === "style" ? styleWidth : 11;
|
|
128
154
|
const all = slotsOf(variant);
|
|
129
|
-
let
|
|
155
|
+
let kept = all;
|
|
130
156
|
let folded = variant === "essential" ? slotsOf("full").filter((s) => s.kind === "button" && !ESSENTIAL_COMMANDS.includes(s.spec.command)) : [];
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (used + widthOf(slot) > budget) break;
|
|
139
|
-
used += widthOf(slot);
|
|
140
|
-
count += 1;
|
|
141
|
-
}
|
|
142
|
-
shown = all.slice(0, count);
|
|
143
|
-
folded = [...all.slice(count), ...folded];
|
|
157
|
+
const rowWidth = (slots, more) => withDividers(slots).reduce((sum, s) => sum + widthOf(s), 0) + (more ? button : 0);
|
|
158
|
+
if (width > 0 && rowWidth(kept, folded.length > 0) > width + 4) {
|
|
159
|
+
const byKeep = [...all].sort((x, y) => keepOf(x) - keepOf(y));
|
|
160
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
161
|
+
for (const slot of byKeep) {
|
|
162
|
+
if (rowWidth(all.filter((s) => !dropped.has(s)), true) <= width + 4) break;
|
|
163
|
+
dropped.add(slot);
|
|
144
164
|
}
|
|
165
|
+
kept = all.filter((s) => !dropped.has(s));
|
|
166
|
+
folded = [...all.filter((s) => dropped.has(s)), ...folded];
|
|
145
167
|
}
|
|
146
|
-
|
|
168
|
+
const shown = withDividers(kept);
|
|
147
169
|
const foldedButtons = folded.filter((s) => s.kind === "button");
|
|
148
170
|
const styleFolded = folded.some((s) => s.kind === "style");
|
|
149
171
|
const iconClass = size === "xs" ? "h-3.5 w-3.5" : "h-4 w-4";
|
|
@@ -178,7 +200,7 @@ function FormatButtons({ resolve, disabled = false, className, size = "sm", vari
|
|
|
178
200
|
className: cn(
|
|
179
201
|
"inline-flex shrink-0 items-center gap-1 rounded-md px-1.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
180
202
|
size === "xs" ? "h-6" : "h-7",
|
|
181
|
-
variant === "full" && "w-[
|
|
203
|
+
variant === "full" && "w-[88px] justify-between"
|
|
182
204
|
),
|
|
183
205
|
children: [
|
|
184
206
|
variant === "essential" ? /* @__PURE__ */ jsx(StyleIcon, { className: iconClass }) : /* @__PURE__ */ jsx("span", { className: "truncate", children: BLOCK_SHORT[currentStyle.command] }),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import { type RichEditorProps } from "../editor/RichEditor.js";
|
|
2
|
+
import { type RichEditorProps, type RichEditorView } from "../editor/RichEditor.js";
|
|
3
3
|
import type { CaretContext } from "../core/caret-context.js";
|
|
4
4
|
import { type InPlaceSaveMode } from "./in-place-session.js";
|
|
5
5
|
export type InPlaceEditorPassThrough = Omit<RichEditorProps, "value" | "onSave" | "onChange" | "onCancel" | "onNothingToSave" | "cancelLabel" | "controllerRef">;
|
|
@@ -33,6 +33,16 @@ export interface InPlaceEditorProps {
|
|
|
33
33
|
*/
|
|
34
34
|
actions?: readonly InPlaceAction[];
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* The modes of every edit-in-place editor, in this order (Arman, 2026-10-08:
|
|
38
|
+
* "Default: Write. Next option: Split (plain text on the left and full render
|
|
39
|
+
* on the right). Then, whatever else we need. But you cannot gobble them up and
|
|
40
|
+
* hide the things we need."). Write is the visual editor and always opens
|
|
41
|
+
* first (no per-person memory exists for this); Split shows the raw string
|
|
42
|
+
* next to its render. All four sit in the editor's own toolbar row.
|
|
43
|
+
*/
|
|
44
|
+
export declare const IN_PLACE_VIEWS: readonly RichEditorView[];
|
|
45
|
+
export declare const IN_PLACE_VIEW_LABELS: Partial<Record<RichEditorView, string>>;
|
|
36
46
|
export interface InPlaceAction {
|
|
37
47
|
id: string;
|
|
38
48
|
label: string;
|
|
@@ -8,6 +8,8 @@ import { cn } from "@ai-matrx/design-system";
|
|
|
8
8
|
import { ConfirmDialog } from "@ai-matrx/rich-content/host/app-bindings";
|
|
9
9
|
import RichEditor from "../editor/RichEditor.js";
|
|
10
10
|
import { createInPlaceSession } from "./in-place-session.js";
|
|
11
|
+
const IN_PLACE_VIEWS = ["visual", "split", "source", "preview"];
|
|
12
|
+
const IN_PLACE_VIEW_LABELS = { visual: "Write" };
|
|
11
13
|
function InPlaceEditor({
|
|
12
14
|
value,
|
|
13
15
|
mode = "explicit",
|
|
@@ -129,6 +131,8 @@ function InPlaceEditor({
|
|
|
129
131
|
{
|
|
130
132
|
defaultView: "visual",
|
|
131
133
|
defaultOutlineOpen: false,
|
|
134
|
+
views: IN_PLACE_VIEWS,
|
|
135
|
+
viewLabels: IN_PLACE_VIEW_LABELS,
|
|
132
136
|
...editor,
|
|
133
137
|
controllerRef: controller,
|
|
134
138
|
value: openedOn,
|
|
@@ -206,6 +210,8 @@ function keepCaretInView(root, settleMs = 2500) {
|
|
|
206
210
|
return stop;
|
|
207
211
|
}
|
|
208
212
|
export {
|
|
213
|
+
IN_PLACE_VIEWS,
|
|
214
|
+
IN_PLACE_VIEW_LABELS,
|
|
209
215
|
InPlaceEditor,
|
|
210
216
|
keepCaretInView
|
|
211
217
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/rich-editor",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "The AI Matrx editor: Visual (Tiptap), Source (CodeMirror), Preview, the markdown formatting command layer, and edit-in-place — over @ai-matrx/rich-content. Consumers that only render never install it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -90,13 +90,13 @@
|
|
|
90
90
|
"@tiptap/suggestion": "^3.31.3",
|
|
91
91
|
"github-slugger": "^2.0.0",
|
|
92
92
|
"lucide-react": "^1.48.0",
|
|
93
|
-
"@ai-matrx/alchemy": "latest",
|
|
94
93
|
"@ai-matrx/content-ir": "latest",
|
|
94
|
+
"@ai-matrx/agents": "latest",
|
|
95
95
|
"@ai-matrx/design-system": "latest",
|
|
96
96
|
"@ai-matrx/kit": "latest",
|
|
97
97
|
"@ai-matrx/print": "latest",
|
|
98
|
-
"@ai-matrx/
|
|
99
|
-
"@ai-matrx/
|
|
98
|
+
"@ai-matrx/rich-content": "latest",
|
|
99
|
+
"@ai-matrx/alchemy": "latest"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
102
|
"react": ">=19.0.0",
|