@ai-matrx/rich-content 0.2.40 → 0.2.41
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 +8 -0
- package/dist/copy/CopySplitButton.d.ts +1 -3
- package/dist/copy/CopySplitButton.js +20 -43
- package/dist/copy/ExportPalette.d.ts +20 -0
- package/dist/copy/ExportPalette.js +97 -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 +5 -4
- package/dist/selection-toolbar/selection-actions.d.ts +13 -2
- package/dist/selection-toolbar/selection-actions.js +15 -18
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.41
|
|
4
|
+
|
|
5
|
+
- **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.
|
|
6
|
+
- **"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.
|
|
7
|
+
- **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.
|
|
8
|
+
|
|
9
|
+
**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.
|
|
10
|
+
|
|
3
11
|
## 0.2.40
|
|
4
12
|
|
|
5
13
|
- The selection toolbar's outer strip is `role="group"`, not a second `role=toolbar` wrapped around the real SelectionToolbar: one toolbar on screen.
|
|
@@ -7,8 +7,6 @@ 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
|
|
@@ -20,5 +18,5 @@ export interface CopySplitButtonProps {
|
|
|
20
18
|
disabled?: boolean;
|
|
21
19
|
className?: string;
|
|
22
20
|
}
|
|
23
|
-
export declare function CopySplitButton({ copy, label, size,
|
|
21
|
+
export declare function CopySplitButton({ copy, label, size, moreOnly, preserveSelection, disabled, className, }: CopySplitButtonProps): React.ReactElement;
|
|
24
22
|
export default CopySplitButton;
|
|
@@ -12,7 +12,6 @@ function CopySplitButton({
|
|
|
12
12
|
copy,
|
|
13
13
|
label = "Copy",
|
|
14
14
|
size = "sm",
|
|
15
|
-
mountMore,
|
|
16
15
|
moreOnly = false,
|
|
17
16
|
preserveSelection = false,
|
|
18
17
|
disabled = false,
|
|
@@ -32,7 +31,6 @@ function CopySplitButton({
|
|
|
32
31
|
if (timer.current) clearTimeout(timer.current);
|
|
33
32
|
timer.current = setTimeout(() => setCopied(null), 1600);
|
|
34
33
|
};
|
|
35
|
-
const close = React.useCallback(() => setOpen(false), []);
|
|
36
34
|
const keep = preserveSelection ? (event) => event.preventDefault() : void 0;
|
|
37
35
|
const mainName = `${label} (${flavor === "text" ? "plain text" : "markdown"})`;
|
|
38
36
|
const box = size === "xs" ? "h-7" : "h-8";
|
|
@@ -88,36 +86,33 @@ function CopySplitButton({
|
|
|
88
86
|
children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
|
|
89
87
|
}
|
|
90
88
|
) }),
|
|
91
|
-
/* @__PURE__ */
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
92
90
|
PopoverContent,
|
|
93
91
|
{
|
|
94
92
|
align: "start",
|
|
95
93
|
className: "w-auto min-w-60 max-w-[min(24rem,calc(100vw-16px))] p-1",
|
|
96
94
|
onOpenAutoFocus: preserveSelection ? (event) => event.preventDefault() : void 0,
|
|
97
95
|
"data-copy-split-menu": "",
|
|
98
|
-
children:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
void run(rowFlavor);
|
|
109
|
-
},
|
|
110
|
-
className: "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent",
|
|
111
|
-
children: [
|
|
112
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
113
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1", children: rowLabel }),
|
|
114
|
-
rowFlavor === flavor ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Default" }) : null
|
|
115
|
-
]
|
|
96
|
+
children: /* @__PURE__ */ jsx("div", { role: "menu", "aria-label": "Copy as", children: ROWS.map(({ flavor: rowFlavor, label: rowLabel, icon: Icon }) => /* @__PURE__ */ jsxs(
|
|
97
|
+
"button",
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
role: "menuitem",
|
|
101
|
+
"data-copy-flavor": rowFlavor,
|
|
102
|
+
onMouseDown: keep,
|
|
103
|
+
onClick: () => {
|
|
104
|
+
setOpen(false);
|
|
105
|
+
void run(rowFlavor);
|
|
116
106
|
},
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
107
|
+
className: "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent",
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
110
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: rowLabel }),
|
|
111
|
+
rowFlavor === flavor ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Default" }) : null
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
rowFlavor
|
|
115
|
+
)) })
|
|
121
116
|
}
|
|
122
117
|
)
|
|
123
118
|
] })
|
|
@@ -125,24 +120,6 @@ function CopySplitButton({
|
|
|
125
120
|
}
|
|
126
121
|
);
|
|
127
122
|
}
|
|
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
123
|
var CopySplitButton_default = CopySplitButton;
|
|
147
124
|
export {
|
|
148
125
|
CopySplitButton,
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
/** 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
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function ExportPaletteAnchor({ exportKey, controllerRef, trigger, label, size, className, }: ExportPaletteAnchorProps): React.ReactElement;
|
|
20
|
+
export default ExportPaletteAnchor;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Share } from "lucide-react";
|
|
5
|
+
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, cn } from "@ai-matrx/design-system";
|
|
6
|
+
const hosts = /* @__PURE__ */ new Map();
|
|
7
|
+
function hasExportPalette(key) {
|
|
8
|
+
return hosts.has(key);
|
|
9
|
+
}
|
|
10
|
+
function openExportPalette(key) {
|
|
11
|
+
const open = hosts.get(key);
|
|
12
|
+
if (!open) return false;
|
|
13
|
+
open();
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
function ExportPaletteAnchor({
|
|
17
|
+
exportKey,
|
|
18
|
+
controllerRef,
|
|
19
|
+
trigger = false,
|
|
20
|
+
label,
|
|
21
|
+
size = "sm",
|
|
22
|
+
className
|
|
23
|
+
}) {
|
|
24
|
+
const [open, setOpen] = React.useState(false);
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
if (!exportKey) return;
|
|
27
|
+
const opener = () => {
|
|
28
|
+
window.setTimeout(() => setOpen(true), 0);
|
|
29
|
+
};
|
|
30
|
+
hosts.set(exportKey, opener);
|
|
31
|
+
return () => {
|
|
32
|
+
if (hosts.get(exportKey) === opener) hosts.delete(exportKey);
|
|
33
|
+
};
|
|
34
|
+
}, [exportKey]);
|
|
35
|
+
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
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
39
|
+
trigger ? /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
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) }) }),
|
|
57
|
+
/* @__PURE__ */ jsx(
|
|
58
|
+
PopoverContent,
|
|
59
|
+
{
|
|
60
|
+
align: "end",
|
|
61
|
+
className: "w-auto min-w-60 max-w-[min(24rem,calc(100vw-16px))] p-1",
|
|
62
|
+
"data-export-palette": "",
|
|
63
|
+
children: open ? /* @__PURE__ */ jsx(PaletteSlot, { controllerRef, close }) : null
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
] });
|
|
67
|
+
}
|
|
68
|
+
function PaletteSlot({
|
|
69
|
+
controllerRef,
|
|
70
|
+
close
|
|
71
|
+
}) {
|
|
72
|
+
const ref = React.useRef(null);
|
|
73
|
+
const [failed, setFailed] = React.useState(null);
|
|
74
|
+
React.useEffect(() => {
|
|
75
|
+
const element = ref.current;
|
|
76
|
+
try {
|
|
77
|
+
if (!controllerRef.current) throw new Error("Export is unavailable here.");
|
|
78
|
+
controllerRef.current.mountPalette(element, close);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
setFailed(error instanceof Error ? error.message : "Export is unavailable here.");
|
|
81
|
+
}
|
|
82
|
+
return () => {
|
|
83
|
+
try {
|
|
84
|
+
controllerRef.current?.mountPalette(null);
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}, [controllerRef, close]);
|
|
89
|
+
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
|
+
}
|
|
91
|
+
var ExportPalette_default = ExportPaletteAnchor;
|
|
92
|
+
export {
|
|
93
|
+
ExportPaletteAnchor,
|
|
94
|
+
ExportPalette_default as default,
|
|
95
|
+
hasExportPalette,
|
|
96
|
+
openExportPalette
|
|
97
|
+
};
|
|
@@ -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 } 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,7 @@ function ActionBar(props) {
|
|
|
43
43
|
),
|
|
44
44
|
children: [
|
|
45
45
|
hasCopy ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
46
|
-
/* @__PURE__ */ jsx(CopySplitButton, { copy, size: mini ? "xs" : "sm"
|
|
46
|
+
/* @__PURE__ */ jsx(CopySplitButton, { copy, size: mini ? "xs" : "sm" }),
|
|
47
47
|
/* @__PURE__ */ jsx(
|
|
48
48
|
AlchemyDocumentMenu,
|
|
49
49
|
{
|
|
@@ -67,6 +67,7 @@ function ActionBar(props) {
|
|
|
67
67
|
hideOverflow: true
|
|
68
68
|
}
|
|
69
69
|
),
|
|
70
|
+
hasCopy ? /* @__PURE__ */ jsx(ExportPaletteAnchor, { exportKey: sourceId, controllerRef: palette, trigger: hideOverflow, size: mini ? "xs" : "sm" }) : null,
|
|
70
71
|
hideOverflow ? null : oneMenu ? /* @__PURE__ */ jsx(OpenOneMenuButton, { source: getCtx().source }) : /* @__PURE__ */ jsx(StandaloneOneMenu, { getCtx, target })
|
|
71
72
|
]
|
|
72
73
|
}
|
|
@@ -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,16 @@ 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
|
+
/** The most controls the strip ever shows: the split Copy, the buttons, and More. */
|
|
111
|
+
export declare const SELECTION_MAX_VISIBLE = 6;
|
|
101
112
|
/**
|
|
102
113
|
* Which actions keep a button when the bar cannot show them all (a phone).
|
|
103
114
|
* 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,11 @@ 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_MAX_VISIBLE = 6;
|
|
88
89
|
const SELECTION_PRIORITY = {
|
|
89
90
|
edit: [
|
|
90
91
|
"selection:format-bold",
|
|
@@ -102,11 +103,7 @@ const SELECTION_PRIORITY = {
|
|
|
102
103
|
"selection:format-quote",
|
|
103
104
|
"selection:format-list",
|
|
104
105
|
"selection:format-variable",
|
|
105
|
-
"selection:highlight
|
|
106
|
-
"selection:highlight-green",
|
|
107
|
-
"selection:highlight-blue",
|
|
108
|
-
"selection:highlight-pink",
|
|
109
|
-
"selection:highlight-purple",
|
|
106
|
+
"selection:highlight",
|
|
110
107
|
"selection:table-row-above",
|
|
111
108
|
"selection:table-col-left",
|
|
112
109
|
"selection:table-align-left",
|
|
@@ -119,15 +116,11 @@ const SELECTION_PRIORITY = {
|
|
|
119
116
|
"selection:save-to-table"
|
|
120
117
|
],
|
|
121
118
|
read: [
|
|
122
|
-
"selection:highlight
|
|
119
|
+
"selection:highlight",
|
|
123
120
|
"selection:comment",
|
|
124
121
|
"selection:ai",
|
|
125
122
|
"selection:suggest",
|
|
126
123
|
"selection:tutor-explain",
|
|
127
|
-
"selection:highlight-green",
|
|
128
|
-
"selection:highlight-blue",
|
|
129
|
-
"selection:highlight-pink",
|
|
130
|
-
"selection:highlight-purple",
|
|
131
124
|
"selection:tutor-ask",
|
|
132
125
|
"selection:link-record",
|
|
133
126
|
"selection:report",
|
|
@@ -165,8 +158,10 @@ function presentAt(id, target) {
|
|
|
165
158
|
}
|
|
166
159
|
function selectionPlacement(id, target) {
|
|
167
160
|
const toolbar = selectionToolbarHostOf(target);
|
|
168
|
-
if (!toolbar
|
|
169
|
-
|
|
161
|
+
if (!toolbar) return "primary";
|
|
162
|
+
if (!SELECTION_PRIMARY[toolbar.mode].includes(id)) return "overflow";
|
|
163
|
+
if (toolbar.slots === null) return "primary";
|
|
164
|
+
const shown = SELECTION_PRIMARY[toolbar.mode].filter((x) => presentAt(x, target));
|
|
170
165
|
if (shown.length <= toolbar.slots) return "primary";
|
|
171
166
|
const rank = shown.indexOf(id);
|
|
172
167
|
return rank >= 0 && rank < Math.max(1, toolbar.slots - 1) ? "primary" : "overflow";
|
|
@@ -226,6 +221,8 @@ export {
|
|
|
226
221
|
SELECTION_ACTION_MODES,
|
|
227
222
|
SELECTION_COMMON_HOST_KEY,
|
|
228
223
|
SELECTION_HOST_KINDS,
|
|
224
|
+
SELECTION_MAX_VISIBLE,
|
|
225
|
+
SELECTION_PRIMARY,
|
|
229
226
|
SELECTION_PRIORITY,
|
|
230
227
|
SELECTION_TOOLBAR_KNOB_FEATURE,
|
|
231
228
|
declareSelectionProvider,
|
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.41",
|
|
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",
|
|
@@ -133,18 +133,18 @@
|
|
|
133
133
|
"@ai-matrx/agents": "latest",
|
|
134
134
|
"@ai-matrx/alchemy": "latest",
|
|
135
135
|
"@ai-matrx/canvas": "latest",
|
|
136
|
-
"@ai-matrx/content-ir": "latest",
|
|
137
|
-
"@ai-matrx/content-ir-react": "latest",
|
|
138
136
|
"@ai-matrx/data": "latest",
|
|
139
|
-
"@ai-matrx/diff": "latest",
|
|
140
137
|
"@ai-matrx/design-system": "latest",
|
|
138
|
+
"@ai-matrx/diff": "latest",
|
|
139
|
+
"@ai-matrx/content-ir": "latest",
|
|
141
140
|
"@ai-matrx/icons": "latest",
|
|
141
|
+
"@ai-matrx/content-ir-react": "latest",
|
|
142
142
|
"@ai-matrx/kit": "latest",
|
|
143
|
-
"@ai-matrx/media": "latest",
|
|
144
|
-
"@ai-matrx/records": "latest",
|
|
145
143
|
"@ai-matrx/print": "latest",
|
|
146
|
-
"@ai-matrx/
|
|
147
|
-
"@ai-matrx/records-ui": "latest"
|
|
144
|
+
"@ai-matrx/records": "latest",
|
|
145
|
+
"@ai-matrx/records-ui": "latest",
|
|
146
|
+
"@ai-matrx/media": "latest",
|
|
147
|
+
"@ai-matrx/tap-target": "latest"
|
|
148
148
|
},
|
|
149
149
|
"peerDependencies": {
|
|
150
150
|
"react": ">=19.0.0",
|