@ai-matrx/rich-content 0.2.41 → 0.2.44
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 +20 -0
- package/dist/copy/CopySplitButton.d.ts +6 -1
- package/dist/copy/CopySplitButton.js +42 -21
- package/dist/copy/ExportPalette.d.ts +3 -6
- package/dist/copy/ExportPalette.js +7 -26
- package/dist/rich-document/variants/ActionBar.js +10 -3
- package/dist/selection-toolbar/selection-actions.d.ts +8 -0
- package/dist/selection-toolbar/selection-actions.js +12 -2
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.44
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/rich-content/v0.2.43..npm/rich-content/v0.2.44 -- apps/shared/rich-content`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
9
|
+
## 0.2.43
|
|
10
|
+
|
|
11
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
12
|
+
`git diff npm/rich-content/v0.2.41..npm/rich-content/v0.2.43 -- apps/shared/rich-content`).
|
|
13
|
+
No source changes intended and no consumer action required.
|
|
14
|
+
|
|
15
|
+
## 0.2.42
|
|
16
|
+
|
|
17
|
+
- **No second visible Export control.** `ExportPaletteAnchor` loses `trigger`; a host with no ⋯ menu passes `CopySplitButton onExport`, which adds ONE "Export…" row under a divider in the chevron menu (`useExportPaletteKey` + `openExportPalette`). `ActionBar` with `hideOverflow` does this itself. A host with a ⋯ menu keeps exactly the two rows.
|
|
18
|
+
- **A context action takes Comment's slot.** `SELECTION_CONTEXT_PRIMARY` / `selectionPrimaryFor`: on a study guide the reading row is Copy · Highlight · I don't get this · Ask AI · More; Comment is a More row there. Still at most 6.
|
|
19
|
+
|
|
20
|
+
**Consumer action:** replace `<ExportPaletteAnchor trigger …/>` with an anchor keyed by `useExportPaletteKey()` and `onExport={() => openExportPalette(key)}` on the split.
|
|
21
|
+
|
|
3
22
|
## 0.2.41
|
|
4
23
|
|
|
5
24
|
- **Copy is two rows.** `CopySplitButton`'s chevron menu is exactly "Copy markdown" and "Copy plain text"; the `mountMore` slot (the Alchemy palette under the chevron) is removed.
|
|
@@ -289,3 +308,4 @@ held-back streaming text; the heal tests encode 1.3.1's behavior).
|
|
|
289
308
|
- matrx-frontend: call `configureRichContent` at the root (see README) and import from
|
|
290
309
|
`@ai-matrx/rich-content/<domain>/<module>`.
|
|
291
310
|
|
|
311
|
+
|
|
@@ -13,10 +13,15 @@ export interface CopySplitButtonProps {
|
|
|
13
13
|
* through `className`.
|
|
14
14
|
*/
|
|
15
15
|
moreOnly?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Open the export palette — ONLY for a host with no ⋯ menu of its own: adds one "Export…" row
|
|
18
|
+
* under a divider. A host with a ⋯ menu leaves this out; Export… lives in that menu.
|
|
19
|
+
*/
|
|
20
|
+
onExport?: () => void;
|
|
16
21
|
/** Keep the page's text selection when pressed (the selection toolbar). */
|
|
17
22
|
preserveSelection?: boolean;
|
|
18
23
|
disabled?: boolean;
|
|
19
24
|
className?: string;
|
|
20
25
|
}
|
|
21
|
-
export declare function CopySplitButton({ copy, label, size, moreOnly, preserveSelection, disabled, className, }: CopySplitButtonProps): React.ReactElement;
|
|
26
|
+
export declare function CopySplitButton({ copy, label, size, moreOnly, onExport, preserveSelection, disabled, className, }: CopySplitButtonProps): React.ReactElement;
|
|
22
27
|
export default CopySplitButton;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { Check, ChevronDown, ClipboardCopy, Copy, FileCode2, Type } from "lucide-react";
|
|
4
|
+
import { Check, ChevronDown, ClipboardCopy, Copy, FileCode2, Share, Type } from "lucide-react";
|
|
5
5
|
import { Popover, PopoverContent, PopoverTrigger, cn } from "@ai-matrx/design-system";
|
|
6
6
|
import { defaultCopyFlavor } from "./copy-commands.js";
|
|
7
7
|
const ROWS = [
|
|
@@ -13,6 +13,7 @@ function CopySplitButton({
|
|
|
13
13
|
label = "Copy",
|
|
14
14
|
size = "sm",
|
|
15
15
|
moreOnly = false,
|
|
16
|
+
onExport,
|
|
16
17
|
preserveSelection = false,
|
|
17
18
|
disabled = false,
|
|
18
19
|
className
|
|
@@ -86,33 +87,53 @@ function CopySplitButton({
|
|
|
86
87
|
children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
|
|
87
88
|
}
|
|
88
89
|
) }),
|
|
89
|
-
/* @__PURE__ */
|
|
90
|
+
/* @__PURE__ */ jsxs(
|
|
90
91
|
PopoverContent,
|
|
91
92
|
{
|
|
92
93
|
align: "start",
|
|
93
94
|
className: "w-auto min-w-60 max-w-[min(24rem,calc(100vw-16px))] p-1",
|
|
94
95
|
onOpenAutoFocus: preserveSelection ? (event) => event.preventDefault() : void 0,
|
|
95
96
|
"data-copy-split-menu": "",
|
|
96
|
-
children:
|
|
97
|
-
"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ jsx("div", { role: "menu", "aria-label": "Copy as", children: ROWS.map(({ flavor: rowFlavor, label: rowLabel, icon: Icon }) => /* @__PURE__ */ jsxs(
|
|
99
|
+
"button",
|
|
100
|
+
{
|
|
101
|
+
type: "button",
|
|
102
|
+
role: "menuitem",
|
|
103
|
+
"data-copy-flavor": rowFlavor,
|
|
104
|
+
onMouseDown: keep,
|
|
105
|
+
onClick: () => {
|
|
106
|
+
setOpen(false);
|
|
107
|
+
void run(rowFlavor);
|
|
108
|
+
},
|
|
109
|
+
className: "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent",
|
|
110
|
+
children: [
|
|
111
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
112
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: rowLabel }),
|
|
113
|
+
rowFlavor === flavor ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Default" }) : null
|
|
114
|
+
]
|
|
106
115
|
},
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
rowFlavor
|
|
117
|
+
)) }),
|
|
118
|
+
onExport ? /* @__PURE__ */ jsx("div", { className: "mt-1 border-t border-border pt-1", children: /* @__PURE__ */ jsxs(
|
|
119
|
+
"button",
|
|
120
|
+
{
|
|
121
|
+
type: "button",
|
|
122
|
+
role: "menuitem",
|
|
123
|
+
"data-copy-split-export": "",
|
|
124
|
+
onMouseDown: keep,
|
|
125
|
+
onClick: () => {
|
|
126
|
+
setOpen(false);
|
|
127
|
+
onExport();
|
|
128
|
+
},
|
|
129
|
+
className: "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent",
|
|
130
|
+
children: [
|
|
131
|
+
/* @__PURE__ */ jsx(Share, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
132
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: "Export\u2026" })
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
) }) : null
|
|
136
|
+
]
|
|
116
137
|
}
|
|
117
138
|
)
|
|
118
139
|
] })
|
|
@@ -9,12 +9,9 @@ export interface ExportPaletteAnchorProps {
|
|
|
9
9
|
exportKey?: string;
|
|
10
10
|
/** The host's hidden ContentTransferMenu — the palette that mounts here. */
|
|
11
11
|
controllerRef: React.RefObject<ContentTransferController | null>;
|
|
12
|
-
/** Render a visible "Export…" icon button (a surface with no ⋯ menu of its own). */
|
|
13
|
-
trigger?: boolean;
|
|
14
|
-
/** What is exported, for the trigger's name ("Export note"). */
|
|
15
|
-
label?: string;
|
|
16
|
-
size?: "sm" | "xs";
|
|
17
12
|
className?: string;
|
|
18
13
|
}
|
|
19
|
-
export declare function ExportPaletteAnchor({ exportKey, controllerRef,
|
|
14
|
+
export declare function ExportPaletteAnchor({ exportKey, controllerRef, className, }: ExportPaletteAnchorProps): React.ReactElement;
|
|
15
|
+
/** A key for a host that opens its own anchor (the split Copy's "Export…" row). */
|
|
16
|
+
export declare function useExportPaletteKey(): string;
|
|
20
17
|
export default ExportPaletteAnchor;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import {
|
|
5
|
-
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, cn } from "@ai-matrx/design-system";
|
|
4
|
+
import { Popover, PopoverAnchor, PopoverContent, cn } from "@ai-matrx/design-system";
|
|
6
5
|
const hosts = /* @__PURE__ */ new Map();
|
|
7
6
|
function hasExportPalette(key) {
|
|
8
7
|
return hosts.has(key);
|
|
@@ -16,9 +15,6 @@ function openExportPalette(key) {
|
|
|
16
15
|
function ExportPaletteAnchor({
|
|
17
16
|
exportKey,
|
|
18
17
|
controllerRef,
|
|
19
|
-
trigger = false,
|
|
20
|
-
label,
|
|
21
|
-
size = "sm",
|
|
22
18
|
className
|
|
23
19
|
}) {
|
|
24
20
|
const [open, setOpen] = React.useState(false);
|
|
@@ -33,27 +29,8 @@ function ExportPaletteAnchor({
|
|
|
33
29
|
};
|
|
34
30
|
}, [exportKey]);
|
|
35
31
|
const close = React.useCallback(() => setOpen(false), []);
|
|
36
|
-
const name = `Export${label ? ` ${label}` : ""}\u2026`;
|
|
37
|
-
const box = size === "xs" ? "h-7 min-w-7" : "h-8 min-w-8";
|
|
38
32
|
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
39
|
-
|
|
40
|
-
"button",
|
|
41
|
-
{
|
|
42
|
-
type: "button",
|
|
43
|
-
"aria-label": name,
|
|
44
|
-
title: "Export\u2026",
|
|
45
|
-
"data-export-palette-trigger": "",
|
|
46
|
-
"data-matrx-button": "",
|
|
47
|
-
"data-tap-floor": "",
|
|
48
|
-
"data-tap-floor-square": "",
|
|
49
|
-
className: cn(
|
|
50
|
-
"inline-flex shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground pointer-coarse:h-11 pointer-coarse:min-w-11",
|
|
51
|
-
box,
|
|
52
|
-
className
|
|
53
|
-
),
|
|
54
|
-
children: /* @__PURE__ */ jsx(Share, { className: size === "xs" ? "h-3.5 w-3.5" : "h-4 w-4" })
|
|
55
|
-
}
|
|
56
|
-
) }) : /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, "data-export-palette-anchor": "", className: cn("inline-block h-0 w-0", className) }) }),
|
|
33
|
+
/* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, "data-export-palette-anchor": "", className: cn("inline-block h-0 w-0", className) }) }),
|
|
57
34
|
/* @__PURE__ */ jsx(
|
|
58
35
|
PopoverContent,
|
|
59
36
|
{
|
|
@@ -88,10 +65,14 @@ function PaletteSlot({
|
|
|
88
65
|
}, [controllerRef, close]);
|
|
89
66
|
return /* @__PURE__ */ jsx("div", { ref, "data-export-palette-slot": "", children: failed ? /* @__PURE__ */ jsx("p", { className: "px-2 py-1.5 text-sm text-muted-foreground", children: failed }) : null });
|
|
90
67
|
}
|
|
68
|
+
function useExportPaletteKey() {
|
|
69
|
+
return `export-palette-${React.useId()}`;
|
|
70
|
+
}
|
|
91
71
|
var ExportPalette_default = ExportPaletteAnchor;
|
|
92
72
|
export {
|
|
93
73
|
ExportPaletteAnchor,
|
|
94
74
|
ExportPalette_default as default,
|
|
95
75
|
hasExportPalette,
|
|
96
|
-
openExportPalette
|
|
76
|
+
openExportPalette,
|
|
77
|
+
useExportPaletteKey
|
|
97
78
|
};
|
|
@@ -7,7 +7,7 @@ import { cn } from "@ai-matrx/design-system";
|
|
|
7
7
|
import { AlchemyDocumentMenu } from "./shared/AlchemyDocumentMenu.js";
|
|
8
8
|
import { CopySplitButton } from "../../copy/CopySplitButton.js";
|
|
9
9
|
import { copyRichContent } from "../../copy/copy-commands.js";
|
|
10
|
-
import { ExportPaletteAnchor } from "../../copy/ExportPalette.js";
|
|
10
|
+
import { ExportPaletteAnchor, openExportPalette } from "../../copy/ExportPalette.js";
|
|
11
11
|
import { ensureExportPaletteAction } from "../actions/export-palette-action.js";
|
|
12
12
|
import { OpenOneMenuButton, useOneMenuFor } from "./shared/OpenOneMenuButton.js";
|
|
13
13
|
import { StandaloneOneMenu } from "./shared/StandaloneOneMenu.js";
|
|
@@ -43,7 +43,14 @@ function ActionBar(props) {
|
|
|
43
43
|
),
|
|
44
44
|
children: [
|
|
45
45
|
hasCopy ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
46
|
-
/* @__PURE__ */ jsx(
|
|
46
|
+
/* @__PURE__ */ jsx(
|
|
47
|
+
CopySplitButton,
|
|
48
|
+
{
|
|
49
|
+
copy,
|
|
50
|
+
size: mini ? "xs" : "sm",
|
|
51
|
+
...hideOverflow ? { onExport: () => void openExportPalette(sourceId) } : {}
|
|
52
|
+
}
|
|
53
|
+
),
|
|
47
54
|
/* @__PURE__ */ jsx(
|
|
48
55
|
AlchemyDocumentMenu,
|
|
49
56
|
{
|
|
@@ -67,7 +74,7 @@ function ActionBar(props) {
|
|
|
67
74
|
hideOverflow: true
|
|
68
75
|
}
|
|
69
76
|
),
|
|
70
|
-
hasCopy ? /* @__PURE__ */ jsx(ExportPaletteAnchor, { exportKey: sourceId, controllerRef: palette
|
|
77
|
+
hasCopy ? /* @__PURE__ */ jsx(ExportPaletteAnchor, { exportKey: sourceId, controllerRef: palette }) : null,
|
|
71
78
|
hideOverflow ? null : oneMenu ? /* @__PURE__ */ jsx(OpenOneMenuButton, { source: getCtx().source }) : /* @__PURE__ */ jsx(StandaloneOneMenu, { getCtx, target })
|
|
72
79
|
]
|
|
73
80
|
}
|
|
@@ -107,6 +107,14 @@ export interface SelectionCommonHost {
|
|
|
107
107
|
* editing: Copy · Bold · Italic · Link · Ask AI · More (headings, lists, comment…)
|
|
108
108
|
*/
|
|
109
109
|
export declare const SELECTION_PRIMARY: Readonly<Record<SelectionMode, readonly string[]>>;
|
|
110
|
+
/**
|
|
111
|
+
* A surface's OWN passage action takes the reading row's middle slot when present (Arman,
|
|
112
|
+
* 2026-10-07): the study guide reads Copy · Highlight · I don't get this · Ask AI · More, and
|
|
113
|
+
* Comment moves behind More there. Listed in priority order; the first present one wins.
|
|
114
|
+
*/
|
|
115
|
+
export declare const SELECTION_CONTEXT_PRIMARY: readonly string[];
|
|
116
|
+
/** The buttons for this target: the mode's set, with a present context action in Comment's slot. */
|
|
117
|
+
export declare function selectionPrimaryFor(target: ClickTarget, mode: SelectionMode): readonly string[];
|
|
110
118
|
/** The most controls the strip ever shows: the split Copy, the buttons, and More. */
|
|
111
119
|
export declare const SELECTION_MAX_VISIBLE = 6;
|
|
112
120
|
/**
|
|
@@ -85,6 +85,13 @@ const SELECTION_PRIMARY = {
|
|
|
85
85
|
edit: ["selection:format-bold", "selection:format-italic", "selection:format-link", "selection:ai"],
|
|
86
86
|
read: ["selection:highlight", "selection:comment", "selection:ai"]
|
|
87
87
|
};
|
|
88
|
+
const SELECTION_CONTEXT_PRIMARY = ["selection:tutor-explain"];
|
|
89
|
+
function selectionPrimaryFor(target, mode) {
|
|
90
|
+
const base = SELECTION_PRIMARY[mode];
|
|
91
|
+
if (mode !== "read") return base;
|
|
92
|
+
const context = SELECTION_CONTEXT_PRIMARY.find((id) => presentAt(id, target));
|
|
93
|
+
return context ? base.map((id) => id === "selection:comment" ? context : id) : base;
|
|
94
|
+
}
|
|
88
95
|
const SELECTION_MAX_VISIBLE = 6;
|
|
89
96
|
const SELECTION_PRIORITY = {
|
|
90
97
|
edit: [
|
|
@@ -159,9 +166,10 @@ function presentAt(id, target) {
|
|
|
159
166
|
function selectionPlacement(id, target) {
|
|
160
167
|
const toolbar = selectionToolbarHostOf(target);
|
|
161
168
|
if (!toolbar) return "primary";
|
|
162
|
-
|
|
169
|
+
const primary = selectionPrimaryFor(target, toolbar.mode);
|
|
170
|
+
if (!primary.includes(id)) return "overflow";
|
|
163
171
|
if (toolbar.slots === null) return "primary";
|
|
164
|
-
const shown =
|
|
172
|
+
const shown = primary.filter((x) => presentAt(x, target));
|
|
165
173
|
if (shown.length <= toolbar.slots) return "primary";
|
|
166
174
|
const rank = shown.indexOf(id);
|
|
167
175
|
return rank >= 0 && rank < Math.max(1, toolbar.slots - 1) ? "primary" : "overflow";
|
|
@@ -220,6 +228,7 @@ export {
|
|
|
220
228
|
PASSAGE_ACTIONS_HOST_KEY,
|
|
221
229
|
SELECTION_ACTION_MODES,
|
|
222
230
|
SELECTION_COMMON_HOST_KEY,
|
|
231
|
+
SELECTION_CONTEXT_PRIMARY,
|
|
223
232
|
SELECTION_HOST_KINDS,
|
|
224
233
|
SELECTION_MAX_VISIBLE,
|
|
225
234
|
SELECTION_PRIMARY,
|
|
@@ -232,6 +241,7 @@ export {
|
|
|
232
241
|
passageActionsProvider,
|
|
233
242
|
placeSelectionActions,
|
|
234
243
|
selectionPlacement,
|
|
244
|
+
selectionPrimaryFor,
|
|
235
245
|
selectionToolbarHostOf,
|
|
236
246
|
shownInSelectionMode,
|
|
237
247
|
subscribeDeclaredSelectionProviders
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/rich-content",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.44",
|
|
4
4
|
"description": "The AI Matrx formatted-text engine: the one markdown renderer, streaming markdown, the block splitter and renderer, the generic blocks, <RichContent> levels, the rich-document action registry and menus, copy, and the selection toolbar — as portable React.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -130,21 +130,21 @@
|
|
|
130
130
|
"unist-util-visit": "^5.1.0",
|
|
131
131
|
"unist-util-visit-parents": "^6.0.2",
|
|
132
132
|
"yaml": "^2.9.1",
|
|
133
|
-
"@ai-matrx/agents": "latest",
|
|
134
133
|
"@ai-matrx/alchemy": "latest",
|
|
135
134
|
"@ai-matrx/canvas": "latest",
|
|
135
|
+
"@ai-matrx/content-ir": "latest",
|
|
136
|
+
"@ai-matrx/content-ir-react": "latest",
|
|
136
137
|
"@ai-matrx/data": "latest",
|
|
137
|
-
"@ai-matrx/
|
|
138
|
+
"@ai-matrx/agents": "latest",
|
|
138
139
|
"@ai-matrx/diff": "latest",
|
|
139
|
-
"@ai-matrx/
|
|
140
|
+
"@ai-matrx/design-system": "latest",
|
|
140
141
|
"@ai-matrx/icons": "latest",
|
|
141
|
-
"@ai-matrx/content-ir-react": "latest",
|
|
142
142
|
"@ai-matrx/kit": "latest",
|
|
143
143
|
"@ai-matrx/print": "latest",
|
|
144
|
-
"@ai-matrx/records": "latest",
|
|
145
|
-
"@ai-matrx/records-ui": "latest",
|
|
146
144
|
"@ai-matrx/media": "latest",
|
|
147
|
-
"@ai-matrx/
|
|
145
|
+
"@ai-matrx/records": "latest",
|
|
146
|
+
"@ai-matrx/tap-target": "latest",
|
|
147
|
+
"@ai-matrx/records-ui": "latest"
|
|
148
148
|
},
|
|
149
149
|
"peerDependencies": {
|
|
150
150
|
"react": ">=19.0.0",
|