@ai-matrx/rich-content 0.2.40 → 0.2.43
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 +22 -0
- package/dist/copy/CopySplitButton.d.ts +6 -3
- package/dist/copy/CopySplitButton.js +20 -22
- package/dist/copy/ExportPalette.d.ts +17 -0
- package/dist/copy/ExportPalette.js +78 -0
- package/dist/rich-document/actions/export-palette-action.d.ts +5 -0
- package/dist/rich-document/actions/export-palette-action.js +26 -0
- package/dist/rich-document/variants/ActionBar.js +12 -4
- package/dist/selection-toolbar/selection-actions.d.ts +21 -2
- package/dist/selection-toolbar/selection-actions.js +25 -18
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.43
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/rich-content/v0.2.41..npm/rich-content/v0.2.43 -- apps/shared/rich-content`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
9
|
+
## 0.2.42
|
|
10
|
+
|
|
11
|
+
- **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.
|
|
12
|
+
- **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.
|
|
13
|
+
|
|
14
|
+
**Consumer action:** replace `<ExportPaletteAnchor trigger …/>` with an anchor keyed by `useExportPaletteKey()` and `onExport={() => openExportPalette(key)}` on the split.
|
|
15
|
+
|
|
16
|
+
## 0.2.41
|
|
17
|
+
|
|
18
|
+
- **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.
|
|
19
|
+
- **"Export…" holds the palette.** New `copy/ExportPalette` (`ExportPaletteAnchor`, `openExportPalette`, `hasExportPalette`) and the registry action `export-palette` ("Export…", ⋯ menu). `ActionBar` mounts the anchor at its ⋯; with `hideOverflow` the anchor is its own "Export…" icon. Formatted, JSON, download and AI are unchanged — they moved, nothing was removed.
|
|
20
|
+
- **One compact selection row.** The mode table gains `SELECTION_PRIMARY` (the only actions that take a button) and `SELECTION_MAX_VISIBLE = 6`. Reading: Copy · Highlight · Comment · Ask AI · More. Editing: Copy · Bold · Italic · Link · Ask AI · More. The five `selection:highlight-<colour>` ids are replaced by one `selection:highlight` that opens the colours.
|
|
21
|
+
|
|
22
|
+
**Consumer action:** stop passing `mountMore`; register one `selection:highlight` action (a colour panel) instead of five colour actions; a surface with no ⋯ menu renders `<ExportPaletteAnchor trigger controllerRef=…/>` beside its split Copy.
|
|
23
|
+
|
|
3
24
|
## 0.2.40
|
|
4
25
|
|
|
5
26
|
- The selection toolbar's outer strip is `role="group"`, not a second `role=toolbar` wrapped around the real SelectionToolbar: one toolbar on screen.
|
|
@@ -281,3 +302,4 @@ held-back streaming text; the heal tests encode 1.3.1's behavior).
|
|
|
281
302
|
- matrx-frontend: call `configureRichContent` at the root (see README) and import from
|
|
282
303
|
`@ai-matrx/rich-content/<domain>/<module>`.
|
|
283
304
|
|
|
305
|
+
|
|
@@ -7,18 +7,21 @@ export interface CopySplitButtonProps {
|
|
|
7
7
|
label?: string;
|
|
8
8
|
/** "sm" (32px, an action bar) or "xs" (28px, a dense strip). */
|
|
9
9
|
size?: "sm" | "xs";
|
|
10
|
-
/** Mount more copy options (the Alchemy palette) under the two rows; called with null on close. */
|
|
11
|
-
mountMore?: (element: HTMLElement | null, close: () => void) => void;
|
|
12
10
|
/**
|
|
13
11
|
* Render only the chevron and its menu — for a host whose main control is already its own one-click
|
|
14
12
|
* tile (the phone note dock). Same rows, same "Default" tag, same copy module; the host positions it
|
|
15
13
|
* through `className`.
|
|
16
14
|
*/
|
|
17
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;
|
|
18
21
|
/** Keep the page's text selection when pressed (the selection toolbar). */
|
|
19
22
|
preserveSelection?: boolean;
|
|
20
23
|
disabled?: boolean;
|
|
21
24
|
className?: string;
|
|
22
25
|
}
|
|
23
|
-
export declare function CopySplitButton({ copy, label, size,
|
|
26
|
+
export declare function CopySplitButton({ copy, label, size, moreOnly, onExport, preserveSelection, disabled, className, }: CopySplitButtonProps): React.ReactElement;
|
|
24
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 = [
|
|
@@ -12,8 +12,8 @@ function CopySplitButton({
|
|
|
12
12
|
copy,
|
|
13
13
|
label = "Copy",
|
|
14
14
|
size = "sm",
|
|
15
|
-
mountMore,
|
|
16
15
|
moreOnly = false,
|
|
16
|
+
onExport,
|
|
17
17
|
preserveSelection = false,
|
|
18
18
|
disabled = false,
|
|
19
19
|
className
|
|
@@ -32,7 +32,6 @@ function CopySplitButton({
|
|
|
32
32
|
if (timer.current) clearTimeout(timer.current);
|
|
33
33
|
timer.current = setTimeout(() => setCopied(null), 1600);
|
|
34
34
|
};
|
|
35
|
-
const close = React.useCallback(() => setOpen(false), []);
|
|
36
35
|
const keep = preserveSelection ? (event) => event.preventDefault() : void 0;
|
|
37
36
|
const mainName = `${label} (${flavor === "text" ? "plain text" : "markdown"})`;
|
|
38
37
|
const box = size === "xs" ? "h-7" : "h-8";
|
|
@@ -116,7 +115,24 @@ function CopySplitButton({
|
|
|
116
115
|
},
|
|
117
116
|
rowFlavor
|
|
118
117
|
)) }),
|
|
119
|
-
|
|
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
|
|
120
136
|
]
|
|
121
137
|
}
|
|
122
138
|
)
|
|
@@ -125,24 +141,6 @@ function CopySplitButton({
|
|
|
125
141
|
}
|
|
126
142
|
);
|
|
127
143
|
}
|
|
128
|
-
function MoreSlot({ mount, close }) {
|
|
129
|
-
const ref = React.useRef(null);
|
|
130
|
-
React.useEffect(() => {
|
|
131
|
-
const element = ref.current;
|
|
132
|
-
try {
|
|
133
|
-
mount(element, close);
|
|
134
|
-
} catch (error) {
|
|
135
|
-
console.error("[copy] more copy options could not open:", error);
|
|
136
|
-
}
|
|
137
|
-
return () => {
|
|
138
|
-
try {
|
|
139
|
-
mount(null, close);
|
|
140
|
-
} catch {
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
}, [mount, close]);
|
|
144
|
-
return /* @__PURE__ */ jsx("div", { ref, "data-copy-split-more-slot": "", className: "mt-1 border-t border-border pt-1 empty:hidden" });
|
|
145
|
-
}
|
|
146
144
|
var CopySplitButton_default = CopySplitButton;
|
|
147
145
|
export {
|
|
148
146
|
CopySplitButton,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ContentTransferController } from "@ai-matrx/alchemy/react/workspace";
|
|
3
|
+
/** Is an export palette mounted for this key (a ⋯ "Export…" row shows only then)? */
|
|
4
|
+
export declare function hasExportPalette(key: string): boolean;
|
|
5
|
+
/** Open the export palette mounted under `key`. False when nothing is mounted there. */
|
|
6
|
+
export declare function openExportPalette(key: string): boolean;
|
|
7
|
+
export interface ExportPaletteAnchorProps {
|
|
8
|
+
/** The key a ⋯ "Export…" row opens this palette by (the document's `instanceKey("alchemy")`). */
|
|
9
|
+
exportKey?: string;
|
|
10
|
+
/** The host's hidden ContentTransferMenu — the palette that mounts here. */
|
|
11
|
+
controllerRef: React.RefObject<ContentTransferController | null>;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
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;
|
|
17
|
+
export default ExportPaletteAnchor;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Popover, PopoverAnchor, PopoverContent, cn } from "@ai-matrx/design-system";
|
|
5
|
+
const hosts = /* @__PURE__ */ new Map();
|
|
6
|
+
function hasExportPalette(key) {
|
|
7
|
+
return hosts.has(key);
|
|
8
|
+
}
|
|
9
|
+
function openExportPalette(key) {
|
|
10
|
+
const open = hosts.get(key);
|
|
11
|
+
if (!open) return false;
|
|
12
|
+
open();
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
function ExportPaletteAnchor({
|
|
16
|
+
exportKey,
|
|
17
|
+
controllerRef,
|
|
18
|
+
className
|
|
19
|
+
}) {
|
|
20
|
+
const [open, setOpen] = React.useState(false);
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
if (!exportKey) return;
|
|
23
|
+
const opener = () => {
|
|
24
|
+
window.setTimeout(() => setOpen(true), 0);
|
|
25
|
+
};
|
|
26
|
+
hosts.set(exportKey, opener);
|
|
27
|
+
return () => {
|
|
28
|
+
if (hosts.get(exportKey) === opener) hosts.delete(exportKey);
|
|
29
|
+
};
|
|
30
|
+
}, [exportKey]);
|
|
31
|
+
const close = React.useCallback(() => setOpen(false), []);
|
|
32
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
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) }) }),
|
|
34
|
+
/* @__PURE__ */ jsx(
|
|
35
|
+
PopoverContent,
|
|
36
|
+
{
|
|
37
|
+
align: "end",
|
|
38
|
+
className: "w-auto min-w-60 max-w-[min(24rem,calc(100vw-16px))] p-1",
|
|
39
|
+
"data-export-palette": "",
|
|
40
|
+
children: open ? /* @__PURE__ */ jsx(PaletteSlot, { controllerRef, close }) : null
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
] });
|
|
44
|
+
}
|
|
45
|
+
function PaletteSlot({
|
|
46
|
+
controllerRef,
|
|
47
|
+
close
|
|
48
|
+
}) {
|
|
49
|
+
const ref = React.useRef(null);
|
|
50
|
+
const [failed, setFailed] = React.useState(null);
|
|
51
|
+
React.useEffect(() => {
|
|
52
|
+
const element = ref.current;
|
|
53
|
+
try {
|
|
54
|
+
if (!controllerRef.current) throw new Error("Export is unavailable here.");
|
|
55
|
+
controllerRef.current.mountPalette(element, close);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
setFailed(error instanceof Error ? error.message : "Export is unavailable here.");
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
try {
|
|
61
|
+
controllerRef.current?.mountPalette(null);
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}, [controllerRef, close]);
|
|
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 });
|
|
67
|
+
}
|
|
68
|
+
function useExportPaletteKey() {
|
|
69
|
+
return `export-palette-${React.useId()}`;
|
|
70
|
+
}
|
|
71
|
+
var ExportPalette_default = ExportPaletteAnchor;
|
|
72
|
+
export {
|
|
73
|
+
ExportPaletteAnchor,
|
|
74
|
+
ExportPalette_default as default,
|
|
75
|
+
hasExportPalette,
|
|
76
|
+
openExportPalette,
|
|
77
|
+
useExportPaletteKey
|
|
78
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RichDocumentAction } from "../types.js";
|
|
2
|
+
export declare const EXPORT_PALETTE_ACTION_ID = "export-palette";
|
|
3
|
+
export declare const EXPORT_PALETTE_ACTION: RichDocumentAction;
|
|
4
|
+
/** Idempotent: the bar calls this on load. */
|
|
5
|
+
export declare function ensureExportPaletteAction(): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Share } from "lucide-react";
|
|
2
|
+
import { getAction, registerAction } from "./provider.js";
|
|
3
|
+
import { hasExportPalette, openExportPalette } from "../../copy/ExportPalette.js";
|
|
4
|
+
const EXPORT_PALETTE_ACTION_ID = "export-palette";
|
|
5
|
+
const EXPORT_PALETTE_ACTION = {
|
|
6
|
+
id: EXPORT_PALETTE_ACTION_ID,
|
|
7
|
+
label: "Export\u2026",
|
|
8
|
+
icon: Share,
|
|
9
|
+
category: "export",
|
|
10
|
+
supportedSources: "*",
|
|
11
|
+
renderSlot: "overflow",
|
|
12
|
+
order: 0,
|
|
13
|
+
visible: (ctx) => hasExportPalette(ctx.instanceKey("alchemy")),
|
|
14
|
+
run: (ctx) => {
|
|
15
|
+
openExportPalette(ctx.instanceKey("alchemy"));
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
function ensureExportPaletteAction() {
|
|
19
|
+
if (getAction(EXPORT_PALETTE_ACTION_ID)) return;
|
|
20
|
+
registerAction(EXPORT_PALETTE_ACTION);
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
EXPORT_PALETTE_ACTION,
|
|
24
|
+
EXPORT_PALETTE_ACTION_ID,
|
|
25
|
+
ensureExportPaletteAction
|
|
26
|
+
};
|
|
@@ -7,8 +7,11 @@ 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, openExportPalette } from "../../copy/ExportPalette.js";
|
|
11
|
+
import { ensureExportPaletteAction } from "../actions/export-palette-action.js";
|
|
10
12
|
import { OpenOneMenuButton, useOneMenuFor } from "./shared/OpenOneMenuButton.js";
|
|
11
13
|
import { StandaloneOneMenu } from "./shared/StandaloneOneMenu.js";
|
|
14
|
+
ensureExportPaletteAction();
|
|
12
15
|
const BAR_RESTRICT = { exclude: ["copy"] };
|
|
13
16
|
const SPLIT_COPY_ACTION_IDS = {
|
|
14
17
|
markdown: "copy-markdown",
|
|
@@ -26,9 +29,6 @@ function ActionBar(props) {
|
|
|
26
29
|
const hasCopy = actions.some((action) => action.category === "copy");
|
|
27
30
|
const palette = React.useRef(null);
|
|
28
31
|
const copy = React.useCallback((flavor) => runDocumentCopy(actions, getCtx, flavor), [actions, getCtx]);
|
|
29
|
-
const mountMore = React.useCallback((element, close) => {
|
|
30
|
-
palette.current?.mountPalette(element, close);
|
|
31
|
-
}, []);
|
|
32
32
|
return /* @__PURE__ */ jsxs(
|
|
33
33
|
"div",
|
|
34
34
|
{
|
|
@@ -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,6 +74,7 @@ function ActionBar(props) {
|
|
|
67
74
|
hideOverflow: true
|
|
68
75
|
}
|
|
69
76
|
),
|
|
77
|
+
hasCopy ? /* @__PURE__ */ jsx(ExportPaletteAnchor, { exportKey: sourceId, controllerRef: palette }) : null,
|
|
70
78
|
hideOverflow ? null : oneMenu ? /* @__PURE__ */ jsx(OpenOneMenuButton, { source: getCtx().source }) : /* @__PURE__ */ jsx(StandaloneOneMenu, { getCtx, target })
|
|
71
79
|
]
|
|
72
80
|
}
|
|
@@ -42,7 +42,8 @@ export declare function hostHalf<T>(target: ClickTarget, key: string): T | null;
|
|
|
42
42
|
type ModeRule = readonly SelectionMode[] | ((knobs: SelectionToolbarKnobs) => readonly SelectionMode[]);
|
|
43
43
|
/**
|
|
44
44
|
* THE MODE TABLE. Editing: formatting + AI + comment. Reading: highlight,
|
|
45
|
-
* comment, suggest, AI, link and report. AI shows in both.
|
|
45
|
+
* comment, suggest, AI, link and report. AI shows in both. WHERE each shows —
|
|
46
|
+
* a button or a More row — is `SELECTION_PRIMARY` below.
|
|
46
47
|
*/
|
|
47
48
|
export declare const SELECTION_ACTION_MODES: Readonly<Record<string, ModeRule>>;
|
|
48
49
|
/**
|
|
@@ -50,7 +51,7 @@ export declare const SELECTION_ACTION_MODES: Readonly<Record<string, ModeRule>>;
|
|
|
50
51
|
* the census test checks against the providers (selection-host-kinds.test.ts):
|
|
51
52
|
* a host kind is the set of host halves present on the target.
|
|
52
53
|
*
|
|
53
|
-
* annotated reading (annotation + context menu) highlight
|
|
54
|
+
* annotated reading (annotation + context menu) highlight, comment, Ask AI; More: suggest, link, report —
|
|
54
55
|
* the study guide, document Annotate, and EVERY saved record
|
|
55
56
|
* (annotations/RecordAnnotations): a note's preview, a chat
|
|
56
57
|
* answer, the studio previewing an unedited saved document.
|
|
@@ -98,6 +99,24 @@ export interface SelectionCommonHost {
|
|
|
98
99
|
messageId: string;
|
|
99
100
|
}): void;
|
|
100
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* THE BUTTONS (Arman, 2026-10-07: "simple, obvious, nothing extra"). The strip
|
|
104
|
+
* is the split Copy, these, then More — on every surface, desktop and phone.
|
|
105
|
+
* Every other action of the mode table is a More row.
|
|
106
|
+
* reading: Copy · Highlight (opens the colours) · Comment · Ask AI · More
|
|
107
|
+
* editing: Copy · Bold · Italic · Link · Ask AI · More (headings, lists, comment…)
|
|
108
|
+
*/
|
|
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[];
|
|
118
|
+
/** The most controls the strip ever shows: the split Copy, the buttons, and More. */
|
|
119
|
+
export declare const SELECTION_MAX_VISIBLE = 6;
|
|
101
120
|
/**
|
|
102
121
|
* Which actions keep a button when the bar cannot show them all (a phone).
|
|
103
122
|
* Everything else — including "AI and more" when space is tight — moves into
|
|
@@ -35,12 +35,8 @@ const SELECTION_ACTION_MODES = {
|
|
|
35
35
|
"selection:format-codeblock": ["edit"],
|
|
36
36
|
"selection:format-variable": ["edit"],
|
|
37
37
|
"selection:format-color": ["edit"],
|
|
38
|
-
// Annotations (the annotation sidecar)
|
|
39
|
-
"selection:highlight
|
|
40
|
-
"selection:highlight-green": HIGHLIGHT,
|
|
41
|
-
"selection:highlight-blue": HIGHLIGHT,
|
|
42
|
-
"selection:highlight-pink": HIGHLIGHT,
|
|
43
|
-
"selection:highlight-purple": HIGHLIGHT,
|
|
38
|
+
// Annotations (the annotation sidecar). ONE Highlight button; it opens the colour choice.
|
|
39
|
+
"selection:highlight": HIGHLIGHT,
|
|
44
40
|
"selection:comment": ["read", "edit"],
|
|
45
41
|
"selection:suggest": ["read"],
|
|
46
42
|
"selection:link-record": ["read"],
|
|
@@ -85,6 +81,18 @@ function shownInSelectionMode(actionId, target) {
|
|
|
85
81
|
return modes.includes(toolbar.mode);
|
|
86
82
|
}
|
|
87
83
|
const SELECTION_COMMON_HOST_KEY = "selectionCommon";
|
|
84
|
+
const SELECTION_PRIMARY = {
|
|
85
|
+
edit: ["selection:format-bold", "selection:format-italic", "selection:format-link", "selection:ai"],
|
|
86
|
+
read: ["selection:highlight", "selection:comment", "selection:ai"]
|
|
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
|
+
}
|
|
95
|
+
const SELECTION_MAX_VISIBLE = 6;
|
|
88
96
|
const SELECTION_PRIORITY = {
|
|
89
97
|
edit: [
|
|
90
98
|
"selection:format-bold",
|
|
@@ -102,11 +110,7 @@ const SELECTION_PRIORITY = {
|
|
|
102
110
|
"selection:format-quote",
|
|
103
111
|
"selection:format-list",
|
|
104
112
|
"selection:format-variable",
|
|
105
|
-
"selection:highlight
|
|
106
|
-
"selection:highlight-green",
|
|
107
|
-
"selection:highlight-blue",
|
|
108
|
-
"selection:highlight-pink",
|
|
109
|
-
"selection:highlight-purple",
|
|
113
|
+
"selection:highlight",
|
|
110
114
|
"selection:table-row-above",
|
|
111
115
|
"selection:table-col-left",
|
|
112
116
|
"selection:table-align-left",
|
|
@@ -119,15 +123,11 @@ const SELECTION_PRIORITY = {
|
|
|
119
123
|
"selection:save-to-table"
|
|
120
124
|
],
|
|
121
125
|
read: [
|
|
122
|
-
"selection:highlight
|
|
126
|
+
"selection:highlight",
|
|
123
127
|
"selection:comment",
|
|
124
128
|
"selection:ai",
|
|
125
129
|
"selection:suggest",
|
|
126
130
|
"selection:tutor-explain",
|
|
127
|
-
"selection:highlight-green",
|
|
128
|
-
"selection:highlight-blue",
|
|
129
|
-
"selection:highlight-pink",
|
|
130
|
-
"selection:highlight-purple",
|
|
131
131
|
"selection:tutor-ask",
|
|
132
132
|
"selection:link-record",
|
|
133
133
|
"selection:report",
|
|
@@ -165,8 +165,11 @@ function presentAt(id, target) {
|
|
|
165
165
|
}
|
|
166
166
|
function selectionPlacement(id, target) {
|
|
167
167
|
const toolbar = selectionToolbarHostOf(target);
|
|
168
|
-
if (!toolbar
|
|
169
|
-
const
|
|
168
|
+
if (!toolbar) return "primary";
|
|
169
|
+
const primary = selectionPrimaryFor(target, toolbar.mode);
|
|
170
|
+
if (!primary.includes(id)) return "overflow";
|
|
171
|
+
if (toolbar.slots === null) return "primary";
|
|
172
|
+
const shown = primary.filter((x) => presentAt(x, target));
|
|
170
173
|
if (shown.length <= toolbar.slots) return "primary";
|
|
171
174
|
const rank = shown.indexOf(id);
|
|
172
175
|
return rank >= 0 && rank < Math.max(1, toolbar.slots - 1) ? "primary" : "overflow";
|
|
@@ -225,7 +228,10 @@ export {
|
|
|
225
228
|
PASSAGE_ACTIONS_HOST_KEY,
|
|
226
229
|
SELECTION_ACTION_MODES,
|
|
227
230
|
SELECTION_COMMON_HOST_KEY,
|
|
231
|
+
SELECTION_CONTEXT_PRIMARY,
|
|
228
232
|
SELECTION_HOST_KINDS,
|
|
233
|
+
SELECTION_MAX_VISIBLE,
|
|
234
|
+
SELECTION_PRIMARY,
|
|
229
235
|
SELECTION_PRIORITY,
|
|
230
236
|
SELECTION_TOOLBAR_KNOB_FEATURE,
|
|
231
237
|
declareSelectionProvider,
|
|
@@ -235,6 +241,7 @@ export {
|
|
|
235
241
|
passageActionsProvider,
|
|
236
242
|
placeSelectionActions,
|
|
237
243
|
selectionPlacement,
|
|
244
|
+
selectionPrimaryFor,
|
|
238
245
|
selectionToolbarHostOf,
|
|
239
246
|
shownInSelectionMode,
|
|
240
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.43",
|
|
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",
|
|
@@ -136,15 +136,15 @@
|
|
|
136
136
|
"@ai-matrx/content-ir": "latest",
|
|
137
137
|
"@ai-matrx/content-ir-react": "latest",
|
|
138
138
|
"@ai-matrx/data": "latest",
|
|
139
|
-
"@ai-matrx/diff": "latest",
|
|
140
139
|
"@ai-matrx/design-system": "latest",
|
|
140
|
+
"@ai-matrx/diff": "latest",
|
|
141
141
|
"@ai-matrx/icons": "latest",
|
|
142
142
|
"@ai-matrx/kit": "latest",
|
|
143
143
|
"@ai-matrx/media": "latest",
|
|
144
|
-
"@ai-matrx/records": "latest",
|
|
145
144
|
"@ai-matrx/print": "latest",
|
|
146
|
-
"@ai-matrx/
|
|
147
|
-
"@ai-matrx/records-ui": "latest"
|
|
145
|
+
"@ai-matrx/records": "latest",
|
|
146
|
+
"@ai-matrx/records-ui": "latest",
|
|
147
|
+
"@ai-matrx/tap-target": "latest"
|
|
148
148
|
},
|
|
149
149
|
"peerDependencies": {
|
|
150
150
|
"react": ">=19.0.0",
|